Merged in changes from the conversion branch

- Legacy-Id: 3187
This commit is contained in:
Ole Laursen 2011-06-28 18:20:20 +00:00
parent 95898de070
commit 03bed41630
101 changed files with 6899 additions and 379 deletions

View file

@ -1,5 +1,6 @@
#coding: utf-8
from django.contrib import admin
from django.conf import settings
from ietf.announcements.models import *
class AnnouncedFromAdmin(admin.ModelAdmin):
@ -21,3 +22,22 @@ class ScheduledAnnouncementAdmin(admin.ModelAdmin):
pass
admin.site.register(ScheduledAnnouncement, ScheduledAnnouncementAdmin)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
class MessageAdmin(admin.ModelAdmin):
list_display = ["time", "by", "subject", "groups"]
search_fields = ["body"]
raw_id_fields = ["by"]
def groups(self, instance):
return ", ".join(g.acronym for g in related_groups.all())
admin.site.register(Message, MessageAdmin)
class SendQueueAdmin(admin.ModelAdmin):
list_display = ["time", "by", "message", "send_at", "sent_at"]
list_filter = ["time", "send_at", "sent_at"]
search_fields = ["message__body"]
raw_id_fields = ["by"]
admin.site.register(SendQueue, SendQueueAdmin)

View file

@ -1,6 +1,7 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.db import models
from django.conf import settings
from ietf.idtracker.models import PersonOrOrgInfo, ChairsHistory
#from django.contrib.auth.models import Permission
@ -87,3 +88,43 @@ class ScheduledAnnouncement(models.Model):
db_table = 'scheduled_announcements'
if settings.USE_DB_REDESIGN_PROXY_CLASSES or hasattr(settings, "IMPORTING_ANNOUNCEMENTS"):
import datetime
from person.models import Email, Person
from group.models import Group
class Message(models.Model):
time = models.DateTimeField(default=datetime.datetime.now)
by = models.ForeignKey(Person)
subject = models.CharField(max_length=255)
frm = models.CharField(max_length=255)
to = models.CharField(max_length=1024)
cc = models.CharField(max_length=1024, blank=True)
bcc = models.CharField(max_length=255, blank=True)
reply_to = models.CharField(max_length=255, blank=True)
body = models.TextField()
content_type = models.CharField(max_length=255, blank=True)
related_groups = models.ManyToManyField(Group, blank=True)
class Meta:
ordering = ['time']
def __unicode__(self):
return "'%s' %s -> %s" % (self.subject, self.frm, self.to)
class SendQueue(models.Model):
time = models.DateTimeField(default=datetime.datetime.now)
by = models.ForeignKey(Person)
message = models.ForeignKey(Message)
send_at = models.DateTimeField(blank=True, null=True)
sent_at = models.DateTimeField(blank=True, null=True)
note = models.TextField(blank=True)
class Meta:
ordering = ['time']

View file

@ -1,5 +1,7 @@
import re, datetime, email
from django.conf import settings
from ietf.utils.mail import send_mail_text, send_mail_mime
first_dot_on_line_re = re.compile(r'^\.', re.MULTILINE)
@ -8,7 +10,6 @@ def send_scheduled_announcement(announcement):
# for some reason, the old Perl code base substituted away . on line starts
body = first_dot_on_line_re.sub("", announcement.body)
announcement.content_type
extra = {}
if announcement.replyto:
extra['Reply-To'] = announcement.replyto
@ -33,3 +34,35 @@ def send_scheduled_announcement(announcement):
announcement.actual_sent_time = str(now.time())
announcement.mail_sent = True
announcement.save()
def send_scheduled_announcementREDESIGN(send_queue):
message = send_queue.message
# for some reason, the old Perl code base substituted away . on line starts
body = first_dot_on_line_re.sub("", message.body)
extra = {}
if message.reply_to:
extra['Reply-To'] = message.reply_to
# announcement.content_type can contain a case-sensitive parts separator,
# so we need to keep it as is, not lowercased, but we want a lowercased
# version for the coming comparisons.
content_type_lowercase = announcement.content_type.lower()
if not content_type_lowercase or 'text/plain' in content_type_lowercase:
send_mail_text(None, message.to, message.frm, message.subject,
body, cc=message.cc, bcc=message.bcc)
elif 'multipart' in content_type_lowercase:
# make body a real message so we can parse it
body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n" % announcement.content_type) + body
msg = email.message_from_string(body.encode("utf-8"))
send_mail_mime(None, message.to, message.frm, message.subject,
msg, cc=message.cc, bcc=message.bcc)
send_queue.sent_at = datetime.datetime.now()
send_queue.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
send_scheduled_announcement = send_scheduled_announcementREDESIGN

View file

@ -1,7 +1,11 @@
import datetime
from django.conf import settings
import django.test
from ietf.utils.test_utils import SimpleUrlTestCase, canonicalize_sitemap
from ietf.utils.test_runner import mail_outbox
from ietf.utils.test_data import make_test_data
from ietf.announcements.models import ScheduledAnnouncement
@ -57,3 +61,73 @@ class SendScheduledAnnouncementsTestCase(django.test.TestCase):
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
self.assertTrue("--NextPart" in mail_outbox[-1].as_string())
self.assertTrue(ScheduledAnnouncement.objects.get(id=a.id).mail_sent)
class SendScheduledAnnouncementsTestCaseREDESIGN(django.test.TestCase):
def test_send_plain_announcement(self):
from ietf.announcements.models import Message, SendQueue
from redesign.person.models import Person
make_test_data()
msg = Message.objects.create(
by=Person.objects.get(name="(System)"),
subject="This is a test",
to="test@example.com",
frm="testmonkey@example.com",
cc="cc.a@example.com, cc.b@example.com",
bcc="bcc@example.com",
body="Hello World!",
content_type="",
)
q = SendQueue.objects.create(
by=Person.objects.get(name="(System)"),
message=msg,
send_at=datetime.datetime.now() + datetime.timedelta(hours=12)
)
mailbox_before = len(mail_outbox)
from ietf.announcements.send_scheduled import send_scheduled_announcement
send_scheduled_announcement(q)
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
self.assertTrue(SendQueue.objects.get(id=q.id).sent_at)
def test_send_mime_announcement(self):
from ietf.announcements.models import Message, SendQueue
from redesign.person.models import Person
make_test_data()
msg = Message.objects.create(
by=Person.objects.get(name="(System)"),
subject="This is a test",
to="test@example.com",
frm="testmonkey@example.com",
cc="cc.a@example.com, cc.b@example.com",
bcc="bcc@example.com",
body='--NextPart\r\n\r\nA New Internet-Draft is available from the on-line Internet-Drafts directories.\r\n--NextPart\r\nContent-Type: Message/External-body;\r\n\tname="draft-huang-behave-bih-01.txt";\r\n\tsite="ftp.ietf.org";\r\n\taccess-type="anon-ftp";\r\n\tdirectory="internet-drafts"\r\n\r\nContent-Type: text/plain\r\nContent-ID: <2010-07-30001541.I-D@ietf.org>\r\n\r\n--NextPart--',
content_type='Multipart/Mixed; Boundary="NextPart"',
)
q = SendQueue.objects.create(
by=Person.objects.get(name="(System)"),
message=msg,
send_at=datetime.datetime.now() + datetime.timedelta(hours=12)
)
mailbox_before = len(mail_outbox)
from ietf.announcements.send_scheduled import send_scheduled_announcement
send_scheduled_announcement(q)
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
self.assertTrue("--NextPart" in mail_outbox[-1].as_string())
self.assertTrue(SendQueue.objects.get(id=q.id).sent_at)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
SendScheduledAnnouncementsTestCase = SendScheduledAnnouncementsTestCaseREDESIGN

View file

@ -3,12 +3,14 @@
from django.conf.urls.defaults import patterns
from ietf.announcements.models import Announcement
from django.conf import settings
nomcom_dict = {
'queryset': Announcement.objects.all().filter(nomcom=True)
}
}
urlpatterns = patterns('',
# (r'^nomcom/$', 'django.views.generic.simple.redirect_to', {'url': 'http://www.ietf.org/nomcom/index.html'} ),
(r'^nomcom/$', 'ietf.announcements.views.nomcom'),
(r'^nomcom/(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', nomcom_dict)
(r'^nomcom/(?P<object_id>\d+)/$', 'ietf.announcements.views.message_detail' if settings.USE_DB_REDESIGN_PROXY_CLASSES else 'django.views.generic.list_detail.object_detail', nomcom_dict)
)

View file

@ -1,6 +1,11 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.views.generic.simple import direct_to_template
from django.shortcuts import get_object_or_404
from django.conf import settings
from django.db.models import Q
import re
from ietf.idtracker.models import ChairsHistory
from ietf.idtracker.models import Role
@ -29,3 +34,56 @@ def nomcom(request):
{ 'curr_chair' : curr_chair,
'regimes' : regimes })
def nomcomREDESIGN(request):
from group.models import Group
from ietf.announcements.models import Message
address_re = re.compile("<.*>")
nomcoms = list(Group.objects.filter(acronym__startswith="nomcom").exclude(name="nomcom"))
regimes = []
for n in nomcoms:
e = n.latest_event(type="started")
n.start_year = e.time.year if e else 0
if n.start_year <= 2003:
continue
e = n.latest_event(type="concluded")
n.end_year = e.time.year if e else ""
chair = n.role_set.get(name="chair").email
announcements = Message.objects.filter(related_groups=n).order_by('-time')
for a in announcements:
a.to_name = address_re.sub("", a.to)
regimes.append(dict(chair=chair,
announcements=announcements,
group=n))
regimes.sort(key=lambda x: x["group"].start_year, reverse=True)
return direct_to_template(request,
"announcements/nomcomREDESIGN.html",
{ 'curr_chair' : regimes[0]["chair"],
'regimes' : regimes })
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
nomcom = nomcomREDESIGN
def message_detail(request, object_id, queryset):
from group.models import Group
from ietf.announcements.models import Message
# restrict to nomcom announcements for the time being
nomcoms = Group.objects.filter(acronym__startswith="nomcom").exclude(acronym="nomcom")
m = get_object_or_404(Message, id=object_id,
related_groups__in=nomcoms)
return direct_to_template(request,
"announcements/message_detail.html",
dict(message=m))

View file

@ -15,6 +15,6 @@ if not in_id_expire_freeze():
for doc in get_expired_ids():
send_expire_notice_for_id(doc)
expire_id(doc)
syslog.syslog("Expired %s (id=%s)%s" % (doc.file_tag(), doc.id_document_tag, " in the ID Tracker" if doc.idinternal else ""))
syslog.syslog("Expired %s (id=%s)%s" % (doc.file_tag(), doc.pk, " in the ID Tracker" if doc.latest_event(type="started_iesg_process") else ""))
clean_up_id_files()

View file

@ -14,4 +14,4 @@ from ietf.idrfc.lastcall import *
drafts = get_expired_last_calls()
for doc in drafts:
expire_last_call(doc)
syslog.syslog("Expired last call for %s (id=%s)" % (doc.file_tag(), doc.id_document_tag))
syslog.syslog("Expired last call for %s (id=%s)" % (doc.file_tag(), doc.pk))

View file

@ -6,56 +6,11 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idtracker.models import InternetDraft,IDAuthor,WGChair
from ietf.utils.mail import send_mail_subj
notify_days = 14 # notify about documents that expire within the
# next 2 weeks
start_date = datetime.date.today() - datetime.timedelta(InternetDraft.DAYS_TO_EXPIRE - 1)
end_date = start_date + datetime.timedelta(notify_days - 1)
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id
matches = InternetDraft.objects.filter(revision_date__gte=start_date,revision_date__lte=end_date,status__status='Active')
# notify about documents that expire within the next 2 weeks
notify_days = 14
#For development - focus on one draft
#matches = InternetDraft.objects.filter(filename__icontains='geopriv-http-location-delivery')
# Todo:
#second_cutoff = IDDates.objects.get(date_id=2)
#ietf_monday = IDDates.objects.get(date_id=3)
#freeze_delta = ietf_monday - second_cutoff
for draft in matches:
if not draft.can_expire():
# debugging
#print "%s can't expire, skipping" % draft
continue
expiration = draft.expiration()
# # The I-D expiration job doesn't run while submissions are frozen.
# if ietf_monday > expiration > second_cutoff:
# expiration += freeze_delta
authors = draft.authors.all()
to_addrs = [author.email() for author in authors if author.email()]
cc_addrs = None
if draft.group.acronym != 'none':
cc_addrs = [chair.person.email() for chair in WGChair.objects.filter(group_acronym=draft.group)]
#For development debugging
"""
print "filename: "+draft.filename
print "to: ", to_addrs
print "cc: ", cc_addrs
print "expires: ", expiration
print "status: ", draft.status.status, "/", draft.idstate()
print
continue
"""
if to_addrs or cc_addrs:
send_mail_subj(None, to_addrs, None, 'notify_expirations/subject.txt', 'notify_expirations/body.txt',
{
'draft':draft,
'expiration':expiration,
},
cc_addrs)
for doc in get_soon_to_expire_ids(notify_days):
send_expire_warning_for_id(doc)

View file

@ -14,7 +14,7 @@ from ietf.announcements.models import ScheduledAnnouncement
from ietf.announcements.send_scheduled import *
from django.db.models import Q
if not len(sys.argv) == 2 or sys.argv[1] not in ('all', 'rsync', 'specific'):
if len(sys.argv) != 2 or sys.argv[1] not in ('all', 'rsync', 'specific'):
print "USAGE: %s <all | rsync | specific>" % os.path.basename(__file__)
print "'all' means all not sent"
print "'rsync' means all not sent where to-be-sent-date is null"
@ -24,19 +24,28 @@ if not len(sys.argv) == 2 or sys.argv[1] not in ('all', 'rsync', 'specific'):
mode = sys.argv[1]
now = datetime.datetime.now()
now = datetime.datetime(2010, 8, 5)
announcements = ScheduledAnnouncement.objects.filter(mail_sent=False)
if mode == "rsync":
# include bogus 0000-00-00 entries
announcements = announcements.filter(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
elif mode == "specific":
# exclude null/bogus entries
announcements = announcements.exclude(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
announcements = announcements.filter(to_be_sent_date__lte=now.date(),
to_be_sent_time__lte=now.time())
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.announcements.models import SendQueue
announcements = SendQueue.objects.filter(sent_at=None)
if mode == "rsync":
announcements = announcements.filter(send_at=None)
elif mode == "specific":
announcements = announcements.exclude(send_at=None).filter(send_at__lte=now)
else:
announcements = ScheduledAnnouncement.objects.filter(mail_sent=False)
if mode == "rsync":
# include bogus 0000-00-00 entries
announcements = announcements.filter(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
elif mode == "specific":
# exclude null/bogus entries
announcements = announcements.exclude(Q(to_be_sent_date=None) | Q(to_be_sent_date__lte=datetime.date.min))
announcements = announcements.filter(to_be_sent_date__lte=now.date(),
to_be_sent_time__lte=now.time())
for announcement in announcements:
send_scheduled_announcement(announcement)
syslog.syslog('Sent scheduled announcement %s "%s"' % (announcement.id, announcement.subject))
subject = announcement.message.subject if settings.USE_DB_REDESIGN_PROXY_CLASSES else announcement.subject
syslog.syslog(u'Sent scheduled announcement %s "%s"' % (announcement.id, subject))

View file

@ -39,34 +39,34 @@ class IdIndexUrlTestCase(SimpleUrlTestCase):
def testUrls(self):
self.doTestUrls(__file__)
class IndexTestCase(unittest.TestCase, RealDatabaseTest):
def setUp(self):
self.setUpRealDatabase()
def tearDown(self):
self.tearDownRealDatabase()
# class IndexTestCase(unittest.TestCase, RealDatabaseTest):
# def setUp(self):
# self.setUpRealDatabase()
# def tearDown(self):
# self.tearDownRealDatabase()
def testAllId(self):
print " Testing all_id.txt generation"
c = Client()
response = c.get('/drafts/_test/all_id.txt')
self.assertEquals(response.status_code, 200)
content = response.content
# Test that correct version number is shown for couple of old drafts
self.assert_(content.find("draft-ietf-tls-psk-09") >= 0)
self.assert_(content.find("draft-eronen-eap-sim-aka-80211-00") >= 0)
# Since all_id.txt contains all old drafts, it should never shrink
lines = content.split("\n")
self.assert_(len(lines) > 18000)
# Test that the lines look OK and have correct number of tabs
r = re.compile(r'^(draft-\S*-\d\d)\t(\d\d\d\d-\d\d-\d\d)\t([^\t]+)\t([^\t]*)$')
for line in lines:
if ((line == "") or
(line == "Internet-Drafts Status Summary") or
(line == "Web version is available at") or
(line == "https://datatracker.ietf.org/public/idindex.cgi")):
pass
elif r.match(line):
pass
else:
self.fail("Unexpected line \""+line+"\"")
print "OK (all_id.txt)"
# def testAllId(self):
# print " Testing all_id.txt generation"
# c = Client()
# response = c.get('/drafts/_test/all_id.txt')
# self.assertEquals(response.status_code, 200)
# content = response.content
# # Test that correct version number is shown for couple of old drafts
# self.assert_(content.find("draft-ietf-tls-psk-09") >= 0)
# self.assert_(content.find("draft-eronen-eap-sim-aka-80211-00") >= 0)
# # Since all_id.txt contains all old drafts, it should never shrink
# lines = content.split("\n")
# self.assert_(len(lines) > 18000)
# # Test that the lines look OK and have correct number of tabs
# r = re.compile(r'^(draft-\S*-\d\d)\t(\d\d\d\d-\d\d-\d\d)\t([^\t]+)\t([^\t]*)$')
# for line in lines:
# if ((line == "") or
# (line == "Internet-Drafts Status Summary") or
# (line == "Web version is available at") or
# (line == "https://datatracker.ietf.org/public/idindex.cgi")):
# pass
# elif r.match(line):
# pass
# else:
# self.fail("Unexpected line \""+line+"\"")
# print "OK (all_id.txt)"

View file

@ -5,16 +5,16 @@
301 /drafts/current/
301 /drafts/all/
301 /drafts/dead/
301 /drafts/9574/related/
301 /drafts/9574/
#301 /drafts/9574/related/
#301 /drafts/9574/
301 /drafts/draft-ietf-dnsext-dnssec-protocol/related/
301 /drafts/draft-ietf-dnsext-dnssec-protocol/
404 /drafts/987654/
#404 /drafts/987654/
301 /drafts/all_id_txt.html
301 /drafts/all_id.html
301 /drafts/
200,heavy /drafts/_test/all_id.txt
#200,heavy /drafts/_test/all_id.txt
# this takes 3 minutes, so disabled by default
#200,heavy /drafts/_test/all_id2.txt
200,heavy /drafts/_test/id_index.txt
200,heavy /drafts/_test/id_abstracts.txt
#200,heavy /drafts/_test/id_index.txt
#200,heavy /drafts/_test/id_abstracts.txt

View file

@ -17,7 +17,8 @@ urlpatterns = patterns('',
(r'^all_id(?:_txt)?.html$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/id/all_id.txt' }),
)
if settings.SERVER_MODE != 'production':
if settings.SERVER_MODE != 'production' and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
# these haven't been ported
urlpatterns += patterns('',
(r'^_test/all_id.txt$', views.test_all_id_txt),
(r'^_test/all_id2.txt$', views.test_all_id2_txt),

View file

@ -35,6 +35,8 @@
from django.http import HttpResponse, HttpResponsePermanentRedirect
from django.template import loader
from django.shortcuts import get_object_or_404
from django.conf import settings
from ietf.idtracker.models import Acronym, IETFWG, InternetDraft, IDInternal,PersonOrOrgInfo, Area
from ietf.idtracker.templatetags.ietf_filters import clean_whitespace
import re
@ -156,6 +158,9 @@ def test_id_abstracts_txt(request):
def redirect_id(request, object_id):
'''Redirect from historical document ID to preferred filename url.'''
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return HttpResponsePermanentRedirect("/doc/")
doc = get_object_or_404(InternetDraft, id_document_tag=object_id)
return HttpResponsePermanentRedirect("/doc/"+doc.filename+"/")
@ -163,6 +168,11 @@ def redirect_filename(request, filename):
return HttpResponsePermanentRedirect("/doc/"+filename+"/")
def wgdocs_redirect_id(request, id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from group.models import Group
group = get_object_or_404(Group, id=id)
return HttpResponsePermanentRedirect("/wg/%s/" % group.acronym)
group = get_object_or_404(Acronym, acronym_id=id)
return HttpResponsePermanentRedirect("/wg/"+group.acronym+"/")

View file

@ -6,9 +6,14 @@ from django.db.models import Q
import datetime, os, shutil, glob, re
from ietf.idtracker.models import InternetDraft, IDDates, IDStatus, IDState, DocumentComment
from ietf.utils.mail import send_mail
from ietf.idtracker.models import InternetDraft, IDDates, IDStatus, IDState, DocumentComment, IDAuthor,WGChair
from ietf.utils.mail import send_mail, send_mail_subj
from ietf.idrfc.utils import log_state_changed, add_document_comment
from doc.models import Document, DocEvent, save_document_in_history
from name.models import IesgDocStateName, DocStateName, DocInfoTagName
from person.models import Person, Email
INTERNET_DRAFT_DAYS_TO_EXPIRE = 185
def in_id_expire_freeze(when=None):
if when == None:
@ -23,6 +28,33 @@ def in_id_expire_freeze(when=None):
return second_cut_off <= when < ietf_monday
def document_expires(doc):
e = doc.latest_event(type__in=("completed_resurrect", "new_revision"))
if e:
return e.time + datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE)
else:
return None
def expirable_documents():
return Document.objects.filter(state="active").exclude(tags="rfc-rev").filter(Q(iesg_state=None) | Q(iesg_state__order__gte=42))
def get_soon_to_expire_ids(days):
start_date = datetime.date.today() - datetime.timedelta(InternetDraft.DAYS_TO_EXPIRE - 1)
end_date = start_date + datetime.timedelta(days - 1)
for d in InternetDraft.objects.filter(revision_date__gte=start_date,revision_date__lte=end_date,status__status='Active'):
if d.can_expire():
yield d
def get_soon_to_expire_idsREDESIGN(days):
start_date = datetime.date.today() - datetime.timedelta(1)
end_date = start_date + datetime.timedelta(days - 1)
for d in expirable_documents():
t = document_expires(d)
if t and start_date <= t.date() <= end_date:
yield d
def get_expired_ids():
cut_off = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE)
@ -32,6 +64,60 @@ def get_expired_ids():
review_by_rfc_editor=0).filter(
Q(idinternal=None) | Q(idinternal__cur_state__document_state_id__gte=42))
def get_expired_idsREDESIGN():
today = datetime.date.today()
for d in expirable_documents():
t = document_expires(d)
if t and t.date() <= today:
yield d
def send_expire_warning_for_id(doc):
expiration = doc.expiration()
# Todo:
#second_cutoff = IDDates.objects.get(date_id=2)
#ietf_monday = IDDates.objects.get(date_id=3)
#freeze_delta = ietf_monday - second_cutoff
# # The I-D expiration job doesn't run while submissions are frozen.
# if ietf_monday > expiration > second_cutoff:
# expiration += freeze_delta
authors = doc.authors.all()
to_addrs = [author.email() for author in authors if author.email()]
cc_addrs = None
if doc.group.acronym != 'none':
cc_addrs = [chair.person.email() for chair in WGChair.objects.filter(group_acronym=doc.group)]
if to_addrs or cc_addrs:
send_mail_subj(None, to_addrs, None, 'notify_expirations/subject.txt', 'notify_expirations/body.txt',
{
'draft':doc,
'expiration':expiration,
},
cc_addrs)
def send_expire_warning_for_idREDESIGN(doc):
expiration = document_expires(doc).date()
to = [e.formatted_email() for e in doc.authors.all() if not e.address.startswith("unknown-email")]
cc = None
if doc.group.type_id != "individ":
cc = [e.formatted_email() for e in Email.objects.filter(role__group=doc.group, role__name="chair") if not e.address.startswith("unknown-email")]
state = doc.iesg_state.name if doc.iesg_state else "I-D Exists"
frm = None
request = None
if to or cc:
send_mail(request, to, frm,
u"Expiration impending: %s" % doc.file_tag(),
"idrfc/expire_warning_email.txt",
dict(doc=doc,
state=state,
expiration=expiration
),
cc=cc)
def send_expire_notice_for_id(doc):
doc.dunn_sent_date = datetime.date.today()
doc.save()
@ -45,7 +131,24 @@ def send_expire_notice_for_id(doc):
"I-D Expiring System <ietf-secretariat-reply@ietf.org>",
u"I-D was expired %s" % doc.file_tag(),
"idrfc/id_expired_email.txt",
dict(doc=doc))
dict(doc=doc,
state=doc.idstate()))
def send_expire_notice_for_idREDESIGN(doc):
if not doc.ad:
return
state = doc.iesg_state.name if doc.iesg_state else "I-D Exists"
request = None
to = doc.ad.formatted_email()
send_mail(request, to,
"I-D Expiring System <ietf-secretariat-reply@ietf.org>",
u"I-D was expired %s" % doc.file_tag(),
"idrfc/id_expired_email.txt",
dict(doc=doc,
state=state,
))
def expire_id(doc):
def move_file(f):
@ -83,6 +186,52 @@ def expire_id(doc):
add_document_comment(None, doc, "Document is expired by system")
def expire_idREDESIGN(doc):
system = Person.objects.get(name="(System)")
# clean up files
def move_file(f):
src = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, f)
dst = os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, f)
if os.path.exists(src):
shutil.move(src, dst)
file_types = ['txt', 'txt.p7s', 'ps', 'pdf']
for t in file_types:
move_file("%s-%s.%s" % (doc.name, doc.rev, t))
# make tombstone
new_revision = "%02d" % (int(doc.rev) + 1)
new_file = open(os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, "%s-%s.txt" % (doc.name, new_revision)), 'w')
txt = render_to_string("idrfc/expire_textREDESIGN.txt",
dict(doc=doc,
authors=[(e.get_name(), e.address) for e in doc.authors.all()],
expire_days=InternetDraft.DAYS_TO_EXPIRE))
new_file.write(txt)
new_file.close()
# now change the states
save_document_in_history(doc)
if doc.latest_event(type='started_iesg_process'):
dead_state = IesgDocStateName.objects.get(slug="dead")
if doc.iesg_state != dead_state:
prev = doc.iesg_state
doc.iesg_state = dead_state
log_state_changed(None, doc, system, prev)
e = DocEvent(doc=doc, by=system)
e.type = "expired_document"
e.desc = "Document has expired"
e.save()
doc.rev = new_revision # FIXME: incrementing the revision like this is messed up
doc.state = DocStateName.objects.get(slug="expired")
doc.time = datetime.datetime.now()
doc.save()
def clean_up_id_files():
"""Move unidentified and old files out of the Internet Draft directory."""
cut_off = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE)
@ -142,3 +291,75 @@ def clean_up_id_files():
except InternetDraft.DoesNotExist:
move_file_to("unknown_ids")
def clean_up_id_filesREDESIGN():
"""Move unidentified and old files out of the Internet Draft directory."""
cut_off = datetime.date.today() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE)
pattern = os.path.join(settings.IDSUBMIT_REPOSITORY_PATH, "draft-*.*")
files = []
filename_re = re.compile('^(.*)-(\d\d)$')
def splitext(fn):
"""
Split the pathname path into a pair (root, ext) such that root + ext
== path, and ext is empty or begins with a period and contains all
periods in the last path component.
This differs from os.path.splitext in the number of periods in the ext
parts when the final path component containt more than one period.
"""
s = fn.rfind("/")
if s == -1:
s = 0
i = fn[s:].find(".")
if i == -1:
return fn, ''
else:
return fn[:s+i], fn[s+i:]
for path in glob.glob(pattern):
basename = os.path.basename(path)
stem, ext = splitext(basename)
match = filename_re.search(stem)
if not match:
filename, revision = ("UNKNOWN", "00")
else:
filename, revision = match.groups()
def move_file_to(subdir):
shutil.move(path,
os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, subdir, basename))
try:
doc = Document.objects.get(name=filename, rev=revision)
if doc.state_id == "rfc":
if ext != ".txt":
move_file_to("unknown_ids")
elif doc.state_id in ("expired", "auth-rm", "repl", "ietf-rm"):
e = doc.latest_event(type__in=('expired_document', 'new_revision', "completed_resurrect"))
expiration_date = e.time.date() if e and e.type == "expired_document" else None
if expiration_date and expiration_date < cut_off:
# Expired, Withdrawn by Author, Replaced, Withdrawn by IETF,
# and expired more than DAYS_TO_EXPIRE ago
if os.path.getsize(path) < 1500:
move_file_to("deleted_tombstones")
# revert version after having deleted tombstone
doc.rev = "%02d" % (int(revision) - 1) # FIXME: messed up
doc.save()
doc.tags.add(DocInfoTagName.objects.get(slug='exp-tomb'))
else:
move_file_to("expired_without_tombstone")
except Document.DoesNotExist:
move_file_to("unknown_ids")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
get_soon_to_expire_ids = get_soon_to_expire_idsREDESIGN
get_expired_ids = get_expired_idsREDESIGN
send_expire_warning_for_id = send_expire_warning_for_idREDESIGN
send_expire_notice_for_id = send_expire_notice_for_idREDESIGN
expire_id = expire_idREDESIGN
clean_up_id_files = clean_up_id_filesREDESIGN

View file

@ -0,0 +1,525 @@
<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0">
<object pk="yes" model="name.ballotpositionname">
<field type="CharField" name="name">Yes</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="noobj" model="name.ballotpositionname">
<field type="CharField" name="name">No Objection</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="abstain" model="name.ballotpositionname">
<field type="CharField" name="name">Abstain</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="discuss" model="name.ballotpositionname">
<field type="CharField" name="name">Discuss</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="recuse" model="name.ballotpositionname">
<field type="CharField" name="name">Recuse</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="norecord" model="name.ballotpositionname">
<field type="CharField" name="name">No record</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="extpty" model="name.docinfotagname">
<field type="CharField" name="name">External Party</field>
<field type="TextField" name="desc">The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the "note" field for more details on who has the action.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="need-rev" model="name.docinfotagname">
<field type="CharField" name="name">Revised ID Needed</field>
<field type="TextField" name="desc">An updated ID is needed to address the issues that have been raised.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="iana-crd" model="name.docinfotagname">
<field type="CharField" name="name">IANA-coord</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ad-f-up" model="name.docinfotagname">
<field type="CharField" name="name">AD Followup</field>
<field type="TextField" name="desc">A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include:
- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG.
- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently.
- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="point" model="name.docinfotagname">
<field type="CharField" name="name">Point Raised - writeup needed</field>
<field type="TextField" name="desc">IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the "Point Raised - Writeup Needed" state until *ALL* IESG comments that have been raised have been documented.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="missref" model="name.docinfotagname">
<field type="CharField" name="name">MissingRef</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="fasttrac" model="name.docinfotagname">
<field type="CharField" name="name">FastTrack</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="rfc-rev" model="name.docinfotagname">
<field type="CharField" name="name">Review by RFC Editor</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="via-rfc" model="name.docinfotagname">
<field type="CharField" name="name">Via RFC Editor</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="exp-tomb" model="name.docinfotagname">
<field type="CharField" name="name">Expired tombstone</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="app-min" model="name.docinfotagname">
<field type="CharField" name="name">Approved in minute</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="errata" model="name.docinfotagname">
<field type="CharField" name="name">Has errata</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="updates" model="name.docrelationshipname">
<field type="CharField" name="name">Updates</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="replaces" model="name.docrelationshipname">
<field type="CharField" name="name">Replaces</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="obs" model="name.docrelationshipname">
<field type="CharField" name="name">Obsoletes</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="reviews" model="name.docrelationshipname">
<field type="CharField" name="name">Reviews</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="refs" model="name.docrelationshipname">
<field type="CharField" name="name">References</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="rfc" model="name.docstatename">
<field type="CharField" name="name">RFC</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="expired" model="name.docstatename">
<field type="CharField" name="name">Expired</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="repl" model="name.docstatename">
<field type="CharField" name="name">Replaced</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="active" model="name.docstatename">
<field type="CharField" name="name">Active</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="auth-rm" model="name.docstatename">
<field type="CharField" name="name">Withdrawn by Submitter</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ietf-rm" model="name.docstatename">
<field type="CharField" name="name">Withdrawn by IETF</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ietf" model="name.docstreamname">
<field type="CharField" name="name">IETF</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="indie" model="name.docstreamname">
<field type="CharField" name="name">Independent Submission</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="legacy" model="name.docstreamname">
<field type="CharField" name="name">Legacy</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="iab" model="name.docstreamname">
<field type="CharField" name="name">IAB</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="irtf" model="name.docstreamname">
<field type="CharField" name="name">IRTF</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="draft" model="name.doctypename">
<field type="CharField" name="name">Draft</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ext" model="name.doctypename">
<field type="CharField" name="name">External</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="bof" model="name.groupstatename">
<field type="CharField" name="name">BOF</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="proposed" model="name.groupstatename">
<field type="CharField" name="name">Proposed</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="active" model="name.groupstatename">
<field type="CharField" name="name">Active</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="dormant" model="name.groupstatename">
<field type="CharField" name="name">Dormant</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="conclude" model="name.groupstatename">
<field type="CharField" name="name">Concluded</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="unknown" model="name.groupstatename">
<field type="CharField" name="name">Unknown</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ietf" model="name.grouptypename">
<field type="CharField" name="name">IETF</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="area" model="name.grouptypename">
<field type="CharField" name="name">Area</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="wg" model="name.grouptypename">
<field type="CharField" name="name">WG</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="rg" model="name.grouptypename">
<field type="CharField" name="name">RG</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="team" model="name.grouptypename">
<field type="CharField" name="name">Team</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="individ" model="name.grouptypename">
<field type="CharField" name="name">Individual</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="pub-req" model="name.iesgdocstatename">
<field type="CharField" name="name">Publication Requested</field>
<field type="TextField" name="desc">A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">10</field>
</object>
<object pk="ad-eval" model="name.iesgdocstatename">
<field type="CharField" name="name">AD Evaluation</field>
<field type="TextField" name="desc">A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">11</field>
</object>
<object pk="review-e" model="name.iesgdocstatename">
<field type="CharField" name="name">Expert Review</field>
<field type="TextField" name="desc">An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the "MIB doctors". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the "note" field for specific details on the nature of the review.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">12</field>
</object>
<object pk="lc-req" model="name.iesgdocstatename">
<field type="CharField" name="name">Last Call requested</field>
<field type="TextField" name="desc">The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">15</field>
</object>
<object pk="lc" model="name.iesgdocstatename">
<field type="CharField" name="name">In Last Call</field>
<field type="TextField" name="desc">The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">16</field>
</object>
<object pk="writeupw" model="name.iesgdocstatename">
<field type="CharField" name="name">Waiting for Writeup</field>
<field type="TextField" name="desc">Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">18</field>
</object>
<object pk="goaheadw" model="name.iesgdocstatename">
<field type="CharField" name="name">Waiting for AD Go-Ahead</field>
<field type="TextField" name="desc">As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">19</field>
</object>
<object pk="iesg-eva" model="name.iesgdocstatename">
<field type="CharField" name="name">IESG Evaluation</field>
<field type="TextField" name="desc">The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as "discuss" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">20</field>
</object>
<object pk="defer" model="name.iesgdocstatename">
<field type="CharField" name="name">IESG Evaluation - Defer</field>
<field type="TextField" name="desc">During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">21</field>
</object>
<object pk="approved" model="name.iesgdocstatename">
<field type="CharField" name="name">Approved-announcement to be sent</field>
<field type="TextField" name="desc">The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">27</field>
</object>
<object pk="ann" model="name.iesgdocstatename">
<field type="CharField" name="name">Approved-announcement sent</field>
<field type="TextField" name="desc">The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">30</field>
</object>
<object pk="rfcqueue" model="name.iesgdocstatename">
<field type="CharField" name="name">RFC Ed Queue</field>
<field type="TextField" name="desc">The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html).</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">31</field>
</object>
<object pk="pub" model="name.iesgdocstatename">
<field type="CharField" name="name">RFC Published</field>
<field type="TextField" name="desc">The ID has been published as an RFC.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">32</field>
</object>
<object pk="nopubadw" model="name.iesgdocstatename">
<field type="CharField" name="name">DNP-waiting for AD note</field>
<field type="TextField" name="desc">Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the "note" field for more details on who has the action item.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">33</field>
</object>
<object pk="nopubanw" model="name.iesgdocstatename">
<field type="CharField" name="name">DNP-announcement to be sent</field>
<field type="TextField" name="desc">The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official "do not publish" recommendation message.</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">34</field>
</object>
<object pk="watching" model="name.iesgdocstatename">
<field type="CharField" name="name">AD is watching</field>
<field type="TextField" name="desc">An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and "I-D Exists" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons).</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">42</field>
</object>
<object pk="dead" model="name.iesgdocstatename">
<field type="CharField" name="name">Dead</field>
<field type="TextField" name="desc">Document is "dead" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.)</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">99</field>
</object>
<object pk="bcp" model="name.intendedstdlevelname">
<field type="CharField" name="name">Best Current Practice</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ds" model="name.intendedstdlevelname">
<field type="CharField" name="name">Draft Standard</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="exp" model="name.intendedstdlevelname">
<field type="CharField" name="name">Experimental</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="hist" model="name.intendedstdlevelname">
<field type="CharField" name="name">Historic</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="inf" model="name.intendedstdlevelname">
<field type="CharField" name="name">Informational</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ps" model="name.intendedstdlevelname">
<field type="CharField" name="name">Proposed Standard</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="std" model="name.intendedstdlevelname">
<field type="CharField" name="name">Standard</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ad" model="name.rolename">
<field type="CharField" name="name">Area Director</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ex-ad" model="name.rolename">
<field type="CharField" name="name">Ex-Area Director</field>
<field type="TextField" name="desc">Inactive Area Director</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="chair" model="name.rolename">
<field type="CharField" name="name">Chair</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="editor" model="name.rolename">
<field type="CharField" name="name">Editor</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="secr" model="name.rolename">
<field type="CharField" name="name">Secretary</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="techadv" model="name.rolename">
<field type="CharField" name="name">Tech Advisor</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="std" model="name.stdlevelname">
<field type="CharField" name="name">Standard</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ds" model="name.stdlevelname">
<field type="CharField" name="name">Draft Standard</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="ps" model="name.stdlevelname">
<field type="CharField" name="name">Proposed Standard</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="inf" model="name.stdlevelname">
<field type="CharField" name="name">Informational</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="exp" model="name.stdlevelname">
<field type="CharField" name="name">Experimental</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="bcp" model="name.stdlevelname">
<field type="CharField" name="name">Best Current Practice</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="hist" model="name.stdlevelname">
<field type="CharField" name="name">Historic</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="unkn" model="name.stdlevelname">
<field type="CharField" name="name">Unknown</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
</django-objects>

View file

@ -0,0 +1,40 @@
#!/usr/bin/python
# boiler plate
import os, sys
one_dir_up = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../'))
sys.path.insert(0, one_dir_up)
from django.core.management import setup_environ
import settings
setup_environ(settings)
# script
from django.core.serializers import serialize
from django.db.models import Q
def output(name, qs):
try:
f = open(os.path.join(settings.BASE_DIR, "idrfc/fixtures/%s.xml" % name), 'w')
f.write(serialize("xml", qs, indent=4))
f.close()
except:
from django.db import connection
from pprint import pprint
pprint(connection.queries)
raise
# pick all name models directly out of the module
names = []
import name.models
for n in dir(name.models):
if n[:1].upper() == n[:1] and n.endswith("Name"):
model = getattr(name.models, n)
if not model._meta.abstract:
names.extend(model.objects.all())
output("names", names)

View file

@ -40,6 +40,7 @@ from django.utils import simplejson as json
from django.db.models import Q
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
import types
BALLOT_ACTIVE_STATES = ['In Last Call',
@ -90,7 +91,7 @@ class IdWrapper:
def __init__(self, draft):
self.id = self
if isinstance(draft, IDInternal):
if isinstance(draft, IDInternal) and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
self._idinternal = draft
self._draft = self._idinternal.draft
else:
@ -119,6 +120,15 @@ class IdWrapper:
self.publication_date = date(1990,1,1)
def rfc_editor_state(self):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
if self._draft.rfc_state:
# extract possible extra states
tags = self._draft.tags.filter(slug__in=("iana-crd", "ref", "missref"))
s = [self._draft.rfc_state.name] + [t.slug.replace("-crd", "").upper() for t in tags]
return " ".join(s)
else:
return None
try:
qs = self._draft.rfc_editor_queue_state
return qs.state
@ -281,10 +291,16 @@ class RfcWrapper:
self.rfc = self
if not self._idinternal:
try:
self._idinternal = IDInternal.objects.get(rfc_flag=1, draft=self._rfcindex.rfc_number)
except IDInternal.DoesNotExist:
pass
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
pub = rfcindex.rfc_published_date
started = rfcindex.started_iesg_process if hasattr(rfcindex, 'started_iesg_process') else rfcindex.latest_event(type="started_iesg_process")
if pub and started and pub < started.time.date():
self._idinternal = rfcindex
else:
try:
self._idinternal = IDInternal.objects.get(rfc_flag=1, draft=self._rfcindex.rfc_number)
except IDInternal.DoesNotExist:
pass
if self._idinternal:
self.ietf_process = IetfProcessData(self._idinternal)
@ -295,7 +311,12 @@ class RfcWrapper:
self.maturity_level = self._rfcindex.current_status
if not self.maturity_level:
self.maturity_level = "Unknown"
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
if not rfcindex.name.startswith('rfc'):
self.draft_name = rfcindex.name
return # we've already done the lookup while importing so skip the rest
ids = InternetDraft.objects.filter(rfc_number=self.rfc_number)
if len(ids) >= 1:
self.draft_name = ids[0].filename
@ -307,10 +328,10 @@ class RfcWrapper:
self.draft_name = self._rfcindex.draft
def _rfc_doc_list(self, name):
if (not self._rfcindex) or (not self._rfcindex.__dict__[name]):
if (not self._rfcindex) or (not getattr(self._rfcindex, name)):
return None
else:
s = self._rfcindex.__dict__[name]
s = getattr(self._rfcindex, name)
s = s.replace(",", ", ")
s = re.sub("([A-Z])([0-9])", "\\1 \\2", s)
return s
@ -418,7 +439,7 @@ class IetfProcessData:
# don't call this unless has_[active_]iesg_ballot returns True
def iesg_ballot_needed( self ):
standardsTrack = 'Standard' in self.intended_maturity_level() or \
self.intended_maturity_level() == "BCP"
self.intended_maturity_level() in ("BCP", "Best Current Practice")
return self.iesg_ballot().ballot.needed( standardsTrack )
def ad_name(self):
@ -436,9 +457,21 @@ class IetfProcessData:
def state_date(self):
try:
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return self._idinternal.docevent_set.filter(
Q(desc__istartswith="Draft Added by ")|
Q(desc__istartswith="Draft Added in state ")|
Q(desc__istartswith="Draft added in state ")|
Q(desc__istartswith="State changed to ")|
Q(desc__istartswith="State Changes to ")|
Q(desc__istartswith="Sub state has been changed to ")|
Q(desc__istartswith="State has been changed to ")|
Q(desc__istartswith="IESG has approved and state has been changed to")).order_by('-time')[0].time.date()
return self._idinternal.comments().filter(
Q(comment_text__istartswith="Draft Added by ")|
Q(comment_text__istartswith="Draft Added in state ")|
Q(comment_text__istartswith="Draft added in state ")|
Q(comment_text__istartswith="State changed to ")|
Q(comment_text__istartswith="State Changes to ")|
Q(comment_text__istartswith="Sub state has been changed to ")|
@ -513,10 +546,10 @@ class IdRfcWrapper:
self.id = id
self.rfc = rfc
if id:
iprs = IprDraft.objects.filter(document=self.id.tracker_id)
iprs = IprDraft.objects.filter(document=self.id.tracker_id, ipr__status__in=[1,3])
self.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(self.id.tracker_id)
elif rfc:
iprs = IprRfc.objects.filter(document=self.rfc.rfc_number)
iprs = IprRfc.objects.filter(document=self.rfc.rfc_number, ipr__status__in=[1,3])
self.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(self.rfc.rfc_number)
else:
raise ValueError("Construction with null id and rfc")
@ -670,8 +703,63 @@ class BallotWrapper:
return []
else:
return self._ballot_set.exclude(draft=self._idinternal)
def _init(self):
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
self.old_init()
return
from redesign.person.models import Person
active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct()
positions = []
seen = {}
from doc.models import BallotPositionDocEvent
for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", time__gte=self.ballot.process_start, time__lte=self.ballot.process_end).select_related('ad').order_by("-time", '-id'):
if pos.ad not in seen:
p = dict(ad_name=pos.ad.name,
ad_username=pos.ad.pk, # ought to rename this in doc_ballot_list
position=pos.pos.name,
is_old_ad=pos.ad not in active_ads,
old_positions=[])
if pos.pos.slug == "discuss":
p["has_text"] = True
p["discuss_text"] = pos.discuss
p["discuss_date"] = pos.discuss_time
p["discuss_revision"] = pos.doc.rev # FIXME: wrong
if pos.comment:
p["has_text"] = True
p["comment_text"] = pos.comment
p["comment_date"] = pos.comment_time
p["comment_revision"] = pos.doc.rev # FIXME: wrong
positions.append(p)
seen[pos.ad] = p
else:
latest = seen[pos.ad]
if latest["old_positions"]:
prev = latest["old_positions"][-1]
else:
prev = latest["position"]
if prev != pos.pos.name:
seen[pos.ad]["old_positions"].append(pos.pos.name)
# add any missing ADs as No Record
if self.ballot_active:
for ad in active_ads:
if ad not in seen:
d = dict(ad_name=ad.name,
ad_username=ad.pk,
position="No Record",
)
positions.append(d)
self._positions = positions
def old_init(self):
try:
ads = set()
except NameError:
@ -770,7 +858,7 @@ def position_to_string(position):
return "No Record"
p = None
for k,v in positions.iteritems():
if position.__dict__[k] > 0:
if getattr(position, k) > 0:
p = v
if not p:
p = "No Record"

View file

@ -2,10 +2,16 @@
import datetime
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
from django.conf import settings
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo
from ietf.idrfc.mails import *
from ietf.idrfc.utils import *
from doc.models import Document, DocEvent, LastCallDocEvent, WriteupDocEvent, save_document_in_history
from name.models import IesgDocStateName
from person.models import Person
def request_last_call(request, doc):
try:
ballot = doc.idinternal.ballot
@ -15,10 +21,37 @@ def request_last_call(request, doc):
send_last_call_request(request, doc, ballot)
add_document_comment(request, doc, "Last Call was requested")
def request_last_callREDESIGN(request, doc):
if not doc.latest_event(type="changed_ballot_writeup_text"):
generate_ballot_writeup(request, doc)
if not doc.latest_event(type="changed_ballot_approval_text"):
generate_approval_mail(request, doc)
if not doc.latest_event(type="changed_last_call_text"):
generate_last_call_announcement(request, doc)
send_last_call_request(request, doc)
e = DocEvent()
e.type = "requested_last_call"
e.by = request.user.get_profile()
e.doc = doc
e.desc = "Last call was requested by %s" % e.by.name
e.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
request_last_call = request_last_callREDESIGN
def get_expired_last_calls():
return InternetDraft.objects.filter(lc_expiration_date__lte=datetime.date.today(),
idinternal__cur_state__document_state_id=IDState.IN_LAST_CALL)
def get_expired_last_callsREDESIGN():
today = datetime.date.today()
for d in Document.objects.filter(iesg_state="lc"):
e = d.latest_event(LastCallDocEvent, type="sent_last_call")
if e and e.expires.date() <= today:
yield d
def expire_last_call(doc):
state = IDState.WAITING_FOR_WRITEUP
@ -36,3 +69,28 @@ def expire_last_call(doc):
log_state_changed(None, doc, by="system", email_watch_list=False)
email_last_call_expired(doc)
def expire_last_callREDESIGN(doc):
state = IesgDocStateName.objects.get(slug="writeupw")
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
if e and "What does this protocol do and why" not in e.text:
# if it boiler-plate text has been removed, we assume the
# write-up has been written
state = IesgDocStateName.objects.get(slug="goaheadw")
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = state
e = log_state_changed(None, doc, Person.objects.get(name="(System)"), prev)
doc.time = e.time
doc.save()
email_last_call_expired(doc)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
get_expired_last_calls = get_expired_last_callsREDESIGN
expire_last_call = expire_last_callREDESIGN

View file

@ -11,6 +11,8 @@ from django.core.urlresolvers import reverse as urlreverse
from ietf.utils.mail import send_mail, send_mail_text
from ietf.idtracker.models import *
from ietf.ipr.search import iprs_from_docs
from doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent
from person.models import Person
def email_state_changed(request, doc, text):
to = [x.strip() for x in doc.idinternal.state_change_notice_to.replace(';', ',').split(',')]
@ -21,6 +23,21 @@ def email_state_changed(request, doc, text):
dict(text=text,
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def email_state_changedREDESIGN(request, doc, text):
to = [x.strip() for x in doc.notify.replace(';', ',').split(',')]
if not to:
return
text = strip_tags(text)
send_mail(request, to, None,
"ID Tracker State Update Notice: %s" % doc.file_tag(),
"idrfc/state_changed_email.txt",
dict(text=text,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_state_changed = email_state_changedREDESIGN
def html_to_text(html):
return strip_tags(html.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&").replace("<br>", "\n"))
@ -37,6 +54,23 @@ def email_owner(request, doc, owner, changed_by, text, subject=None):
doc=doc,
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def email_ownerREDESIGN(request, doc, owner, changed_by, text, subject=None):
if not owner or not changed_by or owner == changed_by:
return
to = owner.formatted_email()
send_mail(request, to,
"DraftTracker Mail System <iesg-secretary@ietf.org>",
"%s updated by %s" % (doc.file_tag(), changed_by.name),
"idrfc/change_notice.txt",
dict(text=html_to_text(text),
doc=doc,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_owner = email_ownerREDESIGN
def full_intended_status(intended_status):
s = str(intended_status)
# FIXME: this should perhaps be defined in the db
@ -57,6 +91,17 @@ def full_intended_status(intended_status):
else:
return "a %s" % s
def generate_ballot_writeup(request, doc):
e = WriteupDocEvent()
e.type = "changed_ballot_writeup_text"
e.by = request.user.get_profile()
e.doc = doc
e.desc = u"Ballot writeup was generated"
e.text = unicode(render_to_string("idrfc/ballot_writeup.txt"))
e.save()
return e
def generate_last_call_announcement(request, doc):
status = full_intended_status(doc.intended_status).replace("a ", "").replace("an ", "")
@ -97,6 +142,58 @@ def generate_last_call_announcement(request, doc):
)
)
def generate_last_call_announcementREDESIGN(request, doc):
doc.full_status = full_intended_status(doc.intended_std_level)
status = doc.full_status.replace("a ", "").replace("an ", "")
expiration_date = date.today() + timedelta(days=14)
cc = []
if doc.group.type_id == "individ":
group = "an individual submitter"
expiration_date += timedelta(days=14)
else:
group = "the %s WG (%s)" % (doc.group.name, doc.group.acronym)
if doc.group.list_email:
cc.append(doc.group.list_email)
doc.filled_title = textwrap.fill(doc.title, width=70, subsequent_indent=" " * 3)
url = settings.IDTRACKER_BASE_URL + doc.get_absolute_url()
iprs, docs = iprs_from_docs([ doc ])
if iprs:
ipr_links = [ urlreverse("ietf.ipr.views.show", kwargs=dict(ipr_id=i.ipr_id)) for i in iprs]
ipr_links = [ settings.IDTRACKER_BASE_URL+url if not url.startswith("http") else url for url in ipr_links ]
else:
ipr_links = None
mail = render_to_string("idrfc/last_call_announcement.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
expiration_date=expiration_date.strftime("%Y-%m-%d"), #.strftime("%B %-d, %Y"),
cc=", ".join("<%s>" % e for e in cc),
group=group,
docs=docs,
urls=[url],
status=status,
impl_report="Draft" in status or "Full" in status,
ipr_links=ipr_links,
)
)
e = WriteupDocEvent()
e.type = "changed_last_call_text"
e.by = request.user.get_profile()
e.doc = doc
e.desc = u"Last call announcement was generated"
e.text = unicode(mail)
e.save()
return e
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_last_call_announcement = generate_last_call_announcementREDESIGN
def generate_approval_mail(request, doc):
if doc.idinternal.cur_state_id in IDState.DO_NOT_PUBLISH_STATES or doc.idinternal.via_rfc_editor:
return generate_approval_mail_rfc_editor(request, doc)
@ -125,7 +222,7 @@ def generate_approval_mail(request, doc):
if len(docs) > 1:
made_by = "These documents have been reviewed in the IETF but are not the products of an IETF Working Group."
else:
made_by = "This document has been reviewed in the IETF but is not the product of an IETF Working Group.";
made_by = "This document has been reviewed in the IETF but is not the product of an IETF Working Group."
else:
if len(docs) > 1:
made_by = "These documents are products of the %s." % doc.group.name_with_wg
@ -170,6 +267,96 @@ def generate_approval_mail_rfc_editor(request, doc):
)
)
DO_NOT_PUBLISH_IESG_STATES = ("nopubadw", "nopubanw")
def generate_approval_mailREDESIGN(request, doc):
if doc.iesg_state_id in DO_NOT_PUBLISH_IESG_STATES or doc.tags.filter(slug='via-rfc'):
mail = generate_approval_mail_rfc_editor(request, doc)
else:
mail = generate_approval_mail_approved(request, doc)
e = WriteupDocEvent()
e.type = "changed_ballot_approval_text"
e.by = request.user.get_profile()
e.doc = doc
e.desc = u"Ballot approval text was generated"
e.text = unicode(mail)
e.save()
return e
def generate_approval_mail_approved(request, doc):
doc.full_status = full_intended_status(doc.intended_std_level.name)
status = doc.full_status.replace("a ", "").replace("an ", "")
if "an " in status:
action_type = "Document"
else:
action_type = "Protocol"
cc = settings.DOC_APPROVAL_EMAIL_CC
# the second check catches some area working groups (like
# Transport Area Working Group)
if doc.group.type_id != "area" and not doc.group.name.endswith("Working Group"):
doc.group.name_with_wg = doc.group.name + " Working Group"
if doc.group.list_email:
cc.append("%s mailing list <%s>" % (doc.group.acronym, doc.group.list_email))
cc.append("%s chair <%s-chairs@tools.ietf.org>" % (doc.group.acronym, doc.group.acronym))
else:
doc.group.name_with_wg = doc.group.name
doc.filled_title = textwrap.fill(doc.title, width=70, subsequent_indent=" " * 3)
if doc.group.type_id == "individ":
made_by = "This document has been reviewed in the IETF but is not the product of an IETF Working Group."
else:
made_by = "This document is the product of the %s." % doc.group.name_with_wg
director = doc.ad
other_director = Person.objects.filter(email__role__group__role__email__person=director, email__role__group__role__name="ad").exclude(pk=director.pk)
if doc.group.type_id != "individ" and other_director:
contacts = "The IESG contact persons are %s and %s." % (director.name, other_director[0].name)
else:
contacts = "The IESG contact person is %s." % director.name
doc_type = "RFC" if doc.state_id == "rfc" else "Internet Draft"
return render_to_string("idrfc/approval_mail.txt",
dict(doc=doc,
docs=[doc],
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
cc=",\n ".join(cc),
doc_type=doc_type,
made_by=made_by,
contacts=contacts,
status=status,
action_type=action_type,
)
)
def generate_approval_mail_rfc_editorREDESIGN(request, doc):
full_status = full_intended_status(doc.intended_std_level.name)
status = full_status.replace("a ", "").replace("an ", "")
disapproved = doc.iesg_state_id in DO_NOT_PUBLISH_IESG_STATES
doc_type = "RFC" if doc.state_id == "rfc" else "Internet Draft"
return render_to_string("idrfc/approval_mail_rfc_editor.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
doc_type=doc_type,
status=status,
full_status=full_status,
disapproved=disapproved,
)
)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_approval_mail = generate_approval_mailREDESIGN
generate_approval_mail_rfc_editor = generate_approval_mail_rfc_editorREDESIGN
def send_last_call_request(request, doc, ballot):
to = "iesg-secretary@ietf.org"
frm = '"DraftTracker Mail System" <iesg-secretary@ietf.org>'
@ -181,6 +368,19 @@ def send_last_call_request(request, doc, ballot):
dict(docs=docs,
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def send_last_call_requestREDESIGN(request, doc):
to = "iesg-secretary@ietf.org"
frm = '"DraftTracker Mail System" <iesg-secretary@ietf.org>'
send_mail(request, to, frm,
"Last Call: %s" % doc.file_tag(),
"idrfc/last_call_request.txt",
dict(docs=[doc],
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
send_last_call_request = send_last_call_requestREDESIGN
def email_resurrect_requested(request, doc, by):
to = "I-D Administrator <internet-drafts@ietf.org>"
frm = u"%s <%s>" % by.person.email()
@ -191,6 +391,19 @@ def email_resurrect_requested(request, doc, by):
by=frm,
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def email_resurrect_requestedREDESIGN(request, doc, by):
to = "I-D Administrator <internet-drafts@ietf.org>"
frm = by.formatted_email()
send_mail(request, to, frm,
"I-D Resurrection Request",
"idrfc/resurrect_request_email.txt",
dict(doc=doc,
by=frm,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_resurrect_requested = email_resurrect_requestedREDESIGN
def email_resurrection_completed(request, doc):
to = u"%s <%s>" % doc.idinternal.resurrect_requested_by.person.email()
frm = "I-D Administrator <internet-drafts-reply@ietf.org>"
@ -201,6 +414,19 @@ def email_resurrection_completed(request, doc):
by=frm,
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def email_resurrection_completedREDESIGN(request, doc, requester):
to = requester.formatted_email()
frm = "I-D Administrator <internet-drafts-reply@ietf.org>"
send_mail(request, to, frm,
"I-D Resurrection Completed - %s" % doc.file_tag(),
"idrfc/resurrect_completed_email.txt",
dict(doc=doc,
by=frm,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_resurrection_completed = email_resurrection_completedREDESIGN
def email_ballot_deferred(request, doc, by, telechat_date):
to = "iesg@ietf.org"
frm = "DraftTracker Mail System <iesg-secretary@ietf.org>"
@ -267,7 +493,79 @@ def generate_issue_ballot_mail(request, doc):
ad_feedback=ad_feedback
)
)
def generate_issue_ballot_mailREDESIGN(request, doc):
full_status = full_intended_status(doc.intended_std_level.name)
status = full_status.replace("a ", "").replace("an ", "")
active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct()
e = doc.latest_event(type="started_iesg_process")
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad')
# format positions and setup discusses and comments
ad_feedback = []
seen = set()
active_ad_positions = []
inactive_ad_positions = []
for p in positions:
if p.ad in seen:
continue
seen.add(p.ad)
def formatted(val):
if val:
return "[ X ]"
else:
return "[ ]"
fmt = u"%-21s%-10s%-11s%-9s%-10s" % (
p.ad.name[:21],
formatted(p.pos_id == "yes"),
formatted(p.pos_id == "noobj"),
formatted(p.pos_id == "discuss"),
"[ R ]" if p.pos_id == "recuse" else formatted(p.pos_id == "abstain"),
)
if p.ad in active_ads:
active_ad_positions.append(fmt)
if not p.pos_id == "discuss":
p.discuss = ""
if p.comment or p.discuss:
ad_feedback.append(p)
else:
inactive_ad_positions.append(fmt)
active_ad_positions.sort()
inactive_ad_positions.sort()
ad_feedback.sort(key=lambda p: p.ad.name)
e = doc.latest_event(LastCallDocEvent, type="sent_last_call")
last_call_expires = e.expires if e else None
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
approval_text = e.text if e else ""
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
ballot_writeup = e.text if e else ""
return render_to_string("idrfc/issue_ballot_mailREDESIGN.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
status=status,
active_ad_positions=active_ad_positions,
inactive_ad_positions=inactive_ad_positions,
ad_feedback=ad_feedback,
last_call_expires=last_call_expires,
approval_text=approval_text,
ballot_writeup=ballot_writeup,
)
)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_issue_ballot_mail = generate_issue_ballot_mailREDESIGN
def email_iana(request, doc, to, msg):
# fix up message and send message to IANA for each in ballot set
import email
@ -285,6 +583,25 @@ def email_iana(request, doc, to, msg):
extra=extra,
bcc="fenner@research.att.com")
def email_ianaREDESIGN(request, doc, to, msg):
# fix up message and send it with extra info on doc in headers
import email
parsed_msg = email.message_from_string(msg.encode("utf-8"))
extra = {}
extra["Reply-To"] = "noreply@ietf.org"
extra["X-IETF-Draft-string"] = doc.name
extra["X-IETF-Draft-revision"] = doc.rev
send_mail_text(request, "IANA <%s>" % to,
parsed_msg["From"], parsed_msg["Subject"],
parsed_msg.get_payload(),
extra=extra,
bcc="fenner@research.att.com")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_iana = email_ianaREDESIGN
def email_last_call_expired(doc):
text = "IETF Last Call has ended, and the state has been changed to\n%s." % doc.idinternal.cur_state.state
@ -298,3 +615,19 @@ def email_last_call_expired(doc):
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()),
cc="iesg-secretary@ietf.org")
def email_last_call_expiredREDESIGN(doc):
text = "IETF Last Call has ended, and the state has been changed to\n%s." % doc.iesg_state.name
send_mail(None,
"iesg@ietf.org",
"DraftTracker Mail System <iesg-secretary@ietf.org>",
"Last Call Expired: %s" % doc.file_tag(),
"idrfc/change_notice.txt",
dict(text=text,
doc=doc,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()),
cc="iesg-secretary@ietf.org")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_last_call_expired = email_last_call_expiredREDESIGN

View file

@ -69,8 +69,7 @@ def parse(response):
events.expandNode(node)
node.normalize()
draft_name = getChildText(node, "draft").strip()
if re.search("-\d\d\.txt$", draft_name):
draft_name = draft_name[0:-7]
draft_name = re.sub("(-\d\d)?(.txt){1,2}$", "", draft_name)
date_received = getChildText(node, "date-received")
states = []
@ -169,6 +168,9 @@ def parse_all(response):
refs.extend(indirect_refs)
del(indirect_refs)
if settings.USE_DB_REDESIGN_PROXY_CLASSES: # note: return before id lookup
return (drafts, refs)
# convert filenames to id_document_tags
log("connecting to database...")
cursor = db.connection.cursor()
@ -190,7 +192,79 @@ def insert_into_database(drafts, refs):
cursor.close()
db.connection._commit()
db.connection.close()
import django.db.transaction
def get_rfc_tag_mapping():
"""Return dict with RFC Editor state name -> DocInfoTagName"""
from name.models import DocInfoTagName
from name.utils import name
return {
'IANA': name(DocInfoTagName, 'iana-crd', 'IANA coordination', "RFC-Editor/IANA Registration Coordination"),
'REF': name(DocInfoTagName, 'ref', 'Holding for references', "Holding for normative reference"),
'MISSREF': name(DocInfoTagName, 'missref', 'Missing references', "Awaiting missing normative reference"),
}
def get_rfc_state_mapping():
"""Return dict with RFC Editor state name -> RfcDocStateName"""
from name.models import RfcDocStateName
from name.utils import name
return {
'AUTH': name(RfcDocStateName, 'auth', 'AUTH', "Awaiting author action"),
'AUTH48': name(RfcDocStateName, 'auth48', "AUTH48", "Awaiting final author approval"),
'EDIT': name(RfcDocStateName, 'edit', 'EDIT', "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing"),
'IANA': name(RfcDocStateName, 'iana-crd', 'IANA', "RFC-Editor/IANA Registration Coordination"),
'IESG': name(RfcDocStateName, 'iesg', 'IESG', "Holding for IESG action"),
'ISR': name(RfcDocStateName, 'isr', 'ISR', "Independent Submission Review by the ISE "),
'ISR-AUTH': name(RfcDocStateName, 'isr-auth', 'ISR-AUTH', "Independent Submission awaiting author update, or in discussion between author and ISE"),
'REF': name(RfcDocStateName, 'ref', 'REF', "Holding for normative reference"),
'RFC-EDITOR': name(RfcDocStateName, 'rfc-edit', 'RFC-EDITOR', "Awaiting final RFC Editor review before AUTH48"),
'TO': name(RfcDocStateName, 'timeout', 'TO', "Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work"),
'MISSREF': name(RfcDocStateName, 'missref', 'MISSREF', "Awaiting missing normative reference"),
}
@django.db.transaction.commit_on_success
def insert_into_databaseREDESIGN(drafts, refs):
from doc.models import Document
from name.models import DocInfoTagName
tags = get_rfc_tag_mapping()
states = get_rfc_state_mapping()
rfc_editor_tags = tags.values()
log("removing old data...")
for d in Document.objects.exclude(rfc_state=None).filter(tags__in=rfc_editor_tags):
d.tags.remove(*rfc_editor_tags)
Document.objects.exclude(rfc_state=None).update(rfc_state=None)
log("inserting new data...")
for name, date_received, state, stream_id in drafts:
try:
d = Document.objects.get(name=name)
except Document.DoesNotExist:
log("unknown document %s" % name)
continue
s = state.split(" ")
if s:
# first is state
d.rfc_state = states[s[0]]
d.save()
# remainding are tags
for x in s[1:]:
d.tags.add(tags[x])
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
insert_into_database = insert_into_databaseREDESIGN
if __name__ == '__main__':
try:
log("output from mirror_rfc_editor_queue.py:\n")

View file

@ -38,7 +38,7 @@ from django import db
from xml.dom import pulldom, Node
import re
import urllib2
from datetime import datetime
from datetime import datetime, date, timedelta
import socket
import sys
@ -147,6 +147,174 @@ def insert_to_database(data):
db.connection._commit()
db.connection.close()
def get_std_level_mapping():
from name.models import StdLevelName
from name.utils import name
return {
"Standard": name(StdLevelName, "std", "Standard"),
"Draft Standard": name(StdLevelName, "ds", "Draft Standard"),
"Proposed Standard": name(StdLevelName, "ps", "Proposed Standard"),
"Informational": name(StdLevelName, "inf", "Informational"),
"Experimental": name(StdLevelName, "exp", "Experimental"),
"Best Current Practice": name(StdLevelName, "bcp", "Best Current Practice"),
"Historic": name(StdLevelName, "hist", "Historic"),
"Unknown": name(StdLevelName, "unkn", "Unknown"),
}
def get_stream_mapping():
from name.models import DocStreamName
from name.utils import name
return {
"Legacy": name(DocStreamName, "legacy", "Legacy"),
"IETF": name(DocStreamName, "ietf", "IETF"),
"INDEPENDENT": name(DocStreamName, "indie", "Independent Submission"),
"IAB": name(DocStreamName, "iab", "IAB"),
"IRTF": name(DocStreamName, "irtf", "IRTF"),
}
import django.db.transaction
@django.db.transaction.commit_on_success
def insert_to_databaseREDESIGN(data):
from person.models import Person
from doc.models import Document, DocAlias, DocEvent, RelatedDocument
from group.models import Group
from name.models import DocInfoTagName, DocRelationshipName
from name.utils import name
system = Person.objects.get(name="(System)")
std_level_mapping = get_std_level_mapping()
stream_mapping = get_stream_mapping()
tag_has_errata = name(DocInfoTagName, 'errata', "Has errata")
relationship_obsoletes = name(DocRelationshipName, "obs", "Obsoletes")
relationship_updates = name(DocRelationshipName, "updates", "Updates")
skip_older_than_date = (date.today() - timedelta(days=365)).strftime("%Y-%m-%d")
log("updating data...")
for d in data:
rfc_number, title, authors, rfc_published_date, current_status, updates, updated_by, obsoletes, obsoleted_by, also, draft, has_errata, stream, wg, file_formats = d
if rfc_published_date < skip_older_than_date:
# speed up the process by skipping old entries
continue
# we assume two things can happen: we get a new RFC, or an
# attribute has been updated at the RFC Editor (RFC Editor
# attributes currently take precedence over our local
# attributes)
# make sure we got the document and alias
created = False
doc = None
name = "rfc%s" % rfc_number
a = DocAlias.objects.filter(name=name)
if a:
doc = a[0].document
else:
if draft:
try:
doc = Document.objects.get(name=draft)
except Document.DoesNotExist:
pass
if not doc:
created = True
log("created document %s" % name)
doc = Document.objects.create(name=name)
# add alias
DocAlias.objects.create(name=name, document=doc)
if not created:
created = True
log("created alias %s to %s" % (name, doc.name))
# check attributes
changed = False
if title != doc.title:
doc.title = title
changed = True
if std_level_mapping[current_status] != doc.std_level:
doc.std_level = std_level_mapping[current_status]
changed = True
if doc.state_id != "rfc":
doc.state_id = "rfc"
changed = True
if doc.stream != stream_mapping[stream]:
doc.stream = stream_mapping[stream]
changed = True
if not doc.group and wg:
doc.group = Group.objects.get(acronym=wg)
changed = True
pubdate = datetime.strptime(rfc_published_date, "%Y-%m-%d")
if not doc.latest_event(type="published_rfc", time=pubdate):
e = DocEvent(doc=doc, type="published_rfc")
e.time = pubdate
e.by = system
e.desc = "RFC published"
e.save()
changed = True
def parse_relation_list(s):
if not s:
return []
res = []
for x in s.split(","):
if x[:3] in ("NIC", "IEN", "STD", "RTR"):
# try translating this to RFCs that we can handle
# sensibly; otherwise we'll have to ignore them
l = DocAlias.objects.filter(name__startswith="rfc", document__docalias__name=x.lower())
else:
l = DocAlias.objects.filter(name=x.lower())
for a in l:
if a not in res:
res.append(a)
return res
for x in parse_relation_list(obsoletes):
if not RelatedDocument.objects.filter(source=doc, target=x, relationship=relationship_obsoletes):
RelatedDocument.objects.create(source=doc, target=x, relationship=relationship_obsoletes)
changed = True
for x in parse_relation_list(updates):
if not RelatedDocument.objects.filter(source=doc, target=x, relationship=relationship_updates):
RelatedDocument.objects.create(source=doc, target=x, relationship=relationship_updates)
changed = True
if also:
for a in also.lower().split(","):
if not DocAlias.objects.filter(name=a):
DocAlias.objects.create(name=a, document=doc)
changed = True
if has_errata:
if not doc.tags.filter(pk=tag_has_errata.pk):
doc.tags.add(tag_has_errata)
changed = True
else:
if doc.tags.filter(pk=tag_has_errata.pk):
doc.tags.remove(tag_has_errata)
changed = True
if changed:
if not created:
log("%s changed" % name)
doc.time = datetime.now()
doc.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
insert_to_database = insert_to_databaseREDESIGN
if __name__ == '__main__':
try:
log("output from mirror_rfc_index.py:\n")

View file

@ -96,4 +96,9 @@ class DraftVersions(models.Model):
return "DraftVersions"+self.filename+self.revision+str(self.revision_date)
class Meta:
db_table = "draft_versions_mirror"
from django.conf import settings
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
RfcIndexOld = RfcIndex
from redesign.doc.proxy import RfcIndex

View file

@ -32,6 +32,7 @@
from django import template
from django.core.urlresolvers import reverse as urlreverse
from django.conf import settings
from ietf.idtracker.models import IDInternal, BallotInfo
from ietf.idrfc.idrfc_wrapper import position_to_string, BALLOT_ACTIVE_STATES
from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days
@ -40,12 +41,21 @@ register = template.Library()
def get_user_adid(context):
if 'user' in context and in_group(context['user'], "Area_Director"):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return context['user'].get_profile().id
return context['user'].get_profile().iesg_login_id()
else:
return None
def get_user_name(context):
if 'user' in context and context['user'].is_authenticated():
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from person.models import Person
try:
return context['user'].get_profile().name
except Person.DoesNotExist:
return None
person = context['user'].get_profile().person()
if person:
return str(person)
@ -61,7 +71,7 @@ def render_ballot_icon(context, doc):
return ""
if str(doc.cur_state) not in BALLOT_ACTIVE_STATES:
return ""
if doc.rfc_flag:
if doc.rfc_flag and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
name = doc.document().filename()
else:
name = doc.document().filename

View file

@ -774,6 +774,34 @@ class ExpireIDsTestCase(django.test.TestCase):
self.assertTrue(in_id_expire_freeze(datetime.datetime(2010, 7, 12, 10, 0)))
self.assertTrue(in_id_expire_freeze(datetime.datetime(2010, 7, 25, 0, 0)))
self.assertTrue(not in_id_expire_freeze(datetime.datetime(2010, 7, 26, 0, 0)))
def test_warn_expirable_ids(self):
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id
# hack into almost expirable state
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft.status = IDStatus.objects.get(status="Active")
draft.review_by_rfc_editor = 0
draft.revision_date = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE - 7)
draft.idinternal.cur_state_id = IDState.AD_WATCHING
draft.idinternal.save()
draft.save()
author = PersonOrOrgInfo.objects.all()[0]
IDAuthor.objects.create(document=draft, person=author, author_order=1)
EmailAddress.objects.create(person_or_org=author, type="I-D", priority=draft.pk, address="author@example.com")
# test query
documents = list(get_soon_to_expire_ids(14))
self.assertEquals(len(documents), 1)
# test send warning
mailbox_before = len(mail_outbox)
send_expire_warning_for_id(documents[0])
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("author@example.com" in str(mail_outbox[-1]))
def test_expire_ids(self):
from ietf.idrfc.expire import get_expired_ids, send_expire_notice_for_id, expire_id
@ -1277,3 +1305,5 @@ class MirrorScriptTestCases(unittest.TestCase,RealDatabaseTest):
self.assertEquals(len(refs), 3)
print "OK"
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from testsREDESIGN import *

1403
ietf/idrfc/testsREDESIGN.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
200 /
200 /doc/
200,heavy /doc/all/
200,heavy /doc/active/
# draft that's now RFC
200 /doc/draft-ietf-avt-rtp-atrac-family/
200 /doc/draft-ietf-avt-rtp-atrac-family/doc.json
200 /doc/draft-ietf-avt-rtp-atrac-family/ballot.json
200 /doc/draft-ietf-avt-rtp-atrac-family/_ballot.data
# replaced draft, never went to IESG
200 /doc/draft-eronen-mobike-mopo/
404 /doc/draft-eronen-mobike-mopo/ballot.json
404 /doc/draft-eronen-mobike-mopo/_ballot.data
# expired draft
200 /doc/draft-eronen-eap-sim-aka-80211/
# Normal RFC
200 /doc/rfc4739/
200 /doc/rfc4739/doc.json
200 /doc/rfc4739/ballot.json # has ballot from I-D
200 /doc/rfc4739/_ballot.data
# RFC that's evaluated in IESG
200 /doc/rfc3852/
200 /doc/rfc3852/doc.json
200 /doc/rfc3852/ballot.json
200 /doc/rfc3852/_ballot.data
# old RFC
200 /doc/rfc822/
200 /doc/rfc822/doc.json
# ballot sets
200 /doc/rfc3550/ballot.json
200 /doc/rfc3550/_ballot.data
200 /doc/rfc3551/ballot.json
200 /doc/rfc3551/_ballot.data
200 /doc/draft-irtf-dtnrg-ltp/ballot.json
200 /doc/draft-irtf-dtnrg-ltp/_ballot.data
# file formats
200 /doc/rfc9/ # PDF only
200 /doc/rfc2490/ # TXT+PDF+PS
200 /doc/rfc500/ # not online
404 /doc/draft-no-such-draft/
404 /doc/rfc4637/
200 /doc/rfc2444/doc.json # foreignkey problem with Django 1.x
# current AD -- needs to be updated at some point
200 /doc/ad/robert.sparks/
# former AD
200 /doc/ad/sam.hartman/
404 /doc/ad/no.body/
# ballot exists, but it's not issued
404 /doc/draft-ietf-aaa-diameter-api/ballot.json
404 /doc/draft-ietf-aaa-diameter-api/_ballot.data
# ballot does not exist
404 /doc/draft-zeilenga-cldap/ballot.json
404 /doc/draft-zeilenga-cldap/_ballot.data
# comment with created_by=999
200 /doc/draft-ietf-l3vpn-2547bis-mcast-bgp/
# comment with created_by=0 (and no idinternal entry)
200 /doc/draft-ietf-proto-wgdocument-states/
200 /doc/search/
200 /doc/search/?rfcs=on&name=snmp
200 /doc/search/?rfcs=on&name=nfs&by=ad&ad=104942
200 /doc/search/?activeDrafts=on&name=sipping
200 /doc/search/?oldDrafts=on&name=tls
200 /doc/search/?activeDrafts=on&oldDrafts=on&ad=104942&by=ad
200 /doc/search/?activeDrafts=on&state=iesg-eva&by=state
200 /doc/search/?activeDrafts=on&oldDrafts=on&subState=need-rev&by=state
200 /doc/search/?activeDrafts=on&oldDrafts=on&rfcs=on&ad=104942&name=nfs&by=ad
200 /doc/search/?rfcs=on&group=tls&by=group
200 /doc/search/?activeDrafts=on&group=tls&by=group
200 /doc/search/?activeDrafts=on&oldDrafts=on&rfcs=on&author=eronen&by=author
200 /doc/search/?activeDrafts=on&oldDrafts=on&rfcs=on&area=934&name=ldap&by=area
200 /doc/search/?activeDrafts=on&name=asdfsadfsdfasdf
200 /doc/search/?activeDrafts=on&name=%EF%BD%8C #non-ASCII
# Test case for missing publication date
200 /doc/search/?oldDrafts=on&name=ppvpn

View file

@ -1,3 +1,5 @@
from django.conf import settings
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
from ietf.idrfc.mails import *
@ -60,6 +62,31 @@ def log_state_changed(request, doc, by, email_watch_list=True, note=''):
return change
def log_state_changedREDESIGN(request, doc, by, prev_iesg_state, note=''):
from doc.models import DocEvent
e = DocEvent(doc=doc, by=by)
e.type = "changed_document"
e.desc = u"State changed to <b>%s</b> from %s" % (
doc.iesg_state.name,
prev_iesg_state.name if prev_iesg_state else "None")
if note:
e.desc += "<br>%s" % note
if doc.iesg_state_id == "lc":
writeup = doc.latest_event(WriteupDocEvent, type="changed_last_call_text")
if writeup:
e.desc += "<br><br><b>The following Last Call Announcement was sent out:</b><br><br>"
e.desc += writeup.text.replace("\n", "<br><br>")
e.save()
return e
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
log_state_changed = log_state_changedREDESIGN
def update_telechat(request, idinternal, new_telechat_date, new_returning_item=None):
on_agenda = bool(new_telechat_date)
@ -95,3 +122,58 @@ def update_telechat(request, idinternal, new_telechat_date, new_returning_item=N
(new_telechat_date,
idinternal.telechat_date))
idinternal.telechat_date = new_telechat_date
def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_item=None):
from doc.models import TelechatDocEvent
on_agenda = bool(new_telechat_date)
prev = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
prev_returning = bool(prev and prev.returning_item)
prev_telechat = prev.telechat_date if prev else None
prev_agenda = bool(prev_telechat)
returning_item_changed = bool(new_returning_item != None and new_returning_item != prev_returning)
if new_returning_item == None:
returning = prev_returning
else:
returning = new_returning_item
if returning == prev_returning and new_telechat_date == prev_telechat:
# fully updated, nothing to do
return
# auto-update returning item
if (not returning_item_changed and on_agenda and prev_agenda
and new_telechat_date != prev_telechat):
returning = True
e = TelechatDocEvent()
e.type = "scheduled_for_telechat"
e.by = by
e.doc = doc
e.returning_item = returning
e.telechat_date = new_telechat_date
if on_agenda != prev_agenda:
if on_agenda:
e.desc = "Placed on agenda for telechat - %s by %s" % (
new_telechat_date, by.name)
else:
e.desc = "Removed from agenda for telechat by %s" % by.name
elif on_agenda and new_telechat_date != prev_telechat:
e.desc = "Telechat date has been changed to <b>%s</b> from <b>%s</b> by %s" % (
new_telechat_date, prev_telechat, by.name)
else:
# we didn't reschedule but flipped returning item bit - let's
# just explain that
if returning:
e.desc = "Added as returning item on telechat by %s" % by.name
else:
e.desc = "Removed as returning item on telechat by %s" % by.name
e.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
update_telechat = update_telechatREDESIGN

View file

@ -10,11 +10,12 @@ from django.template.loader import render_to_string
from django.template import RequestContext
from django import forms
from django.utils.html import strip_tags
from django.conf import settings
from ietf import settings
from ietf.utils.mail import send_mail_text, send_mail_preformatted
from ietf.ietfauth.decorators import group_required
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ietfauth.decorators import has_role
from ietf.idtracker.models import *
from ietf.iesg.models import *
from ietf.ipr.models import IprDetail
@ -24,6 +25,9 @@ from ietf.idrfc.utils import *
from ietf.idrfc.lastcall import request_last_call
from ietf.idrfc.idrfc_wrapper import BallotWrapper
from doc.models import Document, DocEvent, BallotPositionDocEvent, LastCallDocEvent, save_document_in_history
from name.models import BallotPositionName, IesgDocStateName
BALLOT_CHOICES = (("yes", "Yes"),
("noobj", "No Objection"),
@ -88,14 +92,13 @@ def edit_position(request, name):
if not ad_username:
raise Http404()
ad = get_object_or_404(IESGLogin, login_name=ad_username)
pos, discuss, comment = get_ballot_info(doc.idinternal.ballot, ad)
if request.method == 'POST':
form = EditPositionForm(request.POST)
if form.is_valid():
# save the vote
# save the vote
clean = form.cleaned_data
if clean['return_to_url']:
@ -206,6 +209,155 @@ def edit_position(request, name):
),
context_instance=RequestContext(request))
class EditPositionFormREDESIGN(forms.Form):
position = forms.ModelChoiceField(queryset=BallotPositionName.objects.all(), widget=forms.RadioSelect, initial="norecord", required=True)
discuss = forms.CharField(required=False, widget=forms.Textarea)
comment = forms.CharField(required=False, widget=forms.Textarea)
return_to_url = forms.CharField(required=False, widget=forms.HiddenInput)
def clean_discuss(self):
entered_discuss = self.cleaned_data["discuss"]
entered_pos = self.cleaned_data["position"]
if entered_pos.slug == "discuss" and not entered_discuss:
raise forms.ValidationError("You must enter a non-empty discuss")
return entered_discuss
@group_required('Area_Director','Secretariat')
def edit_positionREDESIGN(request, name):
"""Vote and edit discuss and comment on Internet Draft as Area Director."""
doc = get_object_or_404(Document, docalias__name=name)
started_process = doc.latest_event(type="started_iesg_process")
if not doc.iesg_state or not started_process:
raise Http404()
ad = login = request.user.get_profile()
if 'HTTP_REFERER' in request.META:
return_to_url = request.META['HTTP_REFERER']
else:
return_to_url = doc.get_absolute_url()
# if we're in the Secretariat, we can select an AD to act as stand-in for
if not has_role(request.user, "Area Director"):
ad_id = request.GET.get('ad')
if not ad_id:
raise Http404()
from person.models import Person
ad = get_object_or_404(Person, pk=ad_id)
old_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
if request.method == 'POST':
form = EditPositionForm(request.POST)
if form.is_valid():
# save the vote
clean = form.cleaned_data
if clean['return_to_url']:
return_to_url = clean['return_to_url']
pos = BallotPositionDocEvent(doc=doc, by=login)
pos.type = "changed_ballot_position"
pos.ad = ad
pos.pos = clean["position"]
pos.comment = clean["comment"].strip()
pos.comment_time = old_pos.comment_time if old_pos else None
pos.discuss = clean["discuss"].strip()
pos.discuss_time = old_pos.discuss_time if old_pos else None
changes = []
added_events = []
# possibly add discuss/comment comments to history trail
# so it's easy to see
old_comment = old_pos.comment if old_pos else ""
if pos.comment != old_comment:
pos.comment_time = pos.time
changes.append("comment")
if pos.comment:
e = DocEvent(doc=doc)
e.by = ad # otherwise we can't see who's saying it
e.type = "added_comment"
e.desc = "[Ballot comment]\n" + pos.comment
added_events.append(e)
old_discuss = old_pos.discuss if old_pos else ""
if pos.discuss != old_discuss:
pos.discuss_time = pos.time
changes.append("discuss")
if pos.discuss:
e = DocEvent(doc=doc, by=login)
e.by = ad # otherwise we can't see who's saying it
e.type = "added_comment"
e.desc = "[Ballot discuss]\n" + pos.discuss
added_events.append(e)
# figure out a description
if not old_pos and pos.pos.slug != "norecord":
pos.desc = u"[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.name)
elif old_pos and pos.pos != old_pos.pos:
pos.desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos.ad.name, pos.pos.name, old_pos.pos.name)
if not pos.desc and changes:
pos.desc = u"Ballot %s text updated for %s" % (u" and ".join(changes), ad.name)
# only add new event if we actually got a change
if pos.desc:
if login != ad:
pos.desc += u" by %s" % login.name
pos.save()
for e in added_events:
e.save() # save them after the position is saved to get later id
doc.time = pos.time
doc.save()
if request.POST.get("send_mail"):
qstr = "?return_to_url=%s" % return_to_url
if request.GET.get('ad'):
qstr += "&ad=%s" % request.GET.get('ad')
return HttpResponseRedirect(urlreverse("doc_send_ballot_comment", kwargs=dict(name=doc.name)) + qstr)
elif request.POST.get("Defer"):
return HttpResponseRedirect(urlreverse("doc_defer_ballot", kwargs=dict(name=doc)))
elif request.POST.get("Undefer"):
return HttpResponseRedirect(urlreverse("doc_undefer_ballot", kwargs=dict(name=doc)))
else:
return HttpResponseRedirect(return_to_url)
else:
initial = {}
if old_pos:
initial['position'] = old_pos.pos.slug
initial['discuss'] = old_pos.discuss
initial['comment'] = old_pos.comment
if return_to_url:
initial['return_to_url'] = return_to_url
form = EditPositionForm(initial=initial)
ballot_deferred = None
if doc.iesg_state_id == "defer":
ballot_deferred = doc.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>")
return render_to_response('idrfc/edit_positionREDESIGN.html',
dict(doc=doc,
form=form,
ad=ad,
return_to_url=return_to_url,
old_pos=old_pos,
ballot_deferred=ballot_deferred,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
edit_position = edit_positionREDESIGN
EditPositionForm = EditPositionFormREDESIGN
@group_required('Area_Director','Secretariat')
def send_ballot_comment(request, name):
"""Email Internet Draft ballot discuss/comment for area director."""
@ -278,6 +430,84 @@ def send_ballot_comment(request, name):
),
context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
def send_ballot_commentREDESIGN(request, name):
"""Email Internet Draft ballot discuss/comment for area director."""
doc = get_object_or_404(Document, docalias__name=name)
started_process = doc.latest_event(type="started_iesg_process")
if not started_process:
raise Http404()
ad = login = request.user.get_profile()
return_to_url = request.GET.get('return_to_url')
if not return_to_url:
return_to_url = doc.get_absolute_url()
if 'HTTP_REFERER' in request.META:
back_url = request.META['HTTP_REFERER']
else:
back_url = doc.get_absolute_url()
# if we're in the Secretariat, we can select an AD to act as stand-in for
if not has_role(request.user, "Area Director"):
ad_id = request.GET.get('ad')
if not ad_id:
raise Http404()
from person.models import Person
ad = get_object_or_404(Person, pk=ad_id)
pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
if not pos:
raise Http404()
subj = []
d = ""
if pos.pos == "discuss" and pos.discuss:
d = pos.discuss
subj.append("DISCUSS")
c = ""
if pos.comment:
c = pos.comment
subj.append("COMMENT")
ad_name = ad.get_name()
ad_name_genitive = ad_name + "'" if ad_name.endswith('s') else ad_name + "'s"
subject = "%s %s on %s" % (ad_name_genitive, pos.pos.name if pos.pos else "No Position", "%s-%02d" % (doc.name, doc.rev))
if subj:
subject += ": (with %s)" % " and ".join(subj)
doc.filename = doc.name # compatibility attributes
doc.revision_display = doc.rev
body = render_to_string("idrfc/ballot_comment_mail.txt",
dict(discuss=d, comment=c, ad=ad.get_name(), doc=doc, pos=pos.pos))
frm = ad.formatted_email()
to = "The IESG <iesg@ietf.org>"
if request.method == 'POST':
cc = [x.strip() for x in request.POST.get("cc", "").split(',') if x.strip()]
if request.POST.get("cc_state_change") and doc.notify:
cc.extend(doc.notify.split(','))
send_mail_text(request, to, frm, subject, body, cc=", ".join(cc))
return HttpResponseRedirect(return_to_url)
return render_to_response('idrfc/send_ballot_commentREDESIGN.html',
dict(doc=doc,
subject=subject,
body=body,
frm=frm,
to=to,
ad=ad,
can_send=d or c,
back_url=back_url,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
send_ballot_comment = send_ballot_commentREDESIGN
@group_required('Area_Director','Secretariat')
def defer_ballot(request, name):
@ -309,9 +539,46 @@ def defer_ballot(request, name):
return render_to_response('idrfc/defer_ballot.html',
dict(doc=doc,
telechat_date=telechat_date),
telechat_date=telechat_date,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
def defer_ballotREDESIGN(request, name):
"""Signal post-pone of Internet Draft ballot, notifying relevant parties."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
telechat_date = TelechatDates.objects.all()[0].date2
if request.method == 'POST':
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = IesgDocStateName.objects.get(slug='defer')
e = log_state_changed(request, doc, login, prev)
doc.time = e.time
doc.save()
email_state_changed(request, doc, e.desc)
update_telechat(request, doc, login, telechat_date)
email_ballot_deferred(request, doc, login.name, telechat_date)
return HttpResponseRedirect(doc.get_absolute_url())
return render_to_response('idrfc/defer_ballot.html',
dict(doc=doc,
telechat_date=telechat_date,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
defer_ballot = defer_ballotREDESIGN
@group_required('Area_Director','Secretariat')
def undefer_ballot(request, name):
"""Delete deferral of Internet Draft ballot."""
@ -336,9 +603,45 @@ def undefer_ballot(request, name):
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
return render_to_response('idrfc/undefer_ballot.html',
dict(doc=doc,telechat_date=telechat_date),
dict(doc=doc,
telechat_date=telechat_date,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
def undefer_ballotREDESIGN(request, name):
"""Delete deferral of Internet Draft ballot."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
telechat_date = TelechatDates.objects.all()[0].date1
if request.method == 'POST':
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = IesgDocStateName.objects.get(slug='iesg-eva')
e = log_state_changed(request, doc, login, prev)
doc.time = e.time
doc.save()
email_state_changed(request, doc, e.desc)
return HttpResponseRedirect(doc.get_absolute_url())
return render_to_response('idrfc/undefer_ballot.html',
dict(doc=doc,
telechat_date=telechat_date,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
undefer_ballot = undefer_ballotREDESIGN
class LastCallTextForm(forms.ModelForm):
def clean_last_call_text(self):
lines = self.cleaned_data["last_call_text"].split("\r\n")
@ -424,6 +727,7 @@ def lastcalltext(request, name):
return render_to_response('idrfc/ballot_lastcalltext.html',
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url(),
ballot=ballot,
last_call_form=last_call_form,
can_request_last_call=can_request_last_call,
@ -432,6 +736,101 @@ def lastcalltext(request, name):
),
context_instance=RequestContext(request))
class LastCallTextFormREDESIGN(forms.Form):
last_call_text = forms.CharField(widget=forms.Textarea, required=True)
def clean_last_call_text(self):
lines = self.cleaned_data["last_call_text"].split("\r\n")
for l, next in zip(lines, lines[1:]):
if l.startswith('Subject:') and next.strip():
raise forms.ValidationError("Subject line appears to have a line break, please make sure there is no line breaks in the subject line and that it is followed by an empty line.")
return self.cleaned_data["last_call_text"].replace("\r", "")
@group_required('Area_Director','Secretariat')
def lastcalltextREDESIGN(request, name):
"""Editing of the last call text"""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
existing = doc.latest_event(WriteupDocEvent, type="changed_last_call_text")
if not existing:
existing = generate_last_call_announcement(request, doc)
form = LastCallTextForm(initial=dict(last_call_text=existing.text))
if request.method == 'POST':
if "save_last_call_text" in request.POST or "send_last_call_request" in request.POST:
form = LastCallTextForm(request.POST)
if form.is_valid():
t = form.cleaned_data['last_call_text']
if t != existing.text:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_last_call_text"
e.desc = "Last call announcement was changed"
e.text = t
e.save()
doc.time = e.time
doc.save()
if "send_last_call_request" in request.POST:
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = IesgDocStateName.objects.get(slug='lc-req')
e = log_state_changed(request, doc, login, prev)
doc.time = e.time
doc.save()
email_state_changed(request, doc, e.desc)
email_owner(request, doc, doc.ad, login, e.desc)
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
dict(doc=doc),
context_instance=RequestContext(request))
if "regenerate_last_call_text" in request.POST:
e = generate_last_call_announcement(request, doc)
doc.time = e.time
doc.save()
# make sure form has the updated text
form = LastCallTextForm(initial=dict(last_call_text=e.text))
can_request_last_call = doc.iesg_state.order < 27
can_make_last_call = doc.iesg_state.order < 20
can_announce = doc.iesg_state.order > 19
need_intended_status = ""
if not doc.intended_std_level:
need_intended_status = doc.file_tag()
return render_to_response('idrfc/ballot_lastcalltext.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
last_call_form=form,
can_request_last_call=can_request_last_call,
can_make_last_call=can_make_last_call,
need_intended_status=need_intended_status,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
LastCallTextForm = LastCallTextFormREDESIGN
lastcalltext = lastcalltextREDESIGN
@group_required('Area_Director','Secretariat')
def ballot_writeupnotes(request, name):
"""Editing of ballot write-up and notes"""
@ -489,7 +888,8 @@ def ballot_writeupnotes(request, name):
doc.idinternal.save()
return render_to_response('idrfc/ballot_issued.html',
dict(doc=doc),
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@ -507,6 +907,94 @@ def ballot_writeupnotes(request, name):
),
context_instance=RequestContext(request))
class BallotWriteupFormREDESIGN(forms.Form):
ballot_writeup = forms.CharField(widget=forms.Textarea, required=True)
def clean_ballot_writeup(self):
return self.cleaned_data["ballot_writeup"].replace("\r", "")
@group_required('Area_Director','Secretariat')
def ballot_writeupnotesREDESIGN(request, name):
"""Editing of ballot write-up and notes"""
doc = get_object_or_404(Document, docalias__name=name)
started_process = doc.latest_event(type="started_iesg_process")
if not started_process:
raise Http404()
login = request.user.get_profile()
approval = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
existing = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
if not existing:
existing = generate_ballot_writeup(request, doc)
form = BallotWriteupForm(initial=dict(ballot_writeup=existing.text))
if request.method == 'POST' and "save_ballot_writeup" in request.POST or "issue_ballot" in request.POST:
form = BallotWriteupForm(request.POST)
if form.is_valid():
t = form.cleaned_data["ballot_writeup"]
if t != existing.text:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_ballot_writeup_text"
e.desc = "Ballot writeup was changed"
e.text = t
e.save()
doc.time = e.time
doc.save()
if "issue_ballot" in request.POST and approval:
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, time__gte=started_process.time):
# sending the ballot counts as a yes
pos = BallotPositionDocEvent(doc=doc, by=login)
pos.type = "changed_ballot_position"
pos.ad = login
pos.pos_id = "yes"
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.name)
pos.save()
msg = generate_issue_ballot_mail(request, doc)
send_mail_preformatted(request, msg)
email_iana(request, doc, 'drafts-eval@icann.org', msg)
e = DocEvent(doc=doc, by=login)
e.by = login
e.type = "sent_ballot_announcement"
e.desc = "Ballot has been issued by %s" % login.name
e.save()
doc.time = e.time
doc.save()
return render_to_response('idrfc/ballot_issued.html',
dict(doc=doc,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
need_intended_status = ""
if not doc.intended_std_level:
need_intended_status = doc.file_tag()
return render_to_response('idrfc/ballot_writeupnotesREDESIGN.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
ballot_issued=bool(doc.latest_event(type="sent_ballot_announcement")),
ballot_writeup_form=form,
need_intended_status=need_intended_status,
approval=approval,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
BallotWriteupForm = BallotWriteupFormREDESIGN
ballot_writeupnotes = ballot_writeupnotesREDESIGN
@group_required('Area_Director','Secretariat')
def ballot_approvaltext(request, name):
"""Editing of approval text"""
@ -549,6 +1037,7 @@ def ballot_approvaltext(request, name):
return render_to_response('idrfc/ballot_approvaltext.html',
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url(),
ballot=ballot,
approval_text_form=approval_text_form,
can_announce=can_announce,
@ -556,6 +1045,69 @@ def ballot_approvaltext(request, name):
),
context_instance=RequestContext(request))
class ApprovalTextFormREDESIGN(forms.Form):
approval_text = forms.CharField(widget=forms.Textarea, required=True)
def clean_approval_text(self):
return self.cleaned_data["approval_text"].replace("\r", "")
@group_required('Area_Director','Secretariat')
def ballot_approvaltextREDESIGN(request, name):
"""Editing of approval text"""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
existing = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
if not existing:
existing = generate_approval_mail(request, doc)
form = ApprovalTextForm(initial=dict(approval_text=existing.text))
if request.method == 'POST':
if "save_approval_text" in request.POST:
form = ApprovalTextForm(request.POST)
if form.is_valid():
t = form.cleaned_data['approval_text']
if t != existing.text:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_ballot_approval_text"
e.desc = "Ballot approval text was changed"
e.text = t
e.save()
doc.time = e.time
doc.save()
if "regenerate_approval_text" in request.POST:
e = generate_approval_mail(request, doc)
doc.time = e.time
doc.save()
# make sure form has the updated text
form = ApprovalTextForm(initial=dict(approval_text=existing.text))
can_announce = doc.iesg_state.order > 19
need_intended_status = ""
if not doc.intended_std_level:
need_intended_status = doc.file_tag()
return render_to_response('idrfc/ballot_approvaltext.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
approval_text_form=form,
can_announce=can_announce,
need_intended_status=need_intended_status,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
ApprovalTextForm = ApprovalTextFormREDESIGN
ballot_approvaltext = ballot_approvaltextREDESIGN
@group_required('Secretariat')
def approve_ballot(request, name):
@ -628,6 +1180,83 @@ def approve_ballot(request, name):
announcement=announcement),
context_instance=RequestContext(request))
@group_required('Secretariat')
def approve_ballotREDESIGN(request, name):
"""Approve ballot, sending out announcement, changing state."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
if not e:
e = generate_approval_mail(request, doc)
approval_text = e.text
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
if not e:
e = generate_ballot_writeup(request, doc)
ballot_writeup = e.text
if "NOT be published" in approval_text:
action = "do_not_publish"
elif "To: RFC Editor" in approval_text:
action = "to_rfc_editor"
else:
action = "to_announcement_list"
announcement = approval_text + "\n\n" + ballot_writeup
if request.method == 'POST':
if action == "do_not_publish":
new_state = IesgDocStateName.objects.get(slug="dead")
else:
new_state = IesgDocStateName.objects.get(slug="ann")
# fixup document
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = new_state
e = DocEvent(doc=doc, by=login)
if action == "do_not_publish":
e.type = "iesg_disapproved"
e.desc = "Do Not Publish note has been sent to RFC Editor"
else:
e.type = "iesg_approved"
e.desc = "IESG has approved the document"
e.save()
change_description = e.desc + " and state has been changed to %s" % doc.iesg_state.name
e = log_state_changed(request, doc, login, prev)
doc.time = e.time
doc.save()
email_state_changed(request, doc, change_description)
email_owner(request, doc, doc.ad, login, change_description)
# send announcement
send_mail_preformatted(request, announcement)
if action == "to_announcement_list":
email_iana(request, doc, "drafts-approval@icann.org", announcement)
return HttpResponseRedirect(doc.get_absolute_url())
return render_to_response('idrfc/approve_ballot.html',
dict(doc=doc,
action=action,
announcement=announcement),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
approve_ballot = approve_ballotREDESIGN
class MakeLastCallForm(forms.Form):
last_call_sent_date = forms.DateField(required=True)
@ -684,3 +1313,65 @@ def make_last_call(request, name):
form=form),
context_instance=RequestContext(request))
@group_required('Secretariat')
def make_last_callREDESIGN(request, name):
"""Make last call for Internet Draft, sending out announcement."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
e = doc.latest_event(WriteupDocEvent, type="changed_last_call_text")
if not e:
e = generate_last_call_announcement(request, doc)
announcement = e.text
if request.method == 'POST':
form = MakeLastCallForm(request.POST)
if form.is_valid():
send_mail_preformatted(request, announcement)
email_iana(request, doc, "drafts-lastcall@icann.org", announcement)
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = IesgDocStateName.objects.get(slug='lc')
e = log_state_changed(request, doc, login, prev)
doc.time = e.time
doc.save()
change_description = "Last call has been made for %s and state has been changed to %s" % (doc.name, doc.iesg_state.name)
email_state_changed(request, doc, change_description)
email_owner(request, doc, doc.ad, login, change_description)
e = LastCallDocEvent(doc=doc, by=login)
e.type = "sent_last_call"
e.desc = "Last call sent by %s" % login.name
if form.cleaned_data['last_call_sent_date'] != e.time.date():
e.time = datetime.datetime.combine(form.cleaned_data['last_call_sent_date'], e.time.time())
e.expires = form.cleaned_data['last_call_expiration_date']
e.save()
return HttpResponseRedirect(doc.get_absolute_url())
else:
initial = {}
initial["last_call_sent_date"] = date.today()
expire_days = 14
if doc.group.type_id == "individ":
expire_days = 28
initial["last_call_expiration_date"] = date.today() + timedelta(days=expire_days)
form = MakeLastCallForm(initial=initial)
return render_to_response('idrfc/make_last_callREDESIGN.html',
dict(doc=doc,
form=form),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
make_last_call = make_last_callREDESIGN

View file

@ -41,9 +41,9 @@ from django.template.defaultfilters import truncatewords_html
from django.utils import simplejson as json
from django.utils.decorators import decorator_from_middleware
from django.middleware.gzip import GZipMiddleware
from django.core.urlresolvers import reverse, NoReverseMatch
from django.core.urlresolvers import reverse as urlreverse, NoReverseMatch
from django.conf import settings
from ietf import settings
from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, DocumentComment
from ietf.idtracker.templatetags.ietf_filters import format_textarea, fill
from ietf.idrfc import markup_txt
@ -85,6 +85,7 @@ def include_text(request):
def document_main_rfc(request, rfc_number, tab):
rfci = get_object_or_404(RfcIndex, rfc_number=rfc_number)
rfci.viewing_as_rfc = True
doc = RfcWrapper(rfci)
info = {}
@ -122,7 +123,7 @@ def document_main(request, name, tab):
return document_main_rfc(request, int(m.group(1)), tab)
id = get_object_or_404(InternetDraft, filename=name)
doc = IdWrapper(id)
info = {}
info['has_pdf'] = (".pdf" in doc.file_types())
info['is_rfc'] = False
@ -148,21 +149,59 @@ def document_main(request, name, tab):
# doc is either IdWrapper or RfcWrapper
def _get_history(doc, versions):
results = []
if doc.is_id_wrapper:
comments = DocumentComment.objects.filter(document=doc.tracker_id).exclude(rfc_flag=1)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
versions = [] # clear versions
event_holder = doc._draft if hasattr(doc, "_draft") else doc._rfcindex
for e in event_holder.docevent_set.all().select_related('by').order_by('-time', 'id'):
info = {}
if e.type == "new_revision":
filename = u"%s-%s" % (e.doc.name, e.newrevisiondocevent.rev)
e.desc = 'New version available: <a href="http://tools.ietf.org/id/%s.txt">%s</a>' % (filename, filename)
if int(e.newrevisiondocevent.rev) != 0:
e.desc += ' (<a href="http://tools.ietf.org/rfcdiff?url2=%s">diff from -%02d</a>)' % (filename, int(e.newrevisiondocevent.rev) - 1)
info["dontmolest"] = True
multiset_ballot_text = "This was part of a ballot set with: "
if e.desc.startswith(multiset_ballot_text):
names = e.desc[len(multiset_ballot_text):].split(", ")
e.desc = multiset_ballot_text + ", ".join(u'<a href="%s">%s</a>' % (urlreverse("doc_view", kwargs={'name': n }), n) for n in names)
info["dontmolest"] = True
info['text'] = e.desc
info['by'] = e.by.name
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
info['snipped'] = info['textSnippet'][-3:] == "..." and e.type != "new_revision"
results.append({'comment':e, 'info':info, 'date':e.time, 'is_com':True})
prev_rev = "00"
# actually, we're already sorted and this ruins the sort from
# the ids which is sometimes needed, so the function should be
# rewritten to not rely on a resort
results.sort(key=lambda x: x['date'])
for o in results:
e = o["comment"]
if e.type == "new_revision":
e.version = e.newrevisiondocevent.rev
else:
e.version = prev_rev
prev_rev = e.version
else:
comments = DocumentComment.objects.filter(document=doc.rfc_number,rfc_flag=1)
if len(comments) > 0:
# also include rfc_flag=NULL, but only if at least one
# comment with rfc_flag=1 exists (usually NULL means same as 0)
comments = DocumentComment.objects.filter(document=doc.rfc_number).exclude(rfc_flag=0)
for comment in comments.order_by('-date','-time','-id').filter(public_flag=1).select_related('created_by'):
info = {}
info['text'] = comment.comment_text
info['by'] = comment.get_fullname()
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
info['snipped'] = info['textSnippet'][-3:] == "..."
results.append({'comment':comment, 'info':info, 'date':comment.datetime(), 'is_com':True})
if doc.is_id_wrapper:
comments = DocumentComment.objects.filter(document=doc.tracker_id).exclude(rfc_flag=1)
else:
comments = DocumentComment.objects.filter(document=doc.rfc_number,rfc_flag=1)
if len(comments) > 0:
# also include rfc_flag=NULL, but only if at least one
# comment with rfc_flag=1 exists (usually NULL means same as 0)
comments = DocumentComment.objects.filter(document=doc.rfc_number).exclude(rfc_flag=0)
for comment in comments.order_by('-date','-time','-id').filter(public_flag=1).select_related('created_by'):
info = {}
info['text'] = comment.comment_text
info['by'] = comment.get_fullname()
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
info['snipped'] = info['textSnippet'][-3:] == "..."
results.append({'comment':comment, 'info':info, 'date':comment.datetime(), 'is_com':True})
if doc.is_id_wrapper and versions:
for v in versions:
if v['draft_name'] == doc.draft_name:
@ -171,11 +210,11 @@ def _get_history(doc, versions):
results.insert(0, v)
if doc.is_id_wrapper and doc.draft_status == "Expired" and doc._draft.expiration_date:
results.append({'is_text':True, 'date':doc._draft.expiration_date, 'text':'Draft expired'})
if doc.is_rfc_wrapper:
if not settings.USE_DB_REDESIGN_PROXY_CLASSES and doc.is_rfc_wrapper:
text = 'RFC Published'
if doc.draft_name:
try:
text = 'RFC Published (see <a href="%s">%s</a> for earlier history)' % (reverse('doc_view', args=[doc.draft_name]),doc.draft_name)
text = 'RFC Published (see <a href="%s">%s</a> for earlier history)' % (urlreverse('doc_view', args=[doc.draft_name]),doc.draft_name)
except NoReverseMatch:
pass
results.append({'is_text':True, 'date':doc.publication_date, 'text':text})
@ -210,6 +249,19 @@ def _get_versions(draft, include_replaced=True):
def get_ballot(name):
r = re.compile("^rfc([1-9][0-9]*)$")
m = r.match(name)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import DocAlias
alias = get_object_or_404(DocAlias, name=name)
d = get_object_or_404(InternetDraft, name=alias.document.name)
try:
if not d.ballot.ballot_issued:
raise Http404
except BallotInfo.DoesNotExist:
raise Http404
return (BallotWrapper(d), RfcWrapper(d) if m else IdWrapper(d))
if m:
rfc_number = int(m.group(1))
rfci = get_object_or_404(RfcIndex, rfc_number=rfc_number)

View file

@ -11,17 +11,21 @@ from django.template import RequestContext
from django import forms
from django.utils.html import strip_tags
from django.db.models import Max
from django.conf import settings
from ietf import settings
from ietf.utils.mail import send_mail_text
from ietf.ietfauth.decorators import group_required
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ietfauth.decorators import has_role
from ietf.idtracker.models import *
from ietf.iesg.models import *
from ietf.idrfc.mails import *
from ietf.idrfc.utils import *
from ietf.idrfc.lastcall import request_last_call
from doc.models import Document, DocEvent, StatusDateDocEvent, TelechatDocEvent, save_document_in_history, DocHistory
from name.models import IesgDocStateName, IntendedStdLevelName, DocInfoTagName, get_next_iesg_states, DocStateName
from person.models import Person, Email
class ChangeStateForm(forms.Form):
state = forms.ModelChoiceField(IDState.objects.all(), empty_label=None, required=True)
@ -58,7 +62,8 @@ def change_state(request, name):
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
dict(doc=doc),
dict(doc=doc,
url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
return HttpResponseRedirect(internal.get_absolute_url())
@ -79,6 +84,73 @@ def change_state(request, name):
next_states=next_states),
context_instance=RequestContext(request))
class ChangeStateFormREDESIGN(forms.Form):
state = forms.ModelChoiceField(IesgDocStateName.objects.all(), empty_label=None, required=True)
# FIXME: no tags yet
#substate = forms.ModelChoiceField(IDSubState.objects.all(), required=False)
comment = forms.CharField(widget=forms.Textarea, required=False)
@group_required('Area_Director','Secretariat')
def change_stateREDESIGN(request, name):
"""Change state of Internet Draft, notifying parties as necessary
and logging the change as a comment."""
doc = get_object_or_404(Document, docalias__name=name)
if (not doc.latest_event(type="started_iesg_process")) or doc.state_id == "expired":
raise Http404()
login = request.user.get_profile()
if request.method == 'POST':
form = ChangeStateForm(request.POST)
if form.is_valid():
state = form.cleaned_data['state']
comment = form.cleaned_data['comment']
if state != doc.iesg_state:
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = state
e = log_state_changed(request, doc, login, prev, comment)
doc.time = e.time
doc.save()
email_state_changed(request, doc, e.desc)
email_owner(request, doc, doc.ad, login, e.desc)
if doc.iesg_state_id == "lc-req":
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
dict(doc=doc,
url=doc.get_absolute_url()),
context_instance=RequestContext(request))
return HttpResponseRedirect(doc.get_absolute_url())
else:
form = ChangeStateForm(initial=dict(state=doc.iesg_state_id))
next_states = get_next_iesg_states(doc.iesg_state)
prev_state = None
hists = DocHistory.objects.filter(doc=doc).exclude(iesg_state=doc.iesg_state).order_by("-time")[:1]
if hists:
prev_state = hists[0].iesg_state
return render_to_response('idrfc/change_stateREDESIGN.html',
dict(form=form,
doc=doc,
prev_state=prev_state,
next_states=next_states),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
change_state = change_stateREDESIGN
ChangeStateForm = ChangeStateFormREDESIGN
def dehtmlify_textarea_text(s):
return s.replace("<br>", "\n").replace("<b>", "").replace("</b>", "").replace(" ", " ")
@ -235,7 +307,7 @@ def edit_info(request, name):
setattr(obj, attr, r[attr])
diff(doc, 'intended_status', "Intended Status")
diff(doc.idinternal, 'status_date', "Status Date")
diff(doc.idinternal, 'status_date', "Status date")
if 'area_acronym' in r and r['area_acronym']:
diff(doc.idinternal, 'area_acronym', 'Area acronym')
diff(doc.idinternal, 'job_owner', 'Responsible AD')
@ -302,6 +374,224 @@ def edit_info(request, name):
login=login),
context_instance=RequestContext(request))
class EditInfoFormREDESIGN(forms.Form):
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.all(), empty_label=None, required=True)
status_date = forms.DateField(required=False, help_text="Format is YYYY-MM-DD")
via_rfc_editor = forms.BooleanField(required=False, label="Via IRTF or RFC Editor")
ad = forms.ModelChoiceField(Person.objects.filter(email__role__name__in=("ad", "ex-ad")).order_by('email__role__name', 'name'), label="Responsible AD", empty_label=None, required=True)
notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False)
note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False)
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False)
returning_item = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
old_ads = kwargs.pop('old_ads')
super(self.__class__, self).__init__(*args, **kwargs)
# fix up ad field
choices = self.fields['ad'].choices
ex_ads = dict((e.pk, e) for e in Person.objects.filter(email__role__name="ex-ad").distinct())
if old_ads:
# separate active ADs from inactive
for i, t in enumerate(choices):
if t[0] in ex_ads:
choices.insert(i, ("", "----------------"))
break
else:
# remove old ones
self.fields['ad'].choices = [t for t in choices if t[0] not in ex_ads]
# telechat choices
dates = TelechatDates.objects.all()[0].dates()
init = kwargs['initial']['telechat_date']
if init and init not in dates:
dates.insert(0, init)
choices = [("", "(not on agenda)")]
for d in dates:
choices.append((d, d.strftime("%Y-%m-%d")))
self.fields['telechat_date'].choices = choices
# returning item is rendered non-standard
self.standard_fields = [x for x in self.visible_fields() if x.name not in ('returning_item',)]
def clean_status_date(self):
d = self.cleaned_data['status_date']
if d:
if d < date.today():
raise forms.ValidationError("Date must not be in the past.")
if d >= date.today() + timedelta(days=365 * 2):
raise forms.ValidationError("Date must be within two years.")
return d
def clean_note(self):
# note is stored munged in the database
return self.cleaned_data['note'].replace('\n', '<br>').replace('\r', '').replace(' ', '&nbsp; ')
def get_initial_notify(doc):
# set change state notice to something sensible
receivers = []
if doc.group.type_id == "individ":
for a in doc.authors.all():
receivers.append(e.address)
else:
receivers.append("%s-chairs@%s" % (doc.group.acronym, settings.TOOLS_SERVER))
for editor in Email.objects.filter(role__name="editor", role__group=doc.group):
receivers.append(e.address)
receivers.append("%s@%s" % (doc.name, settings.TOOLS_SERVER))
return ", ".join(receivers)
@group_required('Area_Director','Secretariat')
def edit_infoREDESIGN(request, name):
"""Edit various Internet Draft attributes, notifying parties as
necessary and logging changes as document events."""
doc = get_object_or_404(Document, docalias__name=name)
if doc.state_id == "expired":
raise Http404()
login = request.user.get_profile()
new_document = False
if not doc.iesg_state: # FIXME: should probably get this as argument to view
new_document = True
doc.iesg_state = IesgDocStateName.objects.get(slug="pub-req")
doc.notify = get_initial_notify(doc)
e = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
initial_telechat_date = e.telechat_date if e else None
initial_returning_item = bool(e and e.returning_item)
if request.method == 'POST':
form = EditInfoForm(request.POST,
old_ads=False,
initial=dict(telechat_date=initial_telechat_date))
if form.is_valid():
save_document_in_history(doc)
r = form.cleaned_data
if new_document:
# fix so Django doesn't barf in the diff below because these
# fields can't be NULL
doc.ad = r['ad']
replaces = Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces")
if replaces:
# this should perhaps be somewhere else, e.g. the
# place where the replace relationship is established?
e = DocEvent()
e.type = "added_comment"
e.by = Person.objects.get(name="(System)")
e.doc = doc
e.desc = "Earlier history may be found in the Comment Log for <a href=\"%s\">%s</a>" % (replaces[0], replaces[0].get_absolute_url())
e.save()
e = DocEvent()
e.type = "started_iesg_process"
e.by = login
e.doc = doc
e.desc = "IESG process started in state <b>%s</b>" % doc.iesg_state.name
e.save()
orig_ad = doc.ad
changes = []
def desc(attr, new, old):
entry = "%(attr)s has been changed to <b>%(new)s</b> from <b>%(old)s</b>"
if new_document:
entry = "%(attr)s has been changed to <b>%(new)s</b>"
return entry % dict(attr=attr, new=new, old=old)
def diff(attr, name):
v = getattr(doc, attr)
if r[attr] != v:
changes.append(desc(name, r[attr], v))
setattr(doc, attr, r[attr])
# update the attributes, keeping track of what we're doing
diff('intended_std_level', "Intended Status")
diff('ad', "Responsible AD")
diff('notify', "State Change Notice email list")
if r['note'] != doc.note:
if not r['note']:
if doc.note:
changes.append("Note field has been cleared")
else:
if doc.note:
changes.append("Note changed to '%s'" % r['note'])
else:
changes.append("Note added '%s'" % r['note'])
doc.note = r['note']
for c in changes:
e = DocEvent(doc=doc, by=login)
e.type = "changed_document"
e.save()
update_telechat(request, doc, login,
r['telechat_date'], r['returning_item'])
e = doc.latest_event(StatusDateDocEvent, type="changed_status_date")
status_date = e.date if e else None
if r["status_date"] != status_date:
e = StatusDateDocEvent(doc=doc, by=login)
e.type ="changed_status_date"
d = desc("Status date", r["status_date"], status_date)
changes.append(d)
e.date = r["status_date"]
e.save()
if has_role(request.user, 'Secretariat'):
via_rfc = DocInfoTagName.objects.get(slug="via-rfc")
if r['via_rfc_editor']:
doc.tags.add(via_rfc)
else:
doc.tags.remove(via_rfc)
doc.time = datetime.datetime.now()
if changes and not new_document:
email_owner(request, doc, orig_ad, login, "\n".join(changes))
doc.save()
return HttpResponseRedirect(doc.get_absolute_url())
else:
e = doc.latest_event(StatusDateDocEvent)
status = e.date if e else None
init = dict(intended_std_level=doc.intended_std_level,
status_date=status,
ad=doc.ad,
notify=doc.notify,
note=dehtmlify_textarea_text(doc.note),
telechat_date=initial_telechat_date,
returning_item=initial_returning_item,
)
form = EditInfoForm(old_ads=False, initial=init)
if not has_role(request.user, 'Secretariat'):
# filter out Via RFC Editor
form.standard_fields = [x for x in form.standard_fields if x.name != "via_rfc_editor"]
return render_to_response('idrfc/edit_infoREDESIGN.html',
dict(doc=doc,
form=form,
user=request.user,
login=login),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
EditInfoForm = EditInfoFormREDESIGN
edit_info = edit_infoREDESIGN
@group_required('Area_Director','Secretariat')
def request_resurrect(request, name):
@ -323,9 +613,37 @@ def request_resurrect(request, name):
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
return render_to_response('idrfc/request_resurrect.html',
dict(doc=doc),
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
def request_resurrectREDESIGN(request, name):
"""Request resurrect of expired Internet Draft."""
doc = get_object_or_404(Document, docalias__name=name)
if doc.state_id != "expired":
raise Http404()
login = request.user.get_profile()
if request.method == 'POST':
email_resurrect_requested(request, doc, login)
e = DocEvent(doc=doc, by=login)
e.type = "requested_resurrect"
e.desc = "Resurrection was requested"
e.save()
return HttpResponseRedirect(doc.get_absolute_url())
return render_to_response('idrfc/request_resurrect.html',
dict(doc=doc,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
request_resurrect = request_resurrectREDESIGN
@group_required('Secretariat')
def resurrect(request, name):
"""Resurrect expired Internet Draft."""
@ -350,9 +668,45 @@ def resurrect(request, name):
return HttpResponseRedirect(doc.idinternal.get_absolute_url())
return render_to_response('idrfc/resurrect.html',
dict(doc=doc),
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@group_required('Secretariat')
def resurrectREDESIGN(request, name):
"""Resurrect expired Internet Draft."""
doc = get_object_or_404(Document, docalias__name=name)
if doc.state_id != "expired":
raise Http404()
login = request.user.get_profile()
if request.method == 'POST':
save_document_in_history(doc)
e = doc.latest_event(type__in=('requested_resurrect', "completed_resurrect"))
if e and e.type == 'requested_resurrect':
email_resurrection_completed(request, doc, requester=e.by)
e = DocEvent(doc=doc, by=login)
e.type = "completed_resurrect"
e.desc = "Resurrection was completed"
e.save()
doc.state = DocStateName.objects.get(slug="active")
doc.time = datetime.datetime.now()
doc.save()
return HttpResponseRedirect(doc.get_absolute_url())
return render_to_response('idrfc/resurrect.html',
dict(doc=doc,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
resurrect = resurrectREDESIGN
class AddCommentForm(forms.Form):
comment = forms.CharField(required=True, widget=forms.Textarea)
@ -378,5 +732,40 @@ def add_comment(request, name):
return render_to_response('idrfc/add_comment.html',
dict(doc=doc,
form=form),
form=form,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
def add_commentREDESIGN(request, name):
"""Add comment to Internet Draft."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile()
if request.method == 'POST':
form = AddCommentForm(request.POST)
if form.is_valid():
c = form.cleaned_data['comment']
e = DocEvent(doc=doc, by=login)
e.type = "added_comment"
e.desc = c
e.save()
email_owner(request, doc, doc.ad, login,
"A new comment added by %s" % login.name)
return HttpResponseRedirect(doc.get_absolute_url())
else:
form = AddCommentForm()
return render_to_response('idrfc/add_comment.html',
dict(doc=doc,
form=form,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
add_comment = add_commentREDESIGN

View file

@ -30,7 +30,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import re
import re, datetime
from django import forms
from django.shortcuts import render_to_response
from django.db.models import Q
@ -38,9 +38,10 @@ from django.template import RequestContext
from django.views.decorators.cache import cache_page
from ietf.idtracker.models import IDState, IESGLogin, IDSubState, Area, InternetDraft, Rfc, IDInternal, IETFWG
from ietf.idrfc.models import RfcIndex
from django.http import Http404, HttpResponse, HttpResponsePermanentRedirect
from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponsePermanentRedirect
from ietf.idrfc.idrfc_wrapper import IdWrapper,RfcWrapper,IdRfcWrapper
from ietf.utils import normalize_draftname
from django.conf import settings
def addInputEvents(widget):
widget.attrs["oninput"] = 'inputEvent()'
@ -267,7 +268,246 @@ def search_query(query_original, sort_by=None):
meta['advanced'] = True
return (results,meta)
def genParamURL(request, ignore_list):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import *
from person.models import *
from group.models import *
class SearchForm(forms.Form):
name = forms.CharField(required=False)
addInputEvents(name.widget) # consider moving this to jQuery client-side instead
rfcs = forms.BooleanField(required=False,initial=True)
activeDrafts = forms.BooleanField(required=False,initial=True)
oldDrafts = forms.BooleanField(required=False,initial=False)
lucky = forms.BooleanField(required=False,initial=False)
by = forms.ChoiceField(choices=[(x,x) for x in ('author','group','area','ad','state')], required=False, initial='wg', label='Foobar')
author = forms.CharField(required=False)
addInputEvents(author.widget)
group = forms.CharField(required=False)
addInputEvents(group.widget)
area = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), empty_label="any area", required=False)
addInputEvents(area.widget)
ad = forms.ChoiceField(choices=(), required=False)
addInputEvents(ad.widget)
state = forms.ModelChoiceField(IesgDocStateName.objects.all(), empty_label="any state", required=False)
addInputEvents(state.widget)
subState = forms.ChoiceField(choices=(), required=False)
addInputEvents(subState.widget)
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
responsible = Document.objects.values_list('ad', flat=True).distinct()
active_ads = list(Person.objects.filter(email__role__name="ad",
email__role__group__type="area",
email__role__group__state="active").distinct())
inactive_ads = list(Person.objects.filter(pk__in=responsible)
.exclude(pk__in=[x.pk for x in active_ads]))
extract_last_name = lambda x: x.name_parts()[3]
active_ads.sort(key=extract_last_name)
inactive_ads.sort(key=extract_last_name)
self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.name) for ad in active_ads] + [('', '------------------')] + [(ad.pk, ad.name) for ad in inactive_ads]
self.fields['subState'].choices = [('', 'any substate'), ('0', 'no substate')] + [(n.slug, n.name) for n in DocInfoTagName.objects.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))]
def clean_name(self):
value = self.cleaned_data.get('name','')
return normalize_draftname(value)
def clean(self):
q = self.cleaned_data
# Reset query['by'] if needed
for k in ('author','group','area','ad'):
if (q['by'] == k) and (k not in q or not q[k]):
q['by'] = None
if (q['by'] == 'state') and (not 'state' in q or not 'subState' in q or not (q['state'] or q['subState'])):
q['by'] = None
# Reset other fields
for k in ('author','group','area','ad'):
if q['by'] != k:
self.data[k] = ""
q[k] = ""
if q['by'] != 'state':
self.data['state'] = ""
self.data['subState'] = ""
q['state'] = ""
q['subState'] = ""
return q
def search_query(query_original, sort_by=None):
query = dict(query_original.items())
drafts = query['activeDrafts'] or query['oldDrafts']
if (not drafts) and (not query['rfcs']):
return ([], {})
# Non-ASCII strings don't match anything; this check
# is currently needed to avoid complaints from MySQL.
# FIXME: this should be fixed with MySQL if it's still a problem?
for k in ['name','author','group']:
try:
tmp = str(query.get(k, ''))
except:
query[k] = '*NOSUCH*'
# Start by search InternetDrafts
idresults = []
rfcresults = []
MAX = 500
docs = InternetDraft.objects.all()
# name
if query["name"]:
docs = docs.filter(Q(docalias__name__icontains=query["name"]) |
Q(title__icontains=query["name"])).distinct()
# rfc/active/old check buttons
allowed = []
disallowed = []
def add(allow, states):
l = allowed if allow else disallowed
l.extend(states)
add(query["rfcs"], ['rfc'])
add(query["activeDrafts"], ['active'])
add(query["oldDrafts"], ['repl', 'expired', 'auth-rm', 'ietf-rm'])
docs = docs.filter(state__in=allowed).exclude(state__in=disallowed)
# radio choices
by = query["by"]
if by == "author":
# FIXME: this is full name, not last name as hinted in the HTML
docs = docs.filter(authors__person__name__icontains=query["author"])
elif by == "group":
docs = docs.filter(group__acronym=query["group"])
elif by == "area":
docs = docs.filter(Q(group__parent=query["area"]) |
Q(ad__email__role__name="ad",
ad__email__role__group=query["area"]))
elif by == "ad":
docs = docs.filter(ad=query["ad"])
elif by == "state":
if query["state"]:
docs = docs.filter(iesg_state=query["state"])
if query["subState"]:
docs = docs.filter(tags=query["subState"])
# evaluate and fill in values with aggregate queries to avoid
# too many individual queries
results = list(docs.select_related("state", "iesg_state", "ad", "ad__person", "std_level", "intended_std_level", "group")[:MAX])
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[r.pk for r in results]).values_list("document_id", "name"))
# canonical name
for r in results:
if r.pk in rfc_aliases:
# lambda weirdness works around lambda binding in local for loop scope
r.canonical_name = (lambda x: lambda: x)(rfc_aliases[r.pk])
else:
r.canonical_name = (lambda x: lambda: x)(r.name)
result_map = dict((r.pk, r) for r in results)
# events
event_types = ("published_rfc",
"changed_ballot_position",
"started_iesg_process",
"new_revision")
for d in rfc_aliases.keys():
for e in event_types:
setattr(result_map[d], e, None)
for e in DocEvent.objects.filter(doc__in=rfc_aliases.keys(), type__in=event_types).order_by('-time'):
r = result_map[e.doc_id]
if not getattr(r, e.type):
# sets e.g. r.published_date = e for use in proxy wrapper
setattr(r, e.type, e)
# obsoleted/updated by
for d in rfc_aliases:
r = result_map[d]
r.obsoleted_by_list = []
r.updated_by_list = []
xed_by = RelatedDocument.objects.filter(target__name__in=rfc_aliases.values(), relationship__in=("obs", "updates")).select_related('target__document_id')
rel_rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[rel.source_id for rel in xed_by]).values_list('document_id', 'name'))
for rel in xed_by:
r = result_map[rel.target.document_id]
if rel.relationship_id == "obs":
attr = "obsoleted_by_list"
else:
attr = "updated_by_list"
getattr(r, attr).append(int(rel_rfc_aliases[rel.source_id][3:]))
# sort
def sort_key(d):
res = []
canonical = d.canonical_name()
if canonical.startswith('rfc'):
rfc_num = int(canonical[3:])
else:
rfc_num = None
if rfc_num != None:
res.append(2)
elif d.state_id == "active":
res.append(1)
else:
res.append(3)
if sort_by == "title":
res.append(d.title)
elif sort_by == "date":
res.append(str(d.revision_date or datetime.date(1990, 1, 1)))
elif sort_by == "status":
if rfc_num != None:
res.append(rfc_num)
else:
res.append(d.state)
elif sort_by == "ipr":
res.append(d.name)
elif sort_by == "ad":
if rfc_num != None:
res.append(rfc_num)
elif d.state_id == "active":
if d.iesg_state:
res.append(d.iesg_state.order)
else:
res.append(0)
else:
if rfc_num != None:
res.append(rfc_num)
else:
res.append(canonical)
return res
results.sort(key=sort_key)
meta = {}
if len(docs) == MAX:
meta['max'] = MAX
if query['by']:
meta['advanced'] = True
# finally wrap in old wrappers
wrapped_results = []
for r in results:
draft = None
rfc = None
if not r.name.startswith('rfc'):
draft = IdWrapper(r)
if r.name.startswith('rfc') or r.pk in rfc_aliases:
rfc = RfcWrapper(r)
wrapped_results.append(IdRfcWrapper(draft, rfc))
return (wrapped_results, meta)
def generate_query_string(request, ignore_list):
"""Recreates the parameter string from the given request, and
returns it as a string.
Any parameter names present in ignore_list shall not be put
@ -284,7 +524,7 @@ def search_results(request):
return search_main(request)
form = SearchForm(dict(request.REQUEST.items()))
if not form.is_valid():
return HttpResponse("form not valid?", mimetype="text/plain")
return HttpResponseBadRequest("form not valid?", mimetype="text/plain")
sort_by = None
if "sortBy" in request.GET:
@ -294,7 +534,7 @@ def search_results(request):
meta['searching'] = True
meta['by'] = form.cleaned_data['by']
meta['rqps'] = genParamURL(request, ['sortBy'])
meta['rqps'] = generate_query_string(request, ['sortBy'])
# With a later Django we can do this from the template (incude with tag)
# Pass the headers and their sort key names
meta['hdrs'] = [{'htitle': 'Document', 'htype':'doc'},
@ -329,12 +569,19 @@ def search_main(request):
def by_ad(request, name):
ad_id = None
ad_name = None
for i in IESGLogin.objects.filter(user_level__in=[1,2]):
iname = str(i).lower().replace(' ','.')
if name == iname:
ad_id = i.id
ad_name = str(i)
break
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
for p in Person.objects.filter(email__role__name__in=("ad", "ex-ad")):
if name == p.name.lower().replace(" ", "."):
ad_id = p.id
ad_name = p.name
break
else:
for i in IESGLogin.objects.filter(user_level__in=[1,2]):
iname = str(i).lower().replace(' ','.')
if name == iname:
ad_id = i.id
ad_name = str(i)
break
if not ad_id:
raise Http404
form = SearchForm({'by':'ad','ad':ad_id,
@ -347,11 +594,17 @@ def by_ad(request, name):
@cache_page(15*60) # 15 minutes
def all(request):
active = InternetDraft.objects.all().filter(status=1).order_by("filename").values('filename')
rfc1 = InternetDraft.objects.all().filter(status=3).order_by("filename").values('filename','rfc_number')
rfc_numbers1 = InternetDraft.objects.all().filter(status=3).values_list('rfc_number', flat=True)
rfc2 = RfcIndex.objects.all().exclude(rfc_number__in=rfc_numbers1).order_by('rfc_number').values('rfc_number','draft')
dead = InternetDraft.objects.all().exclude(status__in=[1,3]).order_by("filename").select_related('status__status')
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
active = (dict(filename=n) for n in InternetDraft.objects.filter(state="active").order_by("name").values_list('name', flat=True))
rfc1 = (dict(filename=d, rfc_number=int(n[3:])) for d, n in DocAlias.objects.filter(document__state="rfc", name__startswith="rfc").exclude(document__name__startswith="rfc").order_by("document__name").values_list('document__name','name').distinct())
rfc2 = (dict(rfc_number=r, draft=None) for r in sorted(int(n[3:]) for n in Document.objects.filter(type="draft", name__startswith="rfc").values_list('name', flat=True)))
dead = InternetDraft.objects.exclude(state__in=("active", "rfc")).select_related("state").order_by("name")
else:
active = InternetDraft.objects.all().filter(status=1).order_by("filename").values('filename')
rfc1 = InternetDraft.objects.all().filter(status=3).order_by("filename").values('filename','rfc_number')
rfc_numbers1 = InternetDraft.objects.all().filter(status=3).values_list('rfc_number', flat=True)
rfc2 = RfcIndex.objects.all().exclude(rfc_number__in=rfc_numbers1).order_by('rfc_number').values('rfc_number','draft')
dead = InternetDraft.objects.all().exclude(status__in=[1,3]).order_by("filename").select_related('status__status')
return render_to_response('idrfc/all.html', {'active':active, 'rfc1':rfc1, 'rfc2':rfc2, 'dead':dead}, context_instance=RequestContext(request))
@cache_page(15*60) # 15 minutes
@ -366,7 +619,7 @@ def in_last_call(request):
for p in InternetDraft.objects.all().filter(idinternal__primary_flag=1).filter(idinternal__cur_state__state='In Last Call'):
if (p.idinternal.rfc_flag):
lcdocs.append(IdRfcWrapper(None,RfCWrapper(p)))
lcdocs.append(IdRfcWrapper(None,RfcWrapper(p)))
else:
lcdocs.append(IdRfcWrapper(IdWrapper(p),None))

View file

@ -1,14 +1,16 @@
#coding: utf-8
from django.contrib import admin
from django.conf import settings
from ietf.idtracker.models import *
class AcronymAdmin(admin.ModelAdmin):
list_display=('acronym', 'name')
admin.site.register(Acronym, AcronymAdmin)
class AreaAdmin(admin.ModelAdmin):
list_display=('area_acronym', 'status')
admin.site.register(Area, AreaAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
class AcronymAdmin(admin.ModelAdmin):
list_display=('acronym', 'name')
admin.site.register(Acronym, AcronymAdmin)
class AreaAdmin(admin.ModelAdmin):
list_display=('area_acronym', 'status')
admin.site.register(Area, AreaAdmin)
class AreaDirectorAdmin(admin.ModelAdmin):
raw_id_fields=['person']
@ -22,9 +24,10 @@ class AreaWGURLAdmin(admin.ModelAdmin):
pass
admin.site.register(AreaWGURL, AreaWGURLAdmin)
class BallotInfoAdmin(admin.ModelAdmin):
pass
admin.site.register(BallotInfo, BallotInfoAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
class BallotInfoAdmin(admin.ModelAdmin):
pass
admin.site.register(BallotInfo, BallotInfoAdmin)
class ChairsHistoryAdmin(admin.ModelAdmin):
list_display=('person', 'chair_type', 'start_year', 'end_year')
@ -51,12 +54,13 @@ class IDIntendedStatusAdmin(admin.ModelAdmin):
pass
admin.site.register(IDIntendedStatus, IDIntendedStatusAdmin)
class IDInternalAdmin(admin.ModelAdmin):
ordering=['draft']
list_display=['draft', 'token_email', 'note']
search_fields=['draft__filename']
raw_id_fields=['draft','ballot']
admin.site.register(IDInternal, IDInternalAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
class IDInternalAdmin(admin.ModelAdmin):
ordering=['draft']
list_display=['draft', 'token_email', 'note']
search_fields=['draft__filename']
raw_id_fields=['draft','ballot']
admin.site.register(IDInternal, IDInternalAdmin)
class IDNextStateAdmin(admin.ModelAdmin):
pass
@ -86,13 +90,15 @@ class IESGLoginAdmin(admin.ModelAdmin):
ordering=['user_level', 'last_name']
list_display=('login_name', 'first_name', 'last_name', 'user_level')
raw_id_fields=['person']
admin.site.register(IESGLogin, IESGLoginAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
admin.site.register(IESGLogin, IESGLoginAdmin)
class IETFWGAdmin(admin.ModelAdmin):
list_display=('group_acronym', 'group_type', 'status', 'area_acronym', 'start_date', 'concluded_date', 'chairs_link')
search_fields=['group_acronym__acronym', 'group_acronym__name']
list_filter=['status', 'group_type']
admin.site.register(IETFWG, IETFWGAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
admin.site.register(IETFWG, IETFWGAdmin)
class WGChairAdmin(admin.ModelAdmin):
list_display = ('person_link', 'group_link')
@ -102,12 +108,13 @@ class IRTFAdmin(admin.ModelAdmin):
pass
admin.site.register(IRTF, IRTFAdmin)
class InternetDraftAdmin(admin.ModelAdmin):
list_display=('filename', 'revision', 'title', 'status')
search_fields=['filename', 'title']
list_filter=['status']
raw_id_fields=['replaced_by']
admin.site.register(InternetDraft, InternetDraftAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
class InternetDraftAdmin(admin.ModelAdmin):
list_display=('filename', 'revision', 'title', 'status')
search_fields=['filename', 'title']
list_filter=['status']
raw_id_fields=['replaced_by']
admin.site.register(InternetDraft, InternetDraftAdmin)
class PersonOrOrgInfoAdmin(admin.ModelAdmin):
list_display = ['person_or_org_tag', 'last_name', 'first_name', ]
@ -123,7 +130,8 @@ class RfcAdmin(admin.ModelAdmin):
fieldsets=((None, {'fields': ('rfc_number', 'title', 'group_acronym', 'area_acronym', 'status', 'comments', 'last_modified_date')}), ('Metadata', {'fields': (('online_version', 'txt_page_count'), ('fyi_number', 'std_number')), 'classes': 'collapse'}), ('Standards Track Dates', {'fields': ('rfc_published_date', ('proposed_date', 'draft_date'), ('standard_date', 'historic_date')), 'classes': 'collapse'}), ('Last Call / Ballot Info', {'fields': ('intended_status', ('lc_sent_date', 'lc_expiration_date'), ('b_sent_date', 'b_approve_date')), 'classes': 'collapse'}))
list_display=['rfc_number', 'title']
search_fields=['title']
admin.site.register(Rfc, RfcAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
admin.site.register(Rfc, RfcAdmin)
class RfcIntendedStatusAdmin(admin.ModelAdmin):
pass

View file

@ -1,5 +1,6 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf import settings
from django.contrib.syndication.feeds import Feed, FeedDoesNotExist
from django.utils.feedgenerator import Atom1Feed
from ietf.idtracker.models import IDInternal
@ -12,6 +13,9 @@ class DocumentComments(Feed):
if len(bits) != 1:
raise IDInternal.DoesNotExist
rfc = re.match('rfc(\d+)', bits[0])
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return IDInternal.objects.get(docalias__name=bits[0])
if rfc:
return IDInternal.objects.get(draft=int(rfc.group(1)), rfc_flag=1)
else:
@ -21,6 +25,9 @@ class DocumentComments(Feed):
# filename is a function for RFCs and an attribute for I-Ds.
# This works transparently for templates but is not transparent
# for python.
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return "I-D Tracker comments for %s" % obj.filename
if obj.rfc_flag:
filename = obj.document().filename()
else:
@ -39,8 +46,7 @@ class DocumentComments(Feed):
return obj.public_comments().order_by("-date","-id")
def item_pubdate(self, item):
time = datetime.time(*[(t and int(t) or 0) for t in item.time.split(":")])
return datetime.datetime.combine(item.date, time)
return item.datetime()
def item_author_name(self, item):
return item.get_author()
@ -52,7 +58,10 @@ class InLastCall(Feed):
link = "/idtracker/status/last-call/"
def items(self):
ret = list(IDInternal.objects.filter(primary_flag=1).filter(cur_state__state='In Last Call'))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
ret = list(IDInternal.objects.filter(iesg_state='lc'))
else:
ret = list(IDInternal.objects.filter(primary_flag=1).filter(cur_state__state='In Last Call'))
ret.sort(key=lambda item: (item.document().lc_expiration_date or datetime.date.today()))
return ret

View file

@ -183,6 +183,9 @@ class InternetDraft(models.Model):
return "<%s>" % (self.filename_with_rev())
def filename_with_rev(self):
return "%s-%s.txt" % (self.filename, self.revision_display())
def name(self):
# small hack to make model forward-compatible with new schema
return self.filename
def group_acronym(self):
return self.group.acronym
def group_ml_archive(self):
@ -255,11 +258,11 @@ class PersonOrOrgInfo(models.Model):
date_created = models.DateField(auto_now_add=True, null=True)
created_by = models.CharField(blank=True, null=True, max_length=8)
address_type = models.CharField(blank=True, null=True, max_length=4)
def save(self):
def save(self, **kwargs):
self.first_name_key = self.first_name.upper()
self.middle_initial_key = self.middle_initial.upper()
self.last_name_key = self.last_name.upper()
super(PersonOrOrgInfo, self).save()
super(PersonOrOrgInfo, self).save(**kwargs)
def __str__(self):
# For django.VERSION 0.96
if self.first_name == '' and self.last_name == '':
@ -271,18 +274,11 @@ class PersonOrOrgInfo(models.Model):
return u"(Person #%s)" % self.person_or_org_tag
return u"%s %s" % ( self.first_name or u"<nofirst>", self.last_name or u"<nolast>")
def email(self, priority=1, type=None):
name = str(self)
name = unicode(self)
email = ''
types = type and [ type ] or [ "INET", "Prim", None ]
for type in types:
try:
if type:
email = self.emailaddress_set.get(priority=priority, type=type).address
else:
email = self.emailaddress_set.get(priority=priority).address
break
except (EmailAddress.DoesNotExist, AssertionError):
pass
addresses = self.emailaddress_set.filter(address__contains="@").order_by('priority')[:1]
if addresses:
email = addresses[0].address.replace('<', '').replace('>', '')
return (name, email)
# Added by Sunny Lee to display person's affiliation - 5/26/2007
def affiliation(self, priority=1):
@ -410,6 +406,9 @@ class Rfc(models.Model):
return "%s.txt" % ( self.filename() )
def filename(self):
return "rfc%d" % ( self.rfc_number )
def name(self):
# small hack to make model forward-compatible with new schema
return self.filename()
def revision(self):
return "RFC"
def revision_display(self):
@ -1120,6 +1119,22 @@ class DocumentWrapper(object):
def __init__(self, document):
self.document = document
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
InternetDraftOld = InternetDraft
IDInternalOld = IDInternal
RfcOld = Rfc
BallotInfoOld = BallotInfo
IDStateOld = IDState
IDSubStateOld = IDSubState
AreaOld = Area
AcronymOld = Acronym
IESGLoginOld = IESGLogin
IETFWGOld = IETFWG
from redesign.doc.proxy import InternetDraft, IDInternal, BallotInfo, Rfc
from redesign.name.proxy import IDState, IDSubState
from redesign.group.proxy import Area, Acronym, IETFWG
from redesign.person.proxy import IESGLogin
# changes done by convert-096.py:changed maxlength to max_length
# removed core

View file

@ -1,12 +1,16 @@
# Copyright The IETF Trust 2007, All Rights Reserved
#
from django.contrib.sitemaps import Sitemap
from django.conf import settings
from ietf.idtracker.models import IDInternal, InternetDraft
class IDTrackerMap(Sitemap):
changefreq = "always"
def items(self):
return IDInternal.objects.exclude(draft=999999)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return IDInternal.objects.all()
else:
return IDInternal.objects.exclude(draft=999999)
class DraftMap(Sitemap):
changefreq = "always"

View file

@ -2,6 +2,7 @@
import textwrap
from django import template
from django.conf import settings
from django.utils.html import escape, fix_ampersands
from django.template.defaultfilters import linebreaksbr, wordwrap, stringfilter
from django.template import resolve_variable
@ -404,8 +405,18 @@ def startswith(x, y):
# based on http://www.djangosnippets.org/snippets/847/ by 'whiteinge'
@register.filter
def in_group(user, groups):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return has_role(user, groups.replace("Area_Director", "Area Director"))
return user and user.is_authenticated() and bool(user.groups.filter(name__in=groups.split(',')).values('name'))
@register.filter
def has_role(user, role_names):
from ietf.ietfauth.decorators import has_role
if not user:
return False
return has_role(user, role_names.split(','))
@register.filter
def stable_dictsort(value, arg):
"""

View file

@ -25,18 +25,18 @@ class IdTrackerUrlTestCase(SimpleUrlTestCase):
else:
return content
class WGRoleTest(django.test.TestCase):
fixtures = ['wgtest']
def setUp(self):
from ietf.idtracker.models import IETFWG
self.xmas = IETFWG.objects.get(group_acronym__acronym='xmas')
self.snow = IETFWG.objects.get(group_acronym__acronym='snow')
def test_roles(self):
print " Testing WG roles"
self.assertEquals(self.xmas.wgchair_set.all()[0].role(), 'xmas WG Chair')
self.assertEquals(self.snow.wgchair_set.all()[0].role(), 'snow BOF Chair')
self.assertEquals(self.xmas.wgsecretary_set.all()[0].role(), 'xmas WG Secretary')
self.assertEquals(self.xmas.wgtechadvisor_set.all()[0].role(), 'xmas Technical Advisor')
print "OK"
# class WGRoleTest(django.test.TestCase):
# fixtures = ['wgtest']
#
# def setUp(self):
# from ietf.idtracker.models import IETFWG
# self.xmas = IETFWG.objects.get(group_acronym__acronym='xmas')
# self.snow = IETFWG.objects.get(group_acronym__acronym='snow')
#
# def test_roles(self):
# print " Testing WG roles"
# self.assertEquals(self.xmas.wgchair_set.all()[0].role(), 'xmas WG Chair')
# self.assertEquals(self.snow.wgchair_set.all()[0].role(), 'snow BOF Chair')
# self.assertEquals(self.xmas.wgsecretary_set.all()[0].role(), 'xmas WG Secretary')
# self.assertEquals(self.xmas.wgtechadvisor_set.all()[0].role(), 'xmas Technical Advisor')
# print "OK"

View file

@ -6,13 +6,8 @@
200 /idtracker/status/last-call/
301 /idtracker/rfc3847/
301 /idtracker/12689/
301 /idtracker/draft-ietf-isis-link-attr/
301 /idtracker/draft-ietf-isis-link-attr/comment/65232/
301 /idtracker/draft-eronen-tls-psk/ # no IESG information
301 /idtracker/comment/65232/
301 /idtracker/ballot/1760/
404 /idtracker/ballot/1723/ # dangling ballot, does not link to any doc
301 /idtracker/
200 /feed/comments/draft-ietf-isis-link-attr/
200 /feed/comments/rfc3373/
@ -27,5 +22,4 @@
# Test case for missing comment time (bug fixed in changeset 1733)
200 /feed/comments/draft-ietf-msec-newtype-keyid/
200,heavy /sitemap-drafts.xml
200,heavy /sitemap-idtracker.xml

View file

@ -2,11 +2,12 @@
# Create your views here.
from django.http import HttpResponsePermanentRedirect, Http404
from django.conf import settings
from django.template import RequestContext
from django.shortcuts import get_object_or_404, render_to_response
from django.views.generic.list_detail import object_detail, object_list
from ietf.idtracker.models import InternetDraft, IDInternal, IDState, IDSubState, BallotInfo, DocumentComment
import re
import re, datetime
def state_desc(request, state, is_substate=0):
if int(state) == 100:
@ -27,15 +28,30 @@ IESG to do anything with the document.
context_instance=RequestContext(request))
def status(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
drafts = list(IDInternal.objects.exclude(iesg_state=None).exclude(iesg_state__in=('pub', 'dead', 'watching', 'rfcqueue')).order_by('iesg_state__order'))
drafts.sort(key=lambda d: (d.cur_state_id, d.status_date or datetime.date.min, d.b_sent_date or datetime.date.min))
# sadly we can't use the generic view because it only works with a queryset...
return render_to_response('idtracker/status_of_items.html', dict(object_list=drafts, title="IESG Status of Items"), context_instance=RequestContext(request))
queryset = IDInternal.objects.filter(primary_flag=1).exclude(cur_state__state__in=('RFC Ed Queue', 'RFC Published', 'AD is watching', 'Dead')).order_by('cur_state', 'status_date', 'ballot')
return object_list(request, template_name="idtracker/status_of_items.html", queryset=queryset, extra_context={'title': 'IESG Status of Items'})
def last_call(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
drafts = list(IDInternal.objects.exclude(iesg_state=None).filter(iesg_state__in=('lc', 'writeupw', 'goaheadw')).order_by('iesg_state__order'))
drafts.sort(key=lambda d: (d.cur_state_id, d.status_date or datetime.date.min, d.b_sent_date or datetime.date.min))
# sadly we can't use the generic view because it only works with a queryset...
return render_to_response('idtracker/status_of_items.html', dict(object_list=drafts, title="Documents in Last Call", lastcall=1), context_instance=RequestContext(request))
queryset = IDInternal.objects.filter(primary_flag=1).filter(cur_state__state__in=('In Last Call', 'Waiting for Writeup', 'Waiting for AD Go-Ahead')).order_by('cur_state', 'status_date', 'ballot')
return object_list(request, template_name="idtracker/status_of_items.html", queryset=queryset, extra_context={'title': 'Documents in Last Call', 'lastcall': 1})
def redirect_id(request, object_id):
'''Redirect from historical document ID to preferred filename url.'''
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
raise Http404() # we don't store the numbers anymore
doc = get_object_or_404(InternetDraft, id_document_tag=object_id)
return HttpResponsePermanentRedirect("/doc/"+doc.filename+"/")
@ -46,6 +62,9 @@ def redirect_filename(request, filename):
return HttpResponsePermanentRedirect("/doc/"+filename+"/")
def redirect_ballot(request, object_id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
raise Http404() # we don't store the numbers anymore
ballot = get_object_or_404(BallotInfo, pk=object_id)
ids = ballot.drafts.filter(primary_flag=1)
if len(ids) == 0:
@ -57,6 +76,9 @@ def redirect_ballot(request, object_id):
return HttpResponsePermanentRedirect("/doc/"+id.draft.filename+"/#ballot")
def redirect_comment(request, object_id):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
raise Http404() # we don't store the numbers anymore
comment = get_object_or_404(DocumentComment, pk=object_id)
id = comment.document
if id.rfc_flag:

View file

@ -1,5 +1,6 @@
# Copyright The IETF Trust 2007, 2008, All Rights Reserved
from django.conf import settings
from django.contrib.syndication.feeds import Feed
from django.utils.feedgenerator import Atom1Feed
from ietf.idtracker.models import IDInternal
@ -11,12 +12,24 @@ class IESGAgenda(Feed):
feed_type = Atom1Feed
def items(self):
return IDInternal.objects.filter(agenda=1).order_by('telechat_date')
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import TelechatDocEvent
drafts = IDInternal.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.min).distinct()
for d in drafts:
d.latest_telechat_event = d.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
drafts = [d for d in drafts if d.latest_telechat_event.telechat_date]
drafts.sort(key=lambda d: d.latest_telechat_event.telechat_date)
return drafts
return IDInternal.objects.filter(agenda=1).order_by('telechat_date')
def item_categories(self, item):
return [ str(item.telechat_date) ]
def item_pubdate(self, item):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return item.latest_telechat_event.time
f = item.comments().filter(comment_text__startswith='Placed on agenda for telechat')
try:
comment = f[0]
@ -28,4 +41,7 @@ class IESGAgenda(Feed):
def item_author_name(self, item):
return str( item.job_owner )
def item_author_email(self, item):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return item.ad.email_address()
return item.job_owner.person.email()[1]

View file

@ -107,6 +107,7 @@ class WGAction(models.Model):
(22, "WG Rechartering::Under evaluation for IETF Review"),
(23, "WG Rechartering::Proposed for Approval")
)
# note that with the new schema, Acronym is monkey-patched and is really Group
group_acronym = models.ForeignKey(Acronym, db_column='group_acronym_id', primary_key=True, unique=True)
note = models.TextField(blank=True,null=True)
status_date = models.DateField()

View file

@ -54,57 +54,63 @@ class RescheduleOnAgendaTestCase(django.test.TestCase):
self.assertEquals(draft.idinternal.comments().count(), comments_before + 1)
self.assertTrue("Telechat" in draft.idinternal.comments()[0].comment_text)
class RescheduleInEditTestCase(django.test.TestCase):
fixtures = ['base', 'draft']
class RescheduleOnAgendaTestCaseREDESIGN(django.test.TestCase):
fixtures = ['names']
def test_reschedule(self):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft.idinternal.telechat_date = TelechatDates.objects.all()[0].dates()[0]
draft.idinternal.agenda = True
draft.idinternal.returning_item = True
draft.idinternal.save()
form_id = draft.idinternal.draft_id
telechat_date_before = draft.idinternal.telechat_date
from ietf.utils.test_data import make_test_data
from redesign.person.models import Person
from doc.models import TelechatDocEvent
url = urlreverse('ietf.idrfc.views_edit.edit_info', kwargs={"name":"draft-ietf-mipshop-pfmipv6",})
self.client.login(remote_user="klm")
draft = make_test_data()
# add to schedule
e = TelechatDocEvent(type="scheduled_for_telechat")
e.doc = draft
e.by = Person.objects.get(name="Aread Irector")
e.telechat_date = TelechatDates.objects.all()[0].date1
e.returning_item = True
e.save()
form_id = draft.pk
telechat_date_before = e.telechat_date
url = urlreverse('ietf.iesg.views.agenda_documents')
self.client.login(remote_user="secretary")
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form select[name=telechat_date]')), 1)
self.assertEquals(len(q('form input[name=returning_item]')), 1)
self.assertEquals(len(q('form select[name=%s-telechat_date]' % form_id)), 1)
self.assertEquals(len(q('form input[name=%s-clear_returning_item]' % form_id)), 1)
# reschedule
comments_before = draft.idinternal.comments().count()
events_before = draft.docevent_set.count()
d = TelechatDates.objects.all()[0].dates()[2]
r = self.client.post(url, { 'telechat_date': d.strftime("%Y-%m-%d"),
'returning_item': "0",
'job_owner': "49",
'area_acronym': draft.idinternal.area_acronym_id,
'note': draft.idinternal.note,
'state_change_notice_to': draft.idinternal.state_change_notice_to,
'intended_status': "6", })
self.assertEquals(r.status_code, 302)
r = self.client.post(url, { '%s-telechat_date' % form_id: d.strftime("%Y-%m-%d"),
'%s-clear_returning_item' % form_id: "1" })
self.assertEquals(r.status_code, 200)
# check that it moved below the right header in the DOM on the
# agenda docs page
url = urlreverse('ietf.iesg.views.agenda_documents')
r = self.client.get(url)
d_header_pos = r.content.find("IESG telechat %s" % d.strftime("%Y-%m-%d"))
draft_pos = r.content.find(draft.filename)
draft_pos = r.content.find(draft.name)
self.assertTrue(d_header_pos < draft_pos)
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
self.assertEquals(draft.idinternal.telechat_date, d)
self.assertTrue(not draft.idinternal.returning_item)
self.assertEquals(draft.idinternal.comments().count(), comments_before + 1)
self.assertTrue("Telechat" in draft.idinternal.comments()[0].comment_text)
self.assertTrue(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat"))
self.assertEquals(draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").telechat_date, d)
self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").returning_item)
self.assertEquals(draft.docevent_set.count(), events_before + 1)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
RescheduleOnAgendaTestCase = RescheduleOnAgendaTestCaseREDESIGN
class ManageTelechatDatesTestCase(django.test.TestCase):
fixtures = ['base', 'draft']
@ -146,6 +152,56 @@ class ManageTelechatDatesTestCase(django.test.TestCase):
self.assertTrue(dates.date4 == new_date)
self.assertTrue(dates.date1 == old_date2)
class ManageTelechatDatesTestCaseREDESIGN(django.test.TestCase):
fixtures = ['names']
def test_set_dates(self):
from ietf.utils.test_data import make_test_data
make_test_data()
dates = TelechatDates.objects.all()[0]
url = urlreverse('ietf.iesg.views.telechat_dates')
login_testing_unauthorized(self, "secretary", url)
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form input[name=date1]')), 1)
# post
new_date = dates.date1 + timedelta(days=7)
r = self.client.post(url, dict(date1=new_date.isoformat(),
date2=new_date.isoformat(),
date3=new_date.isoformat(),
date4=new_date.isoformat(),
))
self.assertEquals(r.status_code, 200)
dates = TelechatDates.objects.all()[0]
self.assertTrue(dates.date1 == new_date)
def test_rollup_dates(self):
from ietf.utils.test_data import make_test_data
make_test_data()
dates = TelechatDates.objects.all()[0]
url = urlreverse('ietf.iesg.views.telechat_dates')
login_testing_unauthorized(self, "secretary", url)
old_date2 = dates.date2
new_date = dates.date4 + timedelta(days=14)
r = self.client.post(url, dict(rollup_dates="1"))
self.assertEquals(r.status_code, 200)
dates = TelechatDates.objects.all()[0]
self.assertTrue(dates.date4 == new_date)
self.assertTrue(dates.date1 == old_date2)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
ManageTelechatDatesTestCase = ManageTelechatDatesTestCaseREDESIGN
class WorkingGroupActionsTestCase(django.test.TestCase):
fixtures = ['base', 'wgactions']
@ -255,7 +311,152 @@ class WorkingGroupActionsTestCase(django.test.TestCase):
self.assertEquals(r.status_code, 200)
self.assertTrue('(sieve)' not in r.content)
class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase):
fixtures = ['names']
def setUp(self):
super(self.__class__, self).setUp()
curdir = os.path.dirname(os.path.abspath(__file__))
self.evaldir = os.path.join(curdir, "tmp-testdir")
os.mkdir(self.evaldir)
src = os.path.join(curdir, "fixtures", "sieve-charter.txt")
shutil.copy(src, self.evaldir)
settings.IESG_WG_EVALUATION_DIR = self.evaldir
def tearDown(self):
super(self.__class__, self).tearDown()
shutil.rmtree(self.evaldir)
def test_working_group_actions(self):
from ietf.utils.test_data import make_test_data
make_test_data()
url = urlreverse('iesg_working_group_actions')
login_testing_unauthorized(self, "secretary", url)
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
for wga in WGAction.objects.all():
self.assertTrue(wga.group_acronym.name in r.content)
self.assertTrue('(sieve)' in r.content)
def test_delete_wgaction(self):
from ietf.utils.test_data import make_test_data
make_test_data()
wga = WGAction.objects.all()[0]
url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk))
login_testing_unauthorized(self, "secretary", url)
r = self.client.post(url, dict(delete="1"))
self.assertEquals(r.status_code, 302)
self.assertTrue(not WGAction.objects.filter(pk=wga.pk))
def test_edit_wgaction(self):
from ietf.utils.test_data import make_test_data
from redesign.person.models import Person
make_test_data()
wga = WGAction.objects.all()[0]
url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk))
login_testing_unauthorized(self, "secretary", url)
# normal get
r = self.client.get(url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertEquals(len(q('form select[name=token_name]')), 1)
self.assertEquals(len(q('form select[name=telechat_date]')), 1)
# change
dates = TelechatDates.objects.all()[0]
token_name = Person.objects.get(name="Ad No1").name_parts()[1]
old = wga.pk
r = self.client.post(url, dict(status_date=dates.date1.isoformat(),
token_name=token_name,
category="23",
note="Testing.",
telechat_date=dates.date4.isoformat()))
self.assertEquals(r.status_code, 302)
wga = WGAction.objects.get(pk=old)
self.assertEquals(wga.status_date, dates.date1)
self.assertEquals(wga.token_name, token_name)
self.assertEquals(wga.category, 23)
self.assertEquals(wga.note, "Testing.")
self.assertEquals(wga.telechat_date, dates.date4)
def test_add_possible_wg(self):
from ietf.utils.test_data import make_test_data
from redesign.person.models import Person
from redesign.group.models import Group
make_test_data()
url = urlreverse('iesg_working_group_actions')
login_testing_unauthorized(self, "secretary", url)
r = self.client.post(url, dict(add="1",
filename='sieve-charter.txt'))
self.assertEquals(r.status_code, 302)
# now we got back a URL we can use for adding, but first make
# sure we got a proposed group with the acronym
group = Group.objects.create(
name="Sieve test test",
acronym="sieve",
state_id="proposed",
type_id="wg",
parent=None
)
add_url = r['Location']
r = self.client.get(add_url)
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue('(sieve)' in r.content)
self.assertEquals(len(q('form select[name=token_name]')), 1)
self.assertEquals(q('form input[name=status_date]')[0].get("value"), "2010-05-07")
self.assertEquals(len(q('form select[name=telechat_date]')), 1)
wgas_before = WGAction.objects.all().count()
dates = TelechatDates.objects.all()[0]
token_name = Person.objects.get(name="Ad No1").name_parts()[1]
r = self.client.post(add_url,
dict(status_date=dates.date1.isoformat(),
token_name=token_name,
category="23",
note="Testing.",
telechat_date=dates.date4.isoformat()))
self.assertEquals(r.status_code, 302)
self.assertEquals(wgas_before + 1, WGAction.objects.all().count())
def test_delete_possible_wg(self):
from ietf.utils.test_data import make_test_data
make_test_data()
url = urlreverse('iesg_working_group_actions')
login_testing_unauthorized(self, "secretary", url)
r = self.client.post(url, dict(delete="1",
filename='sieve-charter.txt'))
self.assertEquals(r.status_code, 200)
self.assertTrue('(sieve)' not in r.content)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
WorkingGroupActionsTestCase = WorkingGroupActionsTestCaseREDESIGN
class IesgUrlTestCase(SimpleUrlTestCase):

View file

@ -20,8 +20,5 @@
200 /iesg/ann/new/
# This takes ~ 300s:
#200 /iesg/ann/prev/
200 /iesg/ann/2422/
200 /iesg/ann/1563/
404 /iesg/ann/567/
200 /feed/iesg-agenda/

View file

@ -60,6 +60,18 @@ def date_threshold():
return ret
def inddocs(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
queryset_list_ind = [d for d in InternetDraft.objects.filter(tags__slug="via-rfc", docevent__type="iesg_approved").distinct() if d.latest_event(type__in=("iesg_disapproved", "iesg_approved")).type == "iesg_approved"]
queryset_list_ind.sort(key=lambda d: d.b_approve_date, reverse=True)
queryset_list_ind_dnp = [d for d in IDInternal.objects.filter(tags__slug="via-rfc", docevent__type="iesg_disapproved").distinct() if d.latest_event(type__in=("iesg_disapproved", "iesg_approved")).type == "iesg_disapproved"]
queryset_list_ind_dnp.sort(key=lambda d: d.dnp_date, reverse=True)
return render_to_response('iesg/independent_doc.html',
dict(object_list=queryset_list_ind,
object_list_dnp=queryset_list_ind_dnp),
context_instance=RequestContext(request))
queryset_list_ind = InternetDraft.objects.filter(idinternal__via_rfc_editor=1, idinternal__rfc_flag=0, idinternal__noproblem=1, idinternal__dnp=0).order_by('-b_approve_date')
queryset_list_ind_dnp = IDInternal.objects.filter(via_rfc_editor = 1,rfc_flag=0,dnp=1).order_by('-dnp_date')
return object_list(request, queryset=queryset_list_ind, template_name='iesg/independent_doc.html', allow_empty=True, extra_context={'object_list_dnp':queryset_list_ind_dnp })
@ -95,6 +107,61 @@ def wgdocs(request,cat):
queryset_list_doc.append(sub_item2)
return render_to_response( 'iesg/ietf_doc.html', {'object_list': queryset_list, 'object_list_doc':queryset_list_doc, 'is_recent':is_recent}, context_instance=RequestContext(request) )
def wgdocsREDESIGN(request,cat):
is_recent = 0
proto_actions = []
doc_actions = []
threshold = date_threshold()
proto_levels = ["bcp", "ds", "ps", "std"]
doc_levels = ["exp", "inf"]
if cat == 'new':
is_recent = 1
drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__gte=threshold, intended_std_level__in=proto_levels + doc_levels).exclude(tags__slug="via-rfc").distinct()
for d in drafts:
if d.b_approve_date and d.b_approve_date >= threshold:
if d.intended_std_level_id in proto_levels:
proto_actions.append(d)
elif d.intended_std_level_id in doc_levels:
doc_actions.append(d)
elif cat == 'prev':
# proto
start_date = datetime.date(1997, 12, 1)
drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__lt=threshold, docevent__time__gte=start_date, intended_std_level__in=proto_levels).exclude(tags__slug="via-rfc").distinct()
for d in drafts:
if d.b_approve_date and start_date <= d.b_approve_date < threshold:
proto_actions.append(d)
# doc
start_date = datetime.date(1998, 10, 15)
drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__lt=threshold, docevent__time__gte=start_date, intended_std_level__in=doc_levels).exclude(tags__slug="via-rfc").distinct()
for d in drafts:
if d.b_approve_date and start_date <= d.b_approve_date < threshold:
doc_actions.append(d)
else:
raise Http404
proto_actions.sort(key=lambda d: d.b_approve_date, reverse=True)
doc_actions.sort(key=lambda d: d.b_approve_date, reverse=True)
return render_to_response('iesg/ietf_doc.html',
dict(object_list=proto_actions,
object_list_doc=doc_actions,
is_recent=is_recent,
title_prefix="Recent" if is_recent else "Previous"),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
wgdocs = wgdocsREDESIGN
def get_doc_section(id):
states = [16,17,18,19,20,21]
if id.document().intended_status.intended_status_id in [1,2,6,7]:
@ -119,14 +186,63 @@ def get_doc_section(id):
s = s + "1"
return s
def agenda_docs(date, next_agenda):
if next_agenda:
matches = IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1)
def get_doc_sectionREDESIGN(id):
states = [16,17,18,19,20,21]
if id.intended_std_level_id in ["bcp", "ds", "ps", "std"]:
s = "2"
else:
matches = IDInternal.objects.filter(telechat_date=date, primary_flag=1)
idmatches = matches.filter(rfc_flag=0).order_by('ballot')
rfcmatches = matches.filter(rfc_flag=1).order_by('ballot')
res = {}
s = "3"
g = id.document().group_acronym()
if g and str(g) != 'none':
s = s + "1"
elif (s == "3") and id.via_rfc_editor:
s = s + "3"
else:
s = s + "2"
if not id.rfc_flag and id.cur_state.document_state_id not in states:
s = s + "3"
elif id.returning_item:
s = s + "2"
else:
s = s + "1"
return s
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
get_doc_section = get_doc_sectionREDESIGN
def agenda_docs(date, next_agenda):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import TelechatDocEvent
matches = IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date)
idmatches = []
rfcmatches = []
for m in matches:
if m.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
continue
if next_agenda and not m.agenda:
continue
if m.docalias_set.filter(name__startswith="rfc"):
rfcmatches.append(m)
else:
idmatches.append(m)
idmatches.sort(key=lambda d: d.start_date or datetime.date.min)
rfcmatches.sort(key=lambda d: d.start_date or datetime.date.min)
else:
if next_agenda:
matches = IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1)
else:
matches = IDInternal.objects.filter(telechat_date=date, primary_flag=1)
idmatches = matches.filter(rfc_flag=0).order_by('ballot')
rfcmatches = matches.filter(rfc_flag=1).order_by('ballot')
res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4))
for id in list(idmatches)+list(rfcmatches):
section_key = "s"+get_doc_section(id)
if section_key not in res:
@ -189,11 +305,12 @@ def agenda_txt(request):
def agenda_scribe_template(request):
date = TelechatDates.objects.all()[0].date1
docs = agenda_docs(date, True)
return render_to_response('iesg/scribe_template.html', {'date':str(date), 'docs':docs}, context_instance=RequestContext(request) )
return render_to_response('iesg/scribe_template.html', {'date':str(date), 'docs':docs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}, context_instance=RequestContext(request) )
def _agenda_moderator_package(request):
data = _agenda_data(request)
data['ad_names'] = [str(x) for x in IESGLogin.active_iesg()]
data['ad_names'].sort(key=lambda x: x.split(' ')[-1])
return render_to_response("iesg/moderator_package.html", data, context_instance=RequestContext(request))
@group_required('Area_Director','Secretariat')
@ -224,7 +341,13 @@ def agenda_documents_txt(request):
dates = TelechatDates.objects.all()[0].dates()
docs = []
for date in dates:
docs.extend(IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import TelechatDocEvent
for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date):
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date:
docs.append(d)
else:
docs.extend(IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1))
t = loader.get_template('iesg/agenda_documents.txt')
c = Context({'docs':docs})
return HttpResponse(t.render(c), mimetype='text/plain')
@ -259,12 +382,20 @@ def handle_reschedule_form(request, idinternal, dates):
if request.method == 'POST':
form = RescheduleForm(request.POST, **formargs)
if form.is_valid():
update_telechat(request, idinternal,
form.cleaned_data['telechat_date'])
if form.cleaned_data['clear_returning_item']:
idinternal.returning_item = False
idinternal.event_date = datetime.date.today()
idinternal.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
login = request.user.get_profile()
update_telechat(request, idinternal, login,
form.cleaned_data['telechat_date'],
False if form.cleaned_data['clear_returning_item'] else None)
idinternal.time = datetime.datetime.now()
idinternal.save()
else:
update_telechat(request, idinternal,
form.cleaned_data['telechat_date'])
if form.cleaned_data['clear_returning_item']:
idinternal.returning_item = False
idinternal.event_date = datetime.date.today()
idinternal.save()
else:
form = RescheduleForm(**formargs)
@ -273,7 +404,16 @@ def handle_reschedule_form(request, idinternal, dates):
def agenda_documents(request):
dates = TelechatDates.objects.all()[0].dates()
idinternals = list(IDInternal.objects.filter(telechat_date__in=dates,primary_flag=1,agenda=1).order_by('rfc_flag', 'ballot'))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import TelechatDocEvent
idinternals = []
for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date__in=dates):
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date in dates:
idinternals.append(d)
idinternals.sort(key=lambda d: (d.rfc_flag, d.start_date))
else:
idinternals = list(IDInternal.objects.filter(telechat_date__in=dates,primary_flag=1,agenda=1).order_by('rfc_flag', 'ballot'))
for i in idinternals:
i.reschedule_form = handle_reschedule_form(request, i, dates)
@ -294,7 +434,10 @@ def agenda_documents(request):
w.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(w.id.tracker_id)
iprs = IprDraft.objects.filter(document=w.id.tracker_id)
else:
ri = RfcIndex.objects.get(rfc_number=i.draft_id)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
ri = i
else:
ri = RfcIndex.objects.get(rfc_number=i.draft_id)
w = RfcWrapper(ri)
w.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(w.rfc.rfc_number)
iprs = IprRfc.objects.filter(document=w.rfc.rfc_number)
@ -307,7 +450,14 @@ def agenda_documents(request):
def telechat_docs_tarfile(request,year,month,day):
from tempfile import mkstemp
date=datetime.date(int(year),int(month),int(day))
docs= IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from doc.models import TelechatDocEvent
docs = []
for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date):
if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date:
docs.append(d)
else:
docs= IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1)
response = HttpResponse(mimetype='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename=telechat-%s-%s-%s-docs.tgz'%(year, month, day)
tarstream = tarfile.open('','w:gz',response)
@ -330,6 +480,29 @@ def telechat_docs_tarfile(request,year,month,day):
return response
def discusses(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
res = []
for d in IDInternal.objects.filter(iesg_state__in=("pub-req", "ad-eval", "review-e", "lc-req", "lc", "writeupw", "goaheadw", "iesg-eva", "defer", "watching"), docevent__ballotpositiondocevent__pos="discuss").distinct():
found = False
for p in d.positions.all():
if p.discuss:
found = True
break
if not found:
continue
if d.rfc_flag:
doc = RfcWrapper(d)
else:
doc = IdWrapper(draft=d)
if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot():
res.append(doc)
return direct_to_template(request, 'iesg/discusses.html', {'docs':res})
positions = Position.objects.filter(discuss=1)
res = []
try:

View file

@ -31,6 +31,8 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from django.utils.http import urlquote
from django.conf import settings
from django.db.models import Q
from django.contrib.auth.decorators import _CheckLogin
from django.http import HttpResponseRedirect, HttpResponseForbidden
@ -62,3 +64,48 @@ def group_required(*group_names):
def decorate(view_func):
return _CheckLogin403(view_func, lambda u: bool(u.groups.filter(name__in=group_names)), "Restricted to group%s %s" % ("s" if len(group_names) != 1 else "", ",".join(group_names)))
return decorate
def has_role(user, role_names):
"""Determines whether user has any of the given standard roles
given. Role names must be a list or, in case of a single value, a
string."""
if isinstance(role_names, str) or isinstance(role_names, unicode):
role_names = [ role_names ]
if not user or not user.is_authenticated():
return False
from redesign.person.models import Person
try:
person = user.get_profile()
except Person.DoesNotExist:
return False
role_qs = {
"Area Director": Q(email__person=person, name="ad"),
"Secretariat": Q(email__person=person, name="secr", group__acronym="secretariat")
}
filter_expr = Q()
for r in role_names:
filter_expr |= role_qs[r]
from redesign.group.models import Role
return bool(Role.objects.filter(filter_expr)[:1])
def role_required(*role_names):
"""View decorator for checking that the user is logged in and
belongs to (at least) one of the listed roles. Users who are not
logged in are redirected to the login page; users who don't have
one of the roles get a "403" page.
"""
def decorate(view_func):
return _CheckLogin403(view_func,
lambda u: has_role(u, role_names),
"Restricted to role%s %s" % ("s" if len(role_names) != 1 else "", ",".join(role_names)))
return decorate
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
group_required = lambda *group_names: role_required(*[n.replace("Area_Director", "Area Director") for n in group_names])

View file

@ -70,6 +70,20 @@ class IetfUserProfile(models.Model):
except:
return None
def email(self):
# quick hack to bind new and old schema together for the time being
try:
l = IESGLogin.objects.get(login_name=self.user.username)
if l.person:
person = l.person
else:
person = PersonOrOrgInfo.objects.get(first_name=l.first_name,
last_name=l.last_name)
except IESGLogin.DoesNotExist, PersonOrOrgInfo.DoesNotExist:
person = None
from person.models import Email
return Email.objects.get(address=person.email()[1])
def __str__(self):
return "IetfUserProfile(%s)" % (self.user,)

View file

@ -31,6 +31,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import unittest
from django.conf import settings
from django.contrib.auth.models import User
from django.test.client import Client
from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest
@ -41,6 +42,8 @@ class IetfAuthUrlTestCase(SimpleUrlTestCase):
def testUrls(self):
self.doTestUrls(__file__)
# this test case should really work on a test database instead of the
# real one
class IetfAuthTestCase(unittest.TestCase,RealDatabaseTest):
def setUp(self):
self.setUpRealDatabase()
@ -61,7 +64,7 @@ class IetfAuthTestCase(unittest.TestCase,RealDatabaseTest):
response = c.get(nexturl[2], {}, False, REMOTE_USER=username)
self.assertEquals(response.status_code, 200)
self.assert_("Roles/Groups:" in response.content)
self.assert_("User name" in response.content)
return response
def testLogin(self):
@ -95,4 +98,7 @@ class IetfAuthTestCase(unittest.TestCase,RealDatabaseTest):
self.assert_("IETF_Chair" in groups)
print "OK"
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
# this test doesn't make any sense anymore
IetfAuthTestCase.testGroups = lambda x: None

View file

@ -69,4 +69,21 @@ def ietf_loggedin(request):
@login_required
def profile(request):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from person.models import Person
from group.models import Role
roles = []
person = None
try:
person = request.user.get_profile()
roles = Role.objects.filter(email__person=person).distinct()
except Person.DoesNotExist:
pass
return render_to_response('registration/profileREDESIGN.html',
dict(roles=roles,
person=person),
context_instance=RequestContext(request))
return render_to_response('registration/profile.html', context_instance=RequestContext(request))

View file

@ -14,6 +14,8 @@ register = template.Library()
@register.inclusion_tag('ietfworkflows/stream_state.html', takes_context=True)
def stream_state(context, doc):
from django.conf import settings
return settings.TEMPLATE_STRING_IF_INVALID # FIXME: temporary work-around
request = context.get('request', None)
data = {}
stream = get_stream_from_wrapper(doc)
@ -52,6 +54,8 @@ def workflow_history_entry(context, entry):
@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True)
def edit_actions(context, wrapper):
return None # FIXME: temporary work-around
request = context.get('request', None)
user = request and request.user
if not user:

View file

@ -284,6 +284,15 @@ def update_stream(obj, comment, person, to_stream, extra_notify=[]):
def get_full_info_for_draft(draft):
return dict(# FIXME: temporary work-around
streamed=settings.TEMPLATE_STRING_IF_INVALID,
stream=settings.TEMPLATE_STRING_IF_INVALID,
workflow=settings.TEMPLATE_STRING_IF_INVALID,
tags=[settings.TEMPLATE_STRING_IF_INVALID],
state=settings.TEMPLATE_STRING_IF_INVALID,
shepherd=draft.shepherd,
)
return dict(
streamed=get_streamed_draft(draft),
stream=get_stream_from_draft(draft),

View file

@ -1,5 +1,6 @@
#coding: utf-8
from django.contrib import admin
from django.conf import settings
from ietf.ipr.models import *
class IprContactAdmin(admin.ModelAdmin):
@ -12,7 +13,8 @@ admin.site.register(IprDetail, IprDetailAdmin)
class IprDraftAdmin(admin.ModelAdmin):
pass
admin.site.register(IprDraft, IprDraftAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
admin.site.register(IprDraft, IprDraftAdmin)
class IprLicensingAdmin(admin.ModelAdmin):
pass
@ -24,7 +26,8 @@ admin.site.register(IprNotification, IprNotificationAdmin)
class IprRfcAdmin(admin.ModelAdmin):
pass
admin.site.register(IprRfc, IprRfcAdmin)
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
admin.site.register(IprRfc, IprRfcAdmin)
class IprSelecttypeAdmin(admin.ModelAdmin):
pass

View file

@ -1,6 +1,7 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.db import models
from django.conf import settings
#from django import newforms as forms
from ietf.idtracker.views import InternetDraft
from ietf.idtracker.models import Rfc
@ -115,6 +116,8 @@ class IprDetail(models.Model):
def __str__(self):
return self.title
def docs(self):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return list(IprDraftProxy.objects.filter(ipr=self))
return list(self.drafts.all()) + list(self.rfcs.all())
@models.permalink
def get_absolute_url(self):
@ -151,8 +154,8 @@ class IprContact(models.Model):
class IprDraft(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='drafts')
document = models.ForeignKey(InternetDraft, db_column='id_document_tag', related_name="ipr")
ipr = models.ForeignKey(IprDetail, related_name='drafts_old' if settings.USE_DB_REDESIGN_PROXY_CLASSES else 'drafts')
document = models.ForeignKey(InternetDraft, db_column='id_document_tag', related_name="ipr_draft_old" if settings.USE_DB_REDESIGN_PROXY_CLASSES else "ipr")
revision = models.CharField(max_length=2)
def __str__(self):
return "%s which applies to %s-%s" % ( self.ipr, self.document, self.revision )
@ -170,8 +173,8 @@ class IprNotification(models.Model):
db_table = 'ipr_notifications'
class IprRfc(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='rfcs')
document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
ipr = models.ForeignKey(IprDetail, related_name='rfcs_old' if settings.USE_DB_REDESIGN_PROXY_CLASSES else 'rfcs')
document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr_rfc_old" if settings.USE_DB_REDESIGN_PROXY_CLASSES else "ipr")
def __str__(self):
return "%s applies to RFC%04d" % ( self.ipr, self.document_id )
class Meta:
@ -186,6 +189,68 @@ class IprUpdate(models.Model):
class Meta:
db_table = 'ipr_updates'
if settings.USE_DB_REDESIGN_PROXY_CLASSES or hasattr(settings, "IMPORTING_IPR"):
from doc.models import DocAlias
class IprDocAlias(models.Model):
ipr = models.ForeignKey(IprDetail, related_name='documents')
doc_alias = models.ForeignKey(DocAlias)
rev = models.CharField(max_length=2, blank=True)
def __unicode__(self):
if self.rev:
return u"%s which applies to %s-%s" % (self.ipr, self.document, self.revision)
else:
return u"%s which applies to %s" % (self.ipr, self.document)
# proxy stuff
IprDraftOld = IprDraft
IprRfcOld = IprRfc
from redesign.proxy_utils import TranslatingManager
class IprDraftProxy(IprDocAlias):
objects = TranslatingManager(dict(document="doc_alias__name"))
# document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr")
# document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
@property
def document(self):
from redesign.doc.proxy import DraftLikeDocAlias
return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id)
#revision = models.CharField(max_length=2)
@property
def revision(self):
return self.rev
class Meta:
proxy = True
IprDraft = IprDraftProxy
class IprRfcProxy(IprDocAlias):
objects = TranslatingManager(dict(document=lambda v: ("doc_alias__name", "rfc%s" % v)))
# document = models.ForeignKey(InternetDraft, db_column='id_document_tag', "ipr")
# document = models.ForeignKey(Rfc, db_column='rfc_number', related_name="ipr")
@property
def document(self):
from redesign.doc.proxy import DraftLikeDocAlias
return DraftLikeDocAlias.objects.get(pk=self.doc_alias_id)
#revision = models.CharField(max_length=2)
@property
def revision(self):
return self.rev
class Meta:
proxy = True
IprRfc = IprRfcProxy
# changes done by convert-096.py:changed maxlength to max_length
# removed core
# removed edit_inline

View file

@ -102,11 +102,19 @@ def new(request, type, update=None, submitter=None):
setattr(self, contact, ContactForm(prefix=contact[:4], initial=contact_initial.get(contact, {}), *args, **kwnoinit))
rfclist_initial = ""
if update:
rfclist_initial = " ".join(["RFC%d" % rfc.document_id for rfc in update.rfcs.all()])
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.ipr.models import IprDocAlias
rfclist_initial = " ".join(a.doc_alias.name.upper() for a in IprDocAlias.objects.filter(doc_alias__name__startswith="rfc", ipr=update))
else:
rfclist_initial = " ".join(["RFC%d" % rfc.document_id for rfc in update.rfcs.all()])
self.base_fields["rfclist"] = forms.CharField(required=False, initial=rfclist_initial)
draftlist_initial = ""
if update:
draftlist_initial = " ".join([draft.document.filename + (draft.revision and "-%s" % draft.revision or "") for draft in update.drafts.all()])
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.ipr.models import IprDocAlias
draftlist_initial = " ".join(a.doc_alias.name + ("-%s" % a.rev if a.rev else "") for a in IprDocAlias.objects.filter(ipr=update).exclude(doc_alias__name__startswith="rfc"))
else:
draftlist_initial = " ".join([draft.document.filename + (draft.revision and "-%s" % draft.revision or "") for draft in update.drafts.all()])
self.base_fields["draftlist"] = forms.CharField(required=False, initial=draftlist_initial)
if section_list.get("holder_contact", False):
self.base_fields["hold_contact_is_submitter"] = forms.BooleanField(required=False)
@ -134,7 +142,11 @@ def new(request, type, update=None, submitter=None):
rfclist = rfclist.strip().split()
for rfc in rfclist:
try:
Rfc.objects.get(rfc_number=int(rfc))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.models import DocAlias
DocAlias.objects.get(name="rfc%s" % int(rfc))
else:
Rfc.objects.get(rfc_number=int(rfc))
except:
raise forms.ValidationError("Unknown RFC number: %s - please correct this." % rfc)
rfclist = " ".join(rfclist)
@ -155,7 +167,13 @@ def new(request, type, update=None, submitter=None):
filename = draft
rev = None
try:
id = InternetDraft.objects.get(filename=filename)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.models import DocAlias
id = DocAlias.objects.get(name=filename)
# proxy attribute for code below
id.revision = id.document.rev
else:
id = InternetDraft.objects.get(filename=filename)
except Exception, e:
log("Exception: %s" % e)
raise forms.ValidationError("Unknown Internet-Draft: %s - please correct this." % filename)
@ -263,15 +281,32 @@ def new(request, type, update=None, submitter=None):
# Save IprDraft(s)
for draft in form.cleaned_data["draftlist"].split():
id = InternetDraft.objects.get(filename=draft[:-3])
iprdraft = models.IprDraft(document=id, ipr=instance, revision=draft[-2:])
iprdraft.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
name = draft[:-3]
rev = draft[-2:]
from redesign.doc.models import DocAlias
models.IprDocAlias.objects.create(
doc_alias=DocAlias.objects.get(name=name),
ipr=instance,
rev=rev)
else:
id = InternetDraft.objects.get(filename=draft[:-3])
iprdraft = models.IprDraft(document=id, ipr=instance, revision=draft[-2:])
iprdraft.save()
# Save IprRfc(s)
for rfcnum in form.cleaned_data["rfclist"].split():
rfc = Rfc.objects.get(rfc_number=int(rfcnum))
iprrfc = models.IprRfc(document=rfc, ipr=instance)
iprrfc.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.models import DocAlias
models.IprDocAlias.objects.create(
doc_alias=DocAlias.objects.get(name="rfc%s" % int(rfcnum)),
ipr=instance,
rev="")
else:
rfc = Rfc.objects.get(rfc_number=int(rfcnum))
iprrfc = models.IprRfc(document=rfc, ipr=instance)
iprrfc.save()
send_mail(request, settings.IPR_EMAIL_TO, ('IPR Submitter App', 'ietf-ipr@ietf.org'), 'New IPR Submission Notification', "ipr/new_update_email.txt", {"ipr": instance, "update": update})
return render("ipr/submitted.html", {"update": update}, context_instance=RequestContext(request))

View file

@ -1,5 +1,7 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.conf import settings
from django.db.models import Q
from ietf.idtracker.models import InternetDraft, Rfc
inverse = {
@ -77,3 +79,59 @@ def related_docs(doc, found = []):
set_relation(doc, 'is_draft_of', item)
found = related_docs(item, found)
return found
def related_docsREDESIGN(alias, _):
"""Get related document aliases to given alias through depth-first search."""
from redesign.doc.models import RelatedDocument
from redesign.doc.proxy import DraftLikeDocAlias
mapping = dict(
updates='that updated',
obs='that obsoleted',
replaces='that replaced',
)
inverse_mapping = dict(
updates='that was updated by',
obs='that was obsoleted by',
replaces='that was replaced by',
)
res = [ alias ]
remaining = [ alias ]
while remaining:
a = remaining.pop()
related = RelatedDocument.objects.filter(relationship__in=mapping.keys()).filter(Q(source=a.document) | Q(target=a))
for r in related:
if r.source == a.document:
found = DraftLikeDocAlias.objects.filter(pk=r.target_id)
inverse = True
else:
found = DraftLikeDocAlias.objects.filter(document=r.source)
inverse = False
for x in found:
if not x in res:
x.related = a
x.relation = (inverse_mapping if inverse else mapping)[r.relationship_id]
res.append(x)
remaining.append(x)
# there's one more source of relatedness, a draft can have been published
aliases = DraftLikeDocAlias.objects.filter(document=a.document).exclude(pk__in=[x.pk for x in res])
for oa in aliases:
rel = None
if a.name.startswith("rfc") and oa.name.startswith("draft"):
rel = "that was published as"
elif a.name.startswith("draft") and oa.name.startswith("rfc"):
rel = "which came from"
if rel:
oa.related = a
oa.relation = rel
res.append(oa)
remaining.append(oa)
return res
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
related_docs = related_docsREDESIGN

View file

@ -24,7 +24,11 @@ def mark_last_doc(iprs):
def iprs_from_docs(docs):
iprs = []
for doc in docs:
if isinstance(doc, InternetDraft):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.ipr.models import IprDocAlias
disclosures = [ x.ipr for x in IprDocAlias.objects.filter(doc_alias=doc, ipr__status__in=[1,3]) ]
elif isinstance(doc, InternetDraft):
disclosures = [ item.ipr for item in IprDraft.objects.filter(document=doc, ipr__status__in=[1,3]) ]
elif isinstance(doc, Rfc):
disclosures = [ item.ipr for item in IprRfc.objects.filter(document=doc, ipr__status__in=[1,3]) ]
@ -50,7 +54,11 @@ def patent_file_search(url, q):
return False
def search(request, type="", q="", id=""):
wgs = IETFWG.objects.filter(group_type__group_type_id=1).exclude(group_acronym__acronym='2000').select_related().order_by('acronym.acronym')
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from group.models import Group
wgs = Group.objects.filter(type="wg").exclude(acronym="2000").select_related().order_by("acronym")
else:
wgs = IETFWG.objects.filter(group_type__group_type_id=1).exclude(group_acronym__acronym='2000').select_related().order_by('acronym.acronym')
args = request.REQUEST.items()
if args:
for key, value in args:
@ -70,20 +78,32 @@ def search(request, type="", q="", id=""):
if type == "document_search":
if q:
q = normalize_draftname(q)
start = InternetDraft.objects.filter(filename__contains=q)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.proxy import DraftLikeDocAlias
start = DraftLikeDocAlias.objects.filter(name__contains=q, name__startswith="draft")
else:
start = InternetDraft.objects.filter(filename__contains=q)
if id:
try:
id = int(id,10)
except:
id = -1
start = InternetDraft.objects.filter(id_document_tag=id)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.proxy import DraftLikeDocAlias
start = DraftLikeDocAlias.objects.filter(name=id)
else:
try:
id = int(id,10)
except:
id = -1
start = InternetDraft.objects.filter(id_document_tag=id)
if type == "rfc_search":
if q:
try:
q = int(q, 10)
except:
q = -1
start = Rfc.objects.filter(rfc_number=q)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.proxy import DraftLikeDocAlias
start = DraftLikeDocAlias.objects.filter(name__contains=q, name__startswith="rfc")
else:
start = Rfc.objects.filter(rfc_number=q)
if start.count() == 1:
first = start[0]
doc = str(first)
@ -142,12 +162,20 @@ def search(request, type="", q="", id=""):
# Search by wg acronym
# Document list with IPRs
elif type == "wg_search":
try:
docs = list(InternetDraft.objects.filter(group__acronym=q))
except:
docs = []
docs += [ draft.replaced_by for draft in docs if draft.replaced_by_id ]
docs += list(Rfc.objects.filter(group_acronym=q))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.proxy import DraftLikeDocAlias
try:
docs = list(DraftLikeDocAlias.objects.filter(document__group__acronym=q))
docs += list(DraftLikeDocAlias.objects.filter(document__relateddocument__target__in=docs, document__relateddocument__relationship="replaces"))
except:
docs = []
else:
try:
docs = list(InternetDraft.objects.filter(group__acronym=q))
except:
docs = []
docs += [ draft.replaced_by for draft in docs if draft.replaced_by_id ]
docs += list(Rfc.objects.filter(group_acronym=q))
docs = [ doc for doc in docs if doc.ipr.count() ]
iprs, docs = iprs_from_docs(docs)
@ -158,11 +186,18 @@ def search(request, type="", q="", id=""):
# Search by rfc and id title
# Document list with IPRs
elif type == "title_search":
try:
docs = list(InternetDraft.objects.filter(title__icontains=q))
except:
docs = []
docs += list(Rfc.objects.filter(title__icontains=q))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from redesign.doc.proxy import DraftLikeDocAlias
try:
docs = list(DraftLikeDocAlias.objects.filter(document__title__icontains=q))
except:
docs = []
else:
try:
docs = list(InternetDraft.objects.filter(title__icontains=q))
except:
docs = []
docs += list(Rfc.objects.filter(title__icontains=q))
docs = [ doc for doc in docs if doc.ipr.count() ]
iprs, docs = iprs_from_docs(docs)

View file

@ -48,6 +48,8 @@ class IprUrlTestCase(SimpleUrlTestCase):
else:
return content
# this test should be ported to run on a test database instead of the
# real database, and possibly expanded
class NewIprTestCase(unittest.TestCase,RealDatabaseTest):
SPECIFIC_DISCLOSURE = {
'legal_name':'Testing Only Please Ignore',
@ -58,6 +60,7 @@ class NewIprTestCase(unittest.TestCase,RealDatabaseTest):
'ietf_telephone':'555-555-0101',
'ietf_email':'test.participant@example.com',
'rfclist':'1149',
'draftlist':'draft-burdis-http-sasl-00',
'patents':'none',
'date_applied':'never',
'country':'nowhere',

View file

@ -48,6 +48,34 @@ def list_drafts(request):
context_instance=RequestContext(request)),
mimetype="text/plain")
def list_draftsREDESIGN(request):
from ipr.models import IprDocAlias
docipr = {}
for o in IprDocAlias.objects.filter(ipr__status=1).select_related("doc_alias"):
name = o.doc_alias.name
if name.startswith("rfc"):
name = name.upper()
if not name in docipr:
docipr[name] = []
docipr[name].append(o.ipr_id)
docs = [ dict(name=name, iprs=sorted(iprs)) for name, iprs in docipr.iteritems() ]
# drafts.html is not an HTML file
return HttpResponse(render_to_string("ipr/drafts.html",
dict(docs=docs),
context_instance=RequestContext(request)),
mimetype="text/plain")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
list_drafts = list_draftsREDESIGN
# Details views
def show(request, ipr_id=None, removed=None):
@ -93,6 +121,12 @@ def show(request, ipr_id=None, removed=None):
except:
# if file does not exist, iframe is used instead
pass
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from ietf.ipr.models import IprDraft, IprRfc
ipr.drafts = IprDraft.objects.filter(ipr=ipr).exclude(doc_alias__name__startswith="rfc").order_by("id")
ipr.rfcs = IprRfc.objects.filter(ipr=ipr).filter(doc_alias__name__startswith="rfc").order_by("id")
return render("ipr/details.html", {"ipr": ipr, "section_list": section_list},
context_instance=RequestContext(request))

View file

@ -3,8 +3,10 @@
from django.conf.urls.defaults import patterns
from ietf.idtracker.models import IETFWG
http_archive_wg_queryset = IETFWG.objects.filter(email_archive__startswith='http')
urlpatterns = patterns('django.views.generic.list_detail',
(r'^wg/$', 'object_list', { 'queryset': IETFWG.objects.filter(email_archive__startswith='http'), 'template_name': 'mailinglists/wgwebmail_list.html' }),
(r'^wg/$', 'object_list', { 'queryset': http_archive_wg_queryset, 'template_name': 'mailinglists/wgwebmail_list.html' }),
)
urlpatterns += patterns('',
(r'^nonwg/$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/list/nonwg.html'}),

View file

@ -8,9 +8,12 @@ import os
import syslog
syslog.openlog("django", syslog.LOG_PID, syslog.LOG_LOCAL0)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
import sys
sys.path.append(os.path.abspath(BASE_DIR + "/.."))
sys.path.append(os.path.abspath(BASE_DIR + "/../redesign"))
DEBUG = True
TEMPLATE_DEBUG = DEBUG
@ -121,6 +124,11 @@ INSTALLED_APPS = (
'south',
'workflows',
'permissions',
'redesign.person',
'redesign.name',
'redesign.group',
'redesign.doc',
'redesign.issue',
'ietf.announcements',
'ietf.idindex',
'ietf.idtracker',
@ -228,6 +236,13 @@ MAX_DAILY_SUBMISSION = 1000
MAX_DAILY_SUBMISSION_SIZE = 2000
# End of ID Submission Tool settings
# DB redesign
USE_DB_REDESIGN_PROXY_CLASSES = True
if USE_DB_REDESIGN_PROXY_CLASSES:
AUTH_PROFILE_MODULE = 'person.Person'
AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.RemoteUserBackend', )
# Put SECRET_KEY in here, or any other sensitive or site-specific
# changes. DO NOT commit settings_local.py to svn.
from settings_local import *

View file

@ -0,0 +1,18 @@
{% extends "base.html" %}
{% load ietf_filters %}
{% block title %}Announcement: {{ message.time|date:"F j, Y" }} -- {{ message.subject|escape }}{% endblock %}
{% block content %}
<h1>NomCom Message</h1>
<p>
From: {{ message.frm|escape }}<br/>
To: {{ message.to|escape }}<br/>
Date: {{ message.time|date:"F j, Y" }}<br/>
Subject: {{ message.subject|escape }}
</p>
<hr width="400" align="left" />
<pre>
{{ message.body|escape }}
</pre>
{% endblock %}

View file

@ -0,0 +1,76 @@
{% extends "base.html" %}
{% load ietf_filters %}
{% block title %}IAB/IESG Nominating Committee{% endblock %}
{% block content %}
<h1>IAB/IESG Nominating Committee</h1>
<h3>Current Committee Chair: <a href="mailto:{{ curr_chair.address }}">{{ curr_chair.get_name }}</a></h3>
{% for regime in regimes %}
<hr>
<h1>Messages from {{ regime.group.start_year }} - {{ regime.group.end_year }}</h1>
<h4>Committee Chair: <a href="mailto:{{ regime.chair.address }}">{{ regime.chair.get_name }}</a></h4>
<table class="ietf-table">
<tr>
<th width="10%">Date</th>
<th width="60%">Subject</th>
<th width="30%">Sent To</th>
</tr>
{% for ann in regime.announcements %}
<tr>
<td>{{ ann.time|date:"Y-M-d" }}</td>
<td style="max-width:50%"><a href="/ann/nomcom/{{ ann.id }}/">{{ ann.subject|escape }}</a></td>
<td>{{ ann.to_name }}</td>
<tr>
{% endfor %}
</table>
{% endfor %}
<hr>
{# somebody ought to import these announcements in the DB instead of this mess #}
<h3>Messages from 2003-2004 NomCom</h3>
Committee Chair: <A HREF="mailto:richdr@microsoft.com">Rich Draves</A>
<br><br><li><a href="http://www.ietf.org/old/2009/nomcom/msg08-25-2003.txt">IETF Nominations Committee Chair Announcement</a> August 25, 2003
<LI><a href="http://www.ietf.org/old/2009/nomcom/msg09.22.txt">NomCom call for volunteers</a> September 22, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/select-announce_03.txt">Selection of the Nominations Committee</A>
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg10.06.txt">NomCom Volunteer List</a> October 06, 2004
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg1010.txt">NomCom Selection</a> October 10, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg10.17.txt">Call for Nominees</a> October 17, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg10.24.txt">NomCom members</a> October 24, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg11.07.txt">NomCom at IETF</a> November 07, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg11.14.txt">NomCom News</a> November 14, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg11.26.txt">Reminder - nominations to replace Randy Bush</a> November 26, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg12.01.txt">Randy Bush replacement schedule</a> December 01, 2003
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg01.14.txt">Randy Bush replacement</a> January 14, 2004
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg02.13.txt">NomCom results</a> February 13, 2004
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg09.28.txt">Call for Security AD nominations</a> September 28, 2004
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg11.07.04.txt">Steve Bellovin replacement</a> November 07, 2004
<h3>Messages from 2002-2003 NomCom</h3>
Committee Chair: <A HREF="mailto:PRoberts@MEGISTO.com">Phil Roberts</A>
<br><br>
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg19765.html">First Call for Volunteers</A> July 30, 2002
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/select-announce.txt">Selection of the Nominations Committee</A>
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg0918.txt">Announcement of the Nominations Committee</A> September 18, 2002
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg10.21.txt">Announcement of IESG and IAB Nominations Requests</A> October 21, 2002
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg11.05.txt">Announcement of IESG and IAB Nominations Requests</A> November 5, 2002
<LI><A HREF="http://www.ietf.org/old/2009/nomcom/msg11.12.txt">Announcement of IESG and IAB Nominations Requests</A> November 12, 2002
<LI><a href="http://www.ietf.org/old/2009/nomcom/msg02.27.txt">IETF Nomcom Announcement</a> February 27, 2003
<LI><a href="http://www.ietf.org/old/2009/nomcom/msg06.11.txt">Announcement of IESG and IAB Nominations Request</a> June 11, 2003
<LI><a href="http://www.ietf.org/old/2009/nomcom/msg07.15.txt">Nomcom result announcement</a> July 15, 2003
<h3>Historical Information</h3>
<li><a href="http://www.ietf.org/nomcom/committee.html">IAB/IESG Nominating Committee Members (by year)</a>
<h3>References</h3>
<LI><A HREF="http://www.ietf.org/rfc/rfc2026.txt">The Internet Standards Process (RFC 2026)</A>
<LI><A HREF="http://www.ietf.org/rfc/rfc3777.txt">IAB and IESG Selection, Confirmation, and Recall Process: Operation of the Nominating and Recall Committees (RFC 3777) (Also BCP10)</A>
<LI><A HREF="http://www.ietf.org/rfc/rfc3797.txt">Publicly Verifiable Nominations Committee (NomCom) Random Selection (RFC 3797)</A>
{% endblock %}

View file

@ -16,7 +16,7 @@ form.add-comment .actions {
{% block content %}
<h1>Add comment on {{ doc }}</h1>
<p>The comment will be added to the comment trail.</p>
<p>The comment will be added to the history trail.</p>
<form class="add-comment" action="" method="POST">
<table>
@ -24,7 +24,7 @@ form.add-comment .actions {
<tr>
<td></td>
<td class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Add comment"/>
</td>
</tr>

View file

@ -30,7 +30,7 @@ form.approve-ballot .announcement {
</div>
<div class="actions">
<a href="{% url doc_ballot_approvaltext name=doc.filename %}">Back</a>
<a href="{% url doc_ballot_approvaltext name=doc.name %}">Back</a>
{% ifequal action "to_announcement_list" %}
<input type="submit" value="Send out the announcement and close ballot"/>
{% endifequal %}

View file

@ -19,7 +19,7 @@ form #id_approval_text {
{{ approval_text_form.approval_text }}
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" name="save_approval_text" value="Save Approval Announcement Text" />
<input type="submit" name="regenerate_approval_text" value="Regenerate Approval Announcement Text" />
</div>
@ -29,7 +29,7 @@ form #id_approval_text {
{% if user|in_group:"Secretariat" %}
<p>
{% if can_announce %}
<a href="{% url doc_approve_ballot name=doc.filename %}">Approve ballot</a>
<a href="{% url doc_approve_ballot name=doc.name %}">Approve ballot</a>
{% endif %}
</p>
{% endif %}

View file

@ -16,13 +16,13 @@ There is no DISCUSS or COMMENT text associated with this position.
DISCUSS:
----------------------------------------------------------------------
{{ discuss|safe }}
{{ discuss|safe|wordwrap:73 }}
{% endif %}{% if comment %}----------------------------------------------------------------------
COMMENT:
----------------------------------------------------------------------
{{ comment|safe }}
{{ comment|safe|wordwrap:73 }}
{% endif %}
{% endautoescape %}

View file

@ -8,6 +8,6 @@
<p>Ballot has been sent out.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back to document</a>
<a href="{{ back_url }}">Back to document</a>
</div>
{% endblock %}

View file

@ -25,7 +25,7 @@ form #id_last_call_text {
{% endif %}
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" name="save_last_call_text" value="Save Last Call Text" />
<input type="submit" name="regenerate_last_call_text" value="Regenerate Last Call Text" />
{% if can_request_last_call and not need_intended_status %}
@ -38,7 +38,7 @@ form #id_last_call_text {
{% if user|in_group:"Secretariat" %}
<p>
{% if can_make_last_call %}
<a href="{% url doc_make_last_call name=doc.filename %}">Make Last Call</a>
<a href="{% url doc_make_last_call name=doc.name %}">Make Last Call</a>
{% endif %}
</p>

View file

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}Ballot writeup and notes for {{ doc }}{% endblock %}
{% block morecss %}
form #id_ballot_writeup {
width: 700px;
height: 600px;
}
{% endblock %}
{% block content %}
<h1>Ballot writeup and notes for {{ doc }}</h1>
<form action="" method="POST">
<p>(Technical Summary, Working Group Summary, Document Quality,
Personnel, RFC Editor Note, IRTF Note, IESG Note, IANA Note)</p>
<p>This text will be appended to all announcements and messages to
the IRTF or RFC Editor.</p>
{{ ballot_writeup_form.ballot_writeup }}
{% if not approval %}<p style="font-style:italic">Ballot cannot be issued before <a href="{% url doc_ballot_approvaltext name=doc.name %}">announcement text</a> is added.</p>{% endif %}
<div class="actions">
<a href="{{ back_url }}">Back</a>
<input type="submit" name="save_ballot_writeup" value="Save Ballot Writeup" />
<input style="margin-left: 8px" type="submit" {% if not approval %}disabled="disabled"{% endif %} name="issue_ballot" value="Save and {% if ballot_issued %}Re-{% endif %}Issue Ballot" />
</div>
</form>
{% endblock%}

View file

@ -0,0 +1,64 @@
{% extends "base.html" %}
{% block title %}Change state of {{ doc }}{% endblock %}
{% block morecss %}
form.change-state select {
width: 22em;
}
form.change-state .actions {
text-align: right;
padding-top: 10px;
}
.next-states,
.prev-state {
margin-bottom: 30px;
}
.next-states form,
.prev-state form {
display: inline;
margin-right: 10px;
}
{% endblock %}
{% block content %}
<h1>Change state of {{ doc }}</h1>
<p class="helptext">For help on the states, see the <a href="{% url help_states %}">state table</a>.</p>
<form class="change-state" action="" method="post">
<table>
{{ form.as_table }}
<tr>
<td colspan="2" class="actions">
<a href="{{ doc.get_absolute_url }}">Back</a>
<input type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
{% if next_states %}
<h3>Or jump directly to</h3>
<div class="next-states">
{% for n in next_states %}
<form action="" method="post">
<input type="hidden" name="state" value="{{ n.slug }}" />
<input type="submit" value="{{ n.name }}" />
</form>
{% endfor %}
</div>
{% endif %}
{% if prev_state %}
<h3>Or revert to previous state</h3>
<div class="prev-state">
<form action="" method="post">
<input type="hidden" name="state" value="{{ prev_state.slug }}" />
<input type="submit" value="Back to {{ prev_state.name }}" />
</form>
</div>
{% endif %}
{% endblock %}

View file

@ -11,7 +11,7 @@
<p>The ballot will then be on the IESG agenda of {{ telechat_date }}.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Defer ballot"/>
</div>
</form>

View file

@ -65,8 +65,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
</div>
{% else %}
{% if c.info.dontmolest %}
{{ c.info.text|safe }}
{% else %}
{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
{% endif %}
{% endif %}
</td>
{% endif %}

View file

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block title %}Edit info on {{ doc }}{% endblock %}
{% block morecss %}
form.edit-info #id_state_change_notice_to {
width: 600px;
}
form.edit-info #id_note {
width: 600px;
height: 150px;
}
form.edit-info .actions {
padding-top: 20px;
}
{% endblock %}
{% block content %}
{% load ietf_filters %}
<h1>Edit info on {{ doc }}</h1>
<form class="edit-info" action="" method="POST">
<table>
{% for field in form.standard_fields %}
<tr>
<th>{{ field.label_tag }}:</th>
<td>{{ field }}
{% ifequal field.name "telechat_date" %}{{ form.returning_item }} {{ form.returning_item.label_tag }} {{ form.returning_item.errors }}{% endifequal %}
{% ifequal field.name "ad" %}
{% if user|in_group:"Area_Director" %}
<label><input type="checkbox" name="ad" value="{{ login.pk }}" /> Assign to me</label>
{% endif %}
{% endifequal %}
{% if field.help_text %}<div class="help">{{ field.help_text }}</div>{% endif %}
{{ field.errors }}</td>
</tr>
{% endfor %}
<tr>
<td></td>
<td class="actions">
<a href="{{ doc.get_absolute_url }}">Back</a>
<input type="submit" value="Save"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -0,0 +1,80 @@
{% extends "base.html" %}
{% block title %}Change position for {{ ad.name }} on {{ doc }}{% endblock %}
{% block morecss %}
div.ballot-deferred {
margin-top: 8px;
margin-bottom: 8px;
}
form.position-form .position ul {
padding: 0;
margin: 0;
}
form.position-form .position li {
list-style-type: none;
float: left;
padding-right: 10px;
}
form.position-form .last-edited {
font-style: italic;
}
form.position-form .discuss-text {
padding-top: 20px
}
form.position-form #id_discuss_text,
form.position-form #id_comment_text {
width: 700px;
height: 250px;
}
form.position-form .comment-text {
margin-top: 20px;
}
{% endblock %}
{% block content %}
<h1>Change position for {{ ad.name }} {{ doc }}</h1>
{% if ballot_deferred %}
<div class="ballot-deferred">Ballot deferred by {{ ballot_deferred.by }} on {{ ballot_deferred.time|date:"Y-m-d" }}.</div>
{% endif %}
<form class="position-form" action="" method="POST">
<div>
<span class="position">{{ form.position }}</span>
<span class="actions">
<input type="submit" name="send_mail" value="Save and send email"/>
<input type="submit" value="Save"/>
{% if ballot_deferred %}<input type="submit" name="Undefer" value="Undefer"/>{% else %}<input type="submit" name="Defer" value="Defer"/>{% endif %}
</span>
</div>
<div style="clear:left"></div>
<div class="discuss-widgets" {% ifnotequal form.position.initial "discuss" %}style="display:none"{% endifnotequal %}>
<div class="discuss-text">
{{ form.discuss.label_tag }}:
{% if old_pos and old_pos.discuss_time %}<span class="last-edited">(last edited {{ old_pos.discuss_time }})</span>{% endif %}
</div>
{{ form.discuss.errors }}
{{ form.discuss }}
</div>
<div class="comment-text">
{{ form.comment.label_tag }}:
{% if old_pos and old_pos.comment_time %}<span class="last-edited">(last edited {{ old_pos.comment_time }}){% endif %}</span>
</div>
{{ form.comment }}
<div class="actions">
<a href="{{ return_to_url }}">Back</a>
</div>
{{ form.return_to_url }}
</form>
{% endblock %}
{% block content_end %}
<script type="text/javascript" src="/js/doc-edit-position.js"></script>
{% endblock %}

View file

@ -0,0 +1,7 @@
{% filter wordwrap:73 %}This Internet-Draft, {{ doc.name }}-{{ doc.rev }}.txt, has expired, and has been deleted from the Internet-Drafts directory. An Internet-Draft expires {{ expire_days }} days from the date that it is posted unless it is replaced by an updated version, or the Secretariat has been notified that the document is under official review by the IESG or has been passed to the RFC Editor for review and/or publication as an RFC. This Internet-Draft was not published as an RFC.
Internet-Drafts are not archival documents, and copies of Internet-Drafts that have been deleted from the directory are not available. The Secretariat does not have any information regarding the future plans of the author{{ authors|pluralize}} or working group, if applicable, with respect to this deleted Internet-Draft. For more information, or to request a copy of the document, please contact the author{{ authors|pluralize}} directly.{% endfilter %}
Draft Author{{ authors|pluralize}}:
{% for name, email in authors %}{{ name }}<{{ email }}>
{% endfor %}

View file

@ -0,0 +1,7 @@
{% load ietf_filters %}{% autoescape off %}The following draft will expire soon:
Name: {{ doc.name|clean_whitespace }}
Title: {{ doc.title}}
State: {{ state }}
Expires: {{ expiration }} (in {{ expiration|timeuntil }})
{% endautoescape %}

View file

@ -1,5 +1,5 @@
{{ doc.file_tag|safe }} was just expired.
This draft is in the state {{ doc.idstate }} in ID Tracker.
This draft is in the state "{{ state }}" in the ID Tracker.
Thanks,

View file

@ -0,0 +1,40 @@
{% autoescape off %}To: Internet Engineering Steering Group <iesg@ietf.org>
From: IESG Secretary <iesg-secretary@ietf.org>
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
Subject: Evaluation: {{ doc.file_tag }} to {{ status }}
{% filter wordwrap:73 %}Evaluation for {{ doc.file_tag }} can be found at {{ doc_url }}
{% if last_call_expires %}Last call to expire on: {{ last_call_expires }}
{% endif %}{% endfilter %}
Please return the full line with your position.
Yes No-Objection Discuss Abstain
{% for fmt in active_ad_positions %}{{ fmt }}
{% endfor %}{% if inactive_ad_positions %}
{% for fmt in inactive_ad_positions %}{{ fmt }}
{% endfor %}{% endif %}
"Yes" or "No-Objection" positions from 2/3 of non-recused ADs,
with no "Discuss" positions, are needed for approval.
DISCUSSES AND COMMENTS
======================
{% filter wordwrap:79 %}{% for pos in ad_feedback %}{{ pos.ad.get_name }}:
{% if pos.discuss %}Discuss [{{ pos.discuss_time|date:"Y-m-d" }}]:
{{ pos.discuss }}
{% endif %}{% if pos.comment %}Comment [{{ pos.comment_time|date:"Y-m-d" }}]:
{{ pos.comment }}
{% endif %}
{% endfor %}{% endfilter %}
---- following is a DRAFT of message to be sent AFTER approval ---
{{ approval_text }}{% if ballot_writeup %}
{{ ballot_writeup }}
{% endif %}
{% endautoescape%}

View file

@ -12,6 +12,6 @@ secretariat takes appropriate steps. This may take up to one business
day, as it involves a person taking action.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ url }}">Back</a>
</div>
{% endblock %}

View file

@ -0,0 +1,39 @@
{% extends "base.html" %}
{% block title %}Make Last Call for {{ doc.name }}{% endblock %}
{% block morecss %}
form.approve-ballot pre {
margin: 0;
padding: 4px;
border-top: 4px solid #eee;
border-bottom: 4px solid #eee;
}
form.approve-ballot .announcement {
overflow-x: auto;
overflow-y: scroll;
width: 800px;
height: 400px;
border: 1px solid #bbb;
}
{% endblock %}
{% block content %}
<h1>Make Last Call for {{ doc.name }}</h1>
<p>Make last call for following draft:</p>
<div>{{ doc.file_tag }} ({{ doc.group.acronym }}) - {{ doc.intended_std_level.name }}</div>
<form style="margin-top:20px" action="" method="POST">
<table>
{{ form.as_table }}
</table>
<div class="actions">
<a href="{{ doc.get_absolute_url }}">Back</a>
<input type="reset" value="Reset">
<input type="submit" value="Make Last Call"/>
</div>
</form>
{% endblock %}

View file

@ -12,7 +12,7 @@
I-D.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Request resurrect"/>
</div>
</form>

View file

@ -11,7 +11,7 @@
<p>This will change the status to Active{% if doc.idinternal.resurrect_requested_by %} and email a notice to {{ doc.idinternal.resurrect_requested_by }}{% endif %}.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Resurrect"/>
</div>
</form>

View file

@ -1,5 +1,5 @@
{% autoescape off %}As you requsted, the Internet Draft {{ doc.file_tag|safe }}
{% autoescape off %}As you requsted, the Internet Draft {{ doc.file_tag }}
has been resurrected.
ID Tracker URL: {{ url|safe }}
ID Tracker URL: {{ url }}
{% endautoescape %}

View file

@ -1,4 +1,4 @@
{% autoescape off %}I-D that is requested to be resurrected: {{ doc.file_tag|safe }}
Requested by: {{ by|safe }}
ID Tracker URL: {{ url|safe }}
{% autoescape off %}I-D that is requested to be resurrected: {{ doc.file_tag }}
Requested by: {{ by }}
ID Tracker URL: {{ url }}
{% endautoescape %}

View file

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% load ietf_filters %}
{% block title %}Send ballot position email for {{ ad }}{% endblock %}
{% block morecss %}
form.send-ballot pre {
margin: 0;
padding: 4px;
border-top: 4px solid #eee;
border-bottom: 4px solid #eee;
}
{% endblock %}
{% block content %}
<h1>Send ballot position email for {{ ad }}</h1>
<form class="send-ballot" action="" method="POST">
<table>
<tr><th>From:</th> <td>{{ frm }}</td></tr>
<tr><th>To:</th> <td>{{ to }}</td></tr>
<tr>
<th>Cc:<br/>
<span class="help">separated<br/> by comma</span></th>
<td><input type="text" name="cc" value="" size="75" /><br/>
{% if doc.notify %}
<label>
<input type="checkbox" name="cc_state_change" value="1" checked="checked" />
{{ doc.notify }}
</label>
{% endif %}
</td>
</tr>
<tr><th>Subject:</th> <td>{{ subject }}</td></tr>
<tr>
<th>Body:</th>
<td><pre>{{ body|wrap_text }}</pre></td>
</tr>
<tr>
<td></td>
<td class="actions">
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Send"/>
</td>
</tr>
</table>
</form>
{% endblock %}

View file

@ -11,7 +11,7 @@
<p>The ballot will then be on the IESG agenda of {{ telechat_date }}.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" value="Undefer ballot"/>
</div>
</form>

View file

@ -48,11 +48,20 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
{% endif %}{% for ipr in doc.obj.draft.ipr.all %}{% ifequal ipr.ipr.status 1 %} <br>IPR: <a href="http://datatracker.ietf.org/ipr/{{ ipr.ipr.ipr_id }}/">{{ ipr.ipr.title|escape }}</a>{% endifequal %}
{% endfor %} {% if doc.obj.ballot.active %}<br><b>Discusses/comments</b> (from <a href="http://datatracker.ietf.org/idtracker/ballot/{{ doc.obj.ballot.ballot}}/">ballot {{doc.obj.ballot.ballot }})</a>:
<ol>
{% if USE_DB_REDESIGN_PROXY_CLASSES %}
{% for p in doc.obj.active_positions|dictsort:"ad.name" %}{% if p.pos %}{% ifequal p.pos.pos_id "discuss" %}<li><a href="#{{doc.obj.document.filename}}+{{p.pos.ad|slugify}}+discuss">{{ p.pos.ad.name }}: Discuss [{{ p.pos.discuss_time.date }}]</a>:
<br>...
{% endifequal %}{% if p.pos.comment %} <li><a href="#{{doc.obj.document.filename}}+{{position.ad|slugify}}+comment">{{ p.pos.ad.name }}: Comment [{{ p.pos.comment_time.date }}]</a>:
<br>...
{% endif %}{% endif %}{% endfor %}
{% else %}
{% for position in doc.obj.ballot.positions.all|dictsort:"ad.last_name" %}{% ifequal position.discuss 1 %} <li><a href="#{{doc.obj.document.filename}}+{{position.ad|slugify}}+discuss">{{ position.ad }}:{% for item in doc.obj.ballot.discusses.all %}{% ifequal position.ad item.ad %} Discuss [{{ item.date }}]</a>:
<br>...
{% endifequal %}{% endfor %}{% endifequal %}{% for item in doc.obj.ballot.comments.all %}{% ifequal position.ad item.ad %} <li><a href="#{{doc.obj.document.filename}}+{{position.ad|slugify}}+comment">{{ position.ad }}: Comment [{{ item.date }}]</a>:
<br>...
{% endifequal %}{% endfor %}{% endfor %} </ol>
{% endifequal %}{% endfor %}{% endfor %}
{% endif%}
</ol>
{%endif %} <p><b>Telechat</b>:
<ul>
<li>...

View file

@ -170,7 +170,7 @@ Template for {{ section_list.disclosure_type }}" where the submitter provided
<tr class="{% cycle row_parity %}"><td class="iprlabel">I-D Filenames (draft-...):</td><td class="iprdata">{{ ipr.draftlist }}</td></tr>
{% else %}
{% for doc in ipr.drafts.all %}
<tr class="{% cycle row_parity %}"><td class="iprlabel">Internet-Draft:</td><td class="iprdata">"{{ doc.document.title }}"<br />({{ doc.document.filename }}-{{ doc.revision }})</td></tr>
<tr class="{% cycle row_parity %}"><td class="iprlabel">Internet-Draft:</td><td class="iprdata">"{{ doc.document.title }}"<br />({{ doc.document.filename }}{% if doc.revision %}-{{ doc.revision }}{% endif %})</td></tr>
{% endfor %}
{% endif %}
{% if ipr.other_designations %}

View file

@ -24,7 +24,7 @@
<td colspan="3">
{% block intro_prefix %}IPR that was submitted by <b><i>{{ q }}</i></b>, and{% endblock %}
{% block related %}
{% if not ipr.drafts.count and not ipr.rfcs.count %}
{% if not ipr.docs %}
is not related to a specific IETF contribution.
{% else %}
is related to

View file

@ -0,0 +1,31 @@
{# Copyright The IETF Trust 2007, All Rights Reserved #}
{% extends "base.html" %}
{% block morecss %}
table.userProfile th {
text-align: left;
}
{% endblock %}
{% block title %}Profile for {{ user }}{% endblock %}
{% block content %}
<h1>User information</h1>
<table class="userProfile">
<tr>
<th>User name:</th>
<td>{{ user.username }}</td>
</tr>
<tr>
<th>Person:</th>
<td>{{ person.name|default:"?" }}</td>
</tr>
{% for role in roles %}
<tr>
<th>{% if forloop.first %}Roles:{% endif %}</th>
<td>{{ role.name }} in {{ role.group.name }} ({{ role.group.type }})</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View file

@ -2,14 +2,9 @@
IETF Working Group Summary (By Acronym)
The following Area Abreviations are used in this document
The following Area Abbreviations are used in this document
{% for area in area_list %}
{{ area }} - {{ area.area_acronym.name }}{% endfor %}
{{ area|upper }} - {{ area.area_acronym.name }}{% endfor %}
{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.start_date %}
{{ wg.group_acronym.name|safe }} ({{ wg }}) -- {{ wg.area.area|upper }}
{% for chair in wg.wgchair_set.all %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person|safe }} <{{ chair.person.email.1 }}>
{% endfor %} WG Mail: {{ wg.email_address }}
To Join: {{ wg.email_subscribe }}{%if wg.email_keyword %}
In Body: {{ wg.email_keyword|safe }}{% endif %}
Archive: {{ wg.email_archive }}
{% endif %}{% endfor %}
{% include "wginfo/wg_summary.txt" %}{% endif %}{% endfor %}

View file

@ -5,9 +5,4 @@
{{ ad.person }} <{{ ad.person.email.1 }}>{% endfor %}
{% endif %}{% if wg.start_date %}
{{ wg.group_acronym.name|safe }} ({{ wg }})
{% for chair in wg.wgchair_set.all %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person|safe }} <{{ chair.person.email.1 }}>
{% endfor %} WG Mail: {{ wg.email_address }}
To Join: {{ wg.email_subscribe }}{%if wg.email_keyword %}
In Body: {{ wg.email_keyword|safe }}{% endif %}
Archive: {{ wg.email_archive }}
{% endif %}{% endifequal %}{% endfor %}{% endfor %}
{% include "wginfo/wg_summary.txt" %}{% endif %}{% endifequal %}{% endfor %}{% endfor %}

View file

@ -1,4 +1,4 @@
{% load ietf_filters %}{{wg.group_acronym.name|safe}} ({{wg}})
{% if USE_DB_REDESIGN_PROXY_CLASSES %}{% include "wginfo/wg-charterREDESIGN.txt" %}{% else %}{% load ietf_filters %}{{wg.group_acronym.name|safe}} ({{wg}})
{{ wg.group_acronym.name|dashify }}{{ wg.group_acronym.acronym|dashify }}---
Charter
@ -44,4 +44,4 @@ Internet-Drafts:
* {{obs.action}} RFC{{obs.rfc_acted_on_id}}{% endfor %}{% for obs in rfc.obsoleted_by%}
* {%ifequal obs.action 'Obsoletes'%}OBSOLETED BY{%else%}Updated by{%endifequal%} RFC{{obs.rfc_id}}{% endfor %}
{%endfor%}
{%else%}No Requests for Comments{% endif %}
{%else%}No Requests for Comments{% endif %}{% endif %}

View file

@ -0,0 +1,47 @@
{% load ietf_filters %}{{wg.name|safe}} ({{wg.acronym}})
{{ wg.name|dashify }}{{ wg.acronym|dashify }}---
Charter
Last Modified: {{ wg.time.date }}
Current Status: {{ wg.state.name }}
Chair{{ wg.chairs|pluralize }}:
{% for chair in wg.chairs %} {{ chair.person.name|safe }} <{{chair.address}}>
{% endfor %}
{{wg.area.area.area_acronym.name}} Directors:
{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.person.email.1}}>
{% endfor %}
{{wg.area.area.area_acronym.name}} Advisor:
{{ wg.areadirector.person.name|safe }} <{{wg.areadirector.address}}>
{% if wg.techadvisors %}
Tech Advisor{{ wg.techadvisors|pluralize }}:
{% for techadvisor in wg.techadvisors %} {{ techadvisor.person.name|safe }} <{{techadvisor.address}}>
{% endfor %}{% endif %}{% if wg.editors %}
Editor{{ wg.editors|pluralize }}:
{% for editor in wg.editors %} {{ editor.person.name|safe }} <{{editor.person.address}}>
{% endfor %}{% endif %}{% if wg.secretaries %}
Secretar{{ wg.secretaries|pluralize:"y,ies" }}:
{% for secretary in wg.secretaries %} {{ secretary.person.name|safe }} <{{secretary.person.address}}>
{% endfor %}{% endif %}
Mailing Lists:
General Discussion: {{ wg.email_address }}
To Subscribe: {{ wg.email_subscribe }}
Archive: {{ wg.email_archive }}
Description of Working Group:
{{ wg.charter_text|indent|safe }}
Goals and Milestones:
{% for milestone in wg.milestones %} {% if milestone.done %}Done {% else %}{{ milestone.expected_due_date|date:"M Y" }}{% endif %} - {{ milestone.desc|safe }}
{% endfor %}
Internet-Drafts:
{% for alias in wg.drafts %} - {{alias.document.title|safe}} [{{alias.name}}-{{alias.document.rev}}] ({{ alias.document.pages }} pages)
{% endfor %}
{% if wg.rfcs %}Requests for Comments:
{% for alias in wg.rfcs %} {{ alias.name.upper }}: {{ alias.document.title|safe}} ({{ alias.document.pages }} pages){% for r in alias.rel %}
* {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %}
* {% ifequal r.relationsship "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endifequal %} {{ r.source.canonical_name|upper }}{% endfor %}
{%endfor%}
{%else%}No Requests for Comments{% endif %}

View file

@ -0,0 +1,92 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2009, All Rights Reserved #}
{% comment %}
Portion Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the Nokia Corporation and/or its
subsidiary(-ies) nor the names of its contributors may be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
{% block title %}Active IETF Working Groups{% endblock %}
{% block morecss %}
.ietf-wg-table { width: 100%; max-width:50em; }
.ietf-wg-table tr { vertical-align:top; }
{% endblock morecss %}
{% block content %}
<h1>Active IETF Working Groups</h1>
<p>See also: <a href="http://www.ietf.org/wg/concluded/">Concluded
Working Groups (www.ietf.org)</a>, <a href="http://tools.ietf.org/wg/concluded/">Concluded Working Groups (tools.ietf.org)</a>, <a href="http://www.ietf.org/dyn/wg/charter/history/">Historic Charters</a>.</p>
{% for area in areas %}
<h2 class="ietf-divider" id="{{ area.name|cut:" " }}">{{ area.name }}</h2>
{% for ad in area.ads %}
{% if forloop.first %}
<p>Area Director{{ forloop.revcounter|pluralize }}:</p>
<p style="margin-left: 2em">
{% endif %}
<a href="mailto:{{ ad.address }}">{{ ad.person.name }} &lt;{{ ad.address }}&gt;</a>{% if not forloop.last %}<br/>{% endif %}
{% if forloop.last %}
</p>
{% endif %}
{% endfor %}
{% for url in area.urls %}
{% if forloop.first %}
<p>Area Specific Web Page{{ forloop.revcounter|pluralize}}:</p>
<p style="margin-left: 2em">
{% endif %}
<a href="{{url.url}}">{{ url.name }}</a>{% if not forloop.last %}<br/>{% endif %}
{% if forloop.last %}
</p>
{% endif %}
{% endfor %}
{% for wg in area.wgs %}
{% if forloop.first %}
<p>Active Working Groups:</p>
<div style="margin-left:2em;">
<table class="ietf-wg-table">
{% endif %}
<tr><td width="10%;"><a href="/wg/{{ wg.acronym }}/">{{ wg.acronym }}</a></td><td width="50%">{{ wg.name }}</td>
<td width="39%">{% for chair in wg.chairs %}<a href="mailto:{{ chair.address }}">{{ chair.person.name }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</td></tr>
{% if forloop.last %}
</table>
</div>
{% endif %}
{% empty %}
<p>No Active Working Groups</p>
{% endfor %}{# wg #}
{% endfor %}{# area #}
{% endblock %}

View file

@ -0,0 +1,137 @@
{% extends "wginfo/wg_base.html" %}
{% comment %}
Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the Nokia Corporation and/or its
subsidiary(-ies) nor the names of its contributors may be used
to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% endcomment %}
{% load ietf_filters %}
{% block wg_titledetail %}Charter{% endblock %}
{% block wg_content %}
<div class="ietf-box ietf-wg-details">
{% if concluded %}
<span class="ietf-concluded-warning">Note: The data for concluded WGs
is occasionally incorrect.</span>
{% endif %}
<table>
<tr>
<td colspan="2">
<b>Personnel</b>
</td>
</tr>
<tr valign="top">
<td style="width:14ex;">Chair{{ wg.chairs|pluralize }}:</td>
<td>
{% for chair in wg.chairs %}
<a href="mailto:{{ chair.address }}">{{ chair.person.name }} &lt;{{ chair.address }}&gt;</a><br/>
{% endfor %}
</td></tr>
<tr><td>Area Director:</td>
<td>
{% if not wg.ad %}?{% else %}
<a href="mailto:{{ wg.ad_email }}">{{ wg.ad.name }} &lt;{{ wg.ad_email }}&gt;</a>{% endif %}
</td>
</tr>
{% if wg.techadvisors %}
<tr>
<td>Tech Advisor{{ wg.techadvisors|pluralize }}:</td>
<td>
{% for techadvisor in wg.techadvisors %}
<a href="mailto:{{ techadvisor.address }}">{{ techadvisor.person.name }} &lt;{{ techadvisor.address }}&gt;</a><br/>
{% endfor %}
</td></tr>
{% endif %}
{% if wg.editors %}
<td>Editor{{ wg.editors|pluralize }}:</td>
<td>
{% for editor in wg.editors %}
<a href="mailto:{{ editor.address }}">{{ editor.person.name }} &lt;{{ editor.address }}&gt;</a><br/>
{% endfor %}
</td></tr>
{% endif %}
{% if wg.secretaries %}
<tr><td>Secretar{{ wg.secretaries|pluralize:"y,ies" }}:</td>
<td>
{% for secretary in wg.secretaries %}
<a href="mailto:{{ secretary.address }}">{{ secretary.person.name }} &lt;{{ secretary.address }}&gt;</a><br/>
{% endfor %}
</td></tr>
{% endif %}
<tr>
<td colspan="2">
<br/><b>Mailing List</b>
</td>
</tr>
<tr><td>Address:</td><td>{{ wg.email_address|urlize }}</td></tr>
<tr><td>To Subscribe:</td><td>{{ wg.email_subscribe|urlize }}</td></tr>
<tr><td>Archive:</td><td>{{ wg.clean_email_archive|urlize }}</td></tr>
{% if not concluded %}
<tr>
<td colspan="2">
<br/><b>Jabber Chat</b>
</td>
</tr>
<tr><td>Room Address:</td><td><a href="xmpp:{{wg}}@jabber.ietf.org">xmpp:{{wg}}@jabber.ietf.org</a></td></tr>
<tr><td>Logs:</td><td><a href="http://jabber.ietf.org/logs/{{wg}}/">http://jabber.ietf.org/logs/{{wg}}/</a></td></tr>
{% endif %}
</table>
</div>
{% if wg.additional_urls %}
<p>In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at:
{% for url in wg.additional_urls %}
<a href="{{ url.url }}">{{ url.description}}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
{% endif %}
<h2>Description of Working Group</h2>
<p>{{ wg.charter_text|escape|format_charter|safe }}</p>
<h2>Goals and Milestones</h2>
<table>
{% for milestone in wg.milestones %}
<tr>
<td width="80px">
{% if milestone.done %}Done{% else %}{{ milestone.expected_due_date|date:"M Y" }}{% endif %}
</td>
<td>{{ milestone.desc|escape }}
</td></tr>
{% endfor %}
</table>
{% endblock wg_content %}

View file

@ -0,0 +1,5 @@
{% for chair in wg.wgchair_set.all %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person|safe }} <{{ chair.person.email.1 }}>
{% endfor %} WG Mail: {{ wg.email_address }}
To Join: {{ wg.email_subscribe }}{%if wg.email_keyword %}
In Body: {{ wg.email_keyword|safe }}{% endif %}
Archive: {{ wg.email_archive }}

View file

@ -37,6 +37,9 @@ sitemaps = {
'nomcom-announcements': NOMCOMAnnouncementsMap,
}
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
del sitemaps['drafts'] # not needed, overlaps sitemaps['idtracker']
urlpatterns = patterns('',
(r'^feed/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
{ 'feed_dict': feeds}),

171
ietf/utils/test_data.py Normal file
View file

@ -0,0 +1,171 @@
from django.contrib.auth.models import User
from ietf.iesg.models import TelechatDates, WGAction
from redesign.doc.models import *
from redesign.name.models import *
from redesign.group.models import *
from redesign.person.models import *
def make_test_data():
# groups
secretariat = Group.objects.create(
name="Secretariat",
acronym="secretariat",
state_id="active",
type_id="ietf",
parent=None)
area = Group.objects.create(
name="Far Future",
acronym="farfut",
state_id="active",
type_id="area",
parent=None)
group = Group.objects.create(
name="Martian Special Interest Group",
acronym="mars",
state_id="active",
type_id="wg",
parent=area,
)
# persons
# system
system_person = Person.objects.create(
id=0, # special value
name="(System)",
ascii="(System)",
address="",
)
if system_person.id != 0: # work around bug in Django
Person.objects.filter(id=system_person.id).update(id=0)
system_person = Person.objects.get(id=0)
Alias.objects.get_or_create(person=system_person, name=system_person.name)
Email.objects.get_or_create(address="", person=system_person)
# ad
u = User.objects.create(username="ad")
ad = p = Person.objects.create(
name="Aread Irector",
ascii="Aread Irector",
user=u)
email = Email.objects.create(
address="aread@ietf.org",
person=p)
Role.objects.create(
name_id="ad",
group=area,
email=email)
# create a bunch of ads for swarm tests
for i in range(1, 10):
u = User.objects.create(username="ad%s" % i)
p = Person.objects.create(
name="Ad No%s" % i,
ascii="Ad No%s" % i,
user=u)
email = Email.objects.create(
address="ad%s@ietf.org" % i,
person=p)
Role.objects.create(
name_id="ad" if i <= 5 else "ex-ad",
group=area,
email=email)
# group chair
p = Person.objects.create(
name="WG Chair Man",
ascii="WG Chair Man",
)
wgchair = Email.objects.create(
address="wgchairman@ietf.org",
person=p)
Role.objects.create(
name_id="chair",
group=group,
email=wgchair,
)
# secretary
u = User.objects.create(username="secretary")
p = Person.objects.create(
name="Sec Retary",
ascii="Sec Retary",
user=u)
email = Email.objects.create(
address="sec.retary@ietf.org",
person=p)
Role.objects.create(
name_id="secr",
group=secretariat,
email=email,
)
# draft
draft = Document.objects.create(
name="draft-ietf-test",
time=datetime.datetime.now(),
type_id="draft",
title="Optimizing Martian Network Topologies",
state_id="active",
iesg_state_id="pub-req",
stream_id="ietf",
group=group,
abstract="Techniques for achieving near-optimal Martian networks.",
rev="01",
pages=2,
intended_std_level_id="ps",
ad=ad,
notify="aliens@example.mars",
note="",
)
DocAlias.objects.create(
document=draft,
name=draft.name,
)
DocumentAuthor.objects.create(
document=draft,
author=Email.objects.get(address="aread@ietf.org"),
order=1
)
# draft has only one event
DocEvent.objects.create(
type="started_iesg_process",
by=ad,
doc=draft,
desc="Added draft",
)
# telechat dates
t = datetime.date.today()
dates = TelechatDates(date1=t,
date2=t + datetime.timedelta(days=7),
date3=t + datetime.timedelta(days=14),
date4=t + datetime.timedelta(days=21),
)
super(dates.__class__, dates).save(force_insert=True) # work-around hard-coded save block
# WG Actions
group = Group.objects.create(
name="Asteroid Mining Equipment Standardization Group",
acronym="ames",
state_id="proposed",
type_id="wg",
parent=area,
)
WGAction.objects.create(
pk=group.pk,
note="",
status_date=datetime.date.today(),
agenda=1,
token_name="Aread",
category=13,
telechat_date=dates.date2
)
return draft

View file

@ -38,7 +38,7 @@ import django
from django.db import connection
from django.test import TestCase
from django.test.client import Client
import ietf
import ietf.settings
from django.conf import settings
from datetime import datetime
import urllib2 as urllib
@ -117,7 +117,10 @@ class SimpleUrlTestCase(TestCase,RealDatabaseTest):
self.tearDownRealDatabase()
def doTestUrls(self, test_filename):
filename = os.path.dirname(os.path.abspath(test_filename))+"/testurl.list"
if test_filename.endswith(".list"):
filename = test_filename
else:
filename = os.path.dirname(os.path.abspath(test_filename))+"/testurl.list"
print " Reading "+filename
tuples = read_testurls(filename)
failures = 0

Some files were not shown because too many files have changed in this diff Show more