Merged from log:branch/iesg-tracker@2571: IOLA's port of cron-scripts.
- Legacy-Id: 2578
This commit is contained in:
parent
7254611df4
commit
eafcdccd3d
|
@ -74,7 +74,7 @@ class ScheduledAnnouncement(models.Model):
|
|||
cc_val = models.TextField(blank=True,null=True)
|
||||
body = models.TextField(blank=True)
|
||||
actual_sent_date = models.DateField(null=True, blank=True)
|
||||
actual_sent_time = models.CharField(blank=True, max_length=50)
|
||||
actual_sent_time = models.CharField(blank=True, max_length=50) # should be time, but database contains oddities
|
||||
first_q = models.IntegerField(null=True, blank=True)
|
||||
second_q = models.IntegerField(null=True, blank=True)
|
||||
note = models.TextField(blank=True,null=True)
|
||||
|
|
32
ietf/announcements/send_scheduled.py
Normal file
32
ietf/announcements/send_scheduled.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
import re, datetime, email
|
||||
|
||||
from ietf.utils.mail import send_mail_text, send_mail_mime
|
||||
|
||||
first_dot_on_line_re = re.compile(r'^\.', re.MULTILINE)
|
||||
|
||||
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
|
||||
|
||||
content_type = announcement.content_type.lower()
|
||||
if not content_type or 'text/plain' in content_type:
|
||||
send_mail_text(None, announcement.to_val, announcement.from_val, announcement.subject,
|
||||
body, cc=announcement.cc_val, bcc=announcement.bcc_val)
|
||||
elif 'multipart' in content_type:
|
||||
# make body a real message so we can parse it
|
||||
body = ("MIME-Version: 1.0\r\nContent-Type: %s\r\n" % content_type) + body
|
||||
|
||||
msg = email.message_from_string(body.encode("utf-8"))
|
||||
send_mail_mime(None, announcement.to_val, announcement.from_val, announcement.subject,
|
||||
msg, cc=announcement.cc_val, bcc=announcement.bcc_val)
|
||||
|
||||
now = datetime.datetime.now()
|
||||
announcement.actual_sent_date = now.date()
|
||||
announcement.actual_sent_time = str(now.time())
|
||||
announcement.mail_sent = True
|
||||
announcement.save()
|
|
@ -1,5 +1,9 @@
|
|||
import django.test
|
||||
|
||||
from ietf.utils.test_utils import SimpleUrlTestCase, canonicalize_sitemap
|
||||
from ietf.utils.test_runner import mail_outbox
|
||||
|
||||
from ietf.announcements.models import ScheduledAnnouncement
|
||||
|
||||
class AnnouncementsUrlTestCase(SimpleUrlTestCase):
|
||||
def testUrls(self):
|
||||
|
@ -10,3 +14,46 @@ class AnnouncementsUrlTestCase(SimpleUrlTestCase):
|
|||
else:
|
||||
return content
|
||||
|
||||
class SendScheduledAnnouncementsTestCase(django.test.TestCase):
|
||||
def test_send_plain_announcement(self):
|
||||
a = ScheduledAnnouncement.objects.create(
|
||||
mail_sent=False,
|
||||
subject="This is a test",
|
||||
to_val="test@example.com",
|
||||
from_val="testmonkey@example.com",
|
||||
cc_val="cc.a@example.com, cc.b@example.com",
|
||||
bcc_val="bcc@example.com",
|
||||
body="Hello World!",
|
||||
content_type="",
|
||||
)
|
||||
|
||||
mailbox_before = len(mail_outbox)
|
||||
|
||||
from ietf.announcements.send_scheduled import send_scheduled_announcement
|
||||
send_scheduled_announcement(a)
|
||||
|
||||
self.assertEquals(len(mail_outbox), mailbox_before + 1)
|
||||
self.assertTrue("This is a test" in mail_outbox[-1]["Subject"])
|
||||
self.assertTrue(ScheduledAnnouncement.objects.get(id=a.id).mail_sent)
|
||||
|
||||
def test_send_mime_announcement(self):
|
||||
a = ScheduledAnnouncement.objects.create(
|
||||
mail_sent=False,
|
||||
subject="This is a test",
|
||||
to_val="test@example.com",
|
||||
from_val="testmonkey@example.com",
|
||||
cc_val="cc.a@example.com, cc.b@example.com",
|
||||
bcc_val="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"',
|
||||
)
|
||||
|
||||
mailbox_before = len(mail_outbox)
|
||||
|
||||
from ietf.announcements.send_scheduled import send_scheduled_announcement
|
||||
send_scheduled_announcement(a)
|
||||
|
||||
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(ScheduledAnnouncement.objects.get(id=a.id).mail_sent)
|
||||
|
|
20
ietf/bin/expire-ids
Executable file
20
ietf/bin/expire-ids
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import datetime, os
|
||||
import syslog
|
||||
|
||||
from ietf import settings
|
||||
from django.core import management
|
||||
management.setup_environ(settings)
|
||||
|
||||
syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_LOCAL0)
|
||||
|
||||
from ietf.idrfc.expire import *
|
||||
|
||||
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 ""))
|
||||
|
||||
clean_up_id_files()
|
17
ietf/bin/expire-last-calls
Executable file
17
ietf/bin/expire-last-calls
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import datetime, os
|
||||
import syslog
|
||||
|
||||
from ietf import settings
|
||||
from django.core import management
|
||||
management.setup_environ(settings)
|
||||
|
||||
syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_LOCAL0)
|
||||
|
||||
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))
|
|
@ -1,33 +1,42 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
from django.core.management import setup_environ
|
||||
|
||||
import datetime, os, sys
|
||||
import syslog
|
||||
|
||||
from ietf import settings
|
||||
from django.core import management
|
||||
management.setup_environ(settings)
|
||||
|
||||
syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_LOCAL0)
|
||||
|
||||
setup_environ(settings)
|
||||
|
||||
from django.db.models import Q
|
||||
from django.db import connection
|
||||
from ietf.announcements.models import ScheduledAnnouncement
|
||||
from ietf.utils.mail import send_mail_text
|
||||
import datetime
|
||||
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'):
|
||||
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"
|
||||
print "'specific' means all not sent that are due to be sent"
|
||||
sys.exit(1)
|
||||
|
||||
mode = sys.argv[1]
|
||||
|
||||
now = datetime.datetime.now()
|
||||
candidates = ScheduledAnnouncement.objects.filter(to_be_sent_date__isnull=True) | ScheduledAnnouncement.objects.exclude(to_be_sent_date__gt=now.date())
|
||||
candidates = candidates.filter(mail_sent=False) #.exclude(to_be_sent_date__gt=now.date())
|
||||
for c in candidates:
|
||||
if c.to_be_sent_time:
|
||||
tbst = datetime.time( *[int(t) for t in c.to_be_sent_time.split(":")] )
|
||||
if tbst > now.time():
|
||||
# To be sent today, but not yet.
|
||||
continue
|
||||
extra={}
|
||||
if c.content_type:
|
||||
extra['Content-Type'] = c.content_type
|
||||
if c.replyto:
|
||||
extra['Reply-To'] = c.replyto
|
||||
send_mail_text(None, to=c.to_val, frm=c.from_val, subject=c.subject,
|
||||
txt=c.body, cc=c.cc_val, extra=extra, bcc=c.bcc_val)
|
||||
c.actual_sent_date=now.date()
|
||||
c.actual_sent_time=str(now.time()) # sigh
|
||||
c.mail_sent=True
|
||||
c.save()
|
||||
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())
|
||||
|
||||
for announcement in announcements:
|
||||
send_scheduled_announcement(announcement)
|
||||
|
||||
syslog.syslog('Sent scheduled announcement %s "%s"' % (announcement.id, announcement.subject))
|
||||
|
|
122
ietf/idrfc/expire.py
Normal file
122
ietf/idrfc/expire.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
# expiry of Internet Drafts
|
||||
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
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.idrfc.utils import log_state_changed, add_document_comment
|
||||
|
||||
def in_id_expire_freeze(when=None):
|
||||
if when == None:
|
||||
when = datetime.datetime.now()
|
||||
|
||||
d = IDDates.objects.get(id=IDDates.SECOND_CUT_OFF).date
|
||||
# for some reason, the old Perl code started at 9 am
|
||||
second_cut_off = datetime.datetime.combine(d, datetime.time(9, 0))
|
||||
|
||||
d = IDDates.objects.get(id=IDDates.IETF_MONDAY).date
|
||||
ietf_monday = datetime.datetime.combine(d, datetime.time(0, 0))
|
||||
|
||||
return second_cut_off <= when < ietf_monday
|
||||
|
||||
def get_expired_ids():
|
||||
cut_off = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE)
|
||||
|
||||
return InternetDraft.objects.filter(
|
||||
revision_date__lte=cut_off,
|
||||
status__status="Active",
|
||||
review_by_rfc_editor=0).filter(
|
||||
Q(idinternal=None) | Q(idinternal__cur_state__document_state_id__gte=42))
|
||||
|
||||
def send_expire_notice_for_id(doc):
|
||||
doc.dunn_sent_date = datetime.date.today()
|
||||
doc.save()
|
||||
|
||||
if not doc.idinternal:
|
||||
return
|
||||
|
||||
request = None
|
||||
to = u"%s <%s>" % doc.idinternal.job_owner.person.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))
|
||||
|
||||
def expire_id(doc):
|
||||
def move_file(f):
|
||||
src = os.path.join(settings.INTERNET_DRAFT_PATH, f)
|
||||
dst = os.path.join(settings.INTERNET_DRAFT_ARCHIVE_DIR, f)
|
||||
|
||||
if os.path.exists(src):
|
||||
shutil.move(src, dst)
|
||||
|
||||
move_file("%s-%s.txt" % (doc.filename, doc.revision_display()))
|
||||
move_file("%s-%s.ps" % (doc.filename, doc.revision_display()))
|
||||
move_file("%s-%s.pdf" % (doc.filename, doc.revision_display()))
|
||||
|
||||
new_revision = "%02d" % (int(doc.revision) + 1)
|
||||
|
||||
new_file = open(os.path.join(settings.INTERNET_DRAFT_PATH, "%s-%s.txt" % (doc.filename, new_revision)), 'w')
|
||||
txt = render_to_string("idrfc/expire_text.txt",
|
||||
dict(doc=doc,
|
||||
authors=[a.person.email() for a in doc.authors.all()],
|
||||
expire_days=InternetDraft.DAYS_TO_EXPIRE))
|
||||
new_file.write(txt)
|
||||
new_file.close()
|
||||
|
||||
doc.revision = new_revision
|
||||
doc.expiration_date = datetime.date.today()
|
||||
doc.last_modified_date = datetime.date.today()
|
||||
doc.status = IDStatus.objects.get(status="Expired")
|
||||
doc.save()
|
||||
|
||||
if doc.idinternal:
|
||||
if doc.idinternal.cur_state_id != IDState.DEAD:
|
||||
doc.idinternal.change_state(IDState.objects.get(document_state_id=IDState.DEAD), None)
|
||||
log_state_changed(None, doc, "system")
|
||||
|
||||
add_document_comment(None, doc, "Document is expired by system")
|
||||
|
||||
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)
|
||||
|
||||
pattern = os.path.join(settings.INTERNET_DRAFT_PATH, "draft-*.*")
|
||||
files = []
|
||||
filename_re = re.compile('^(.*)-(\d+)$')
|
||||
for path in glob.glob(pattern):
|
||||
basename = os.path.basename(path)
|
||||
stem, ext = os.path.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 = InternetDraft.objects.get(filename=filename, revision=revision)
|
||||
|
||||
if doc.status_id == 3:
|
||||
if ext != ".txt":
|
||||
move_file_to("unknown_ids")
|
||||
elif doc.status_id in (2, 4, 5, 6) and doc.expiration_date and doc.expiration_date < cut_off:
|
||||
if os.path.getsize(path) < 1500:
|
||||
move_file_to("deleted_tombstones")
|
||||
# revert version after having deleted tombstone
|
||||
doc.revision = "%02d" % (int(revision) - 1)
|
||||
doc.expired_tombstone = True
|
||||
doc.save()
|
||||
else:
|
||||
move_file_to("expired_without_tombstone")
|
||||
|
||||
except InternetDraft.DoesNotExist:
|
||||
move_file_to("unknown_ids")
|
|
@ -1283,4 +1283,34 @@ withdrawn, etc.)</field>
|
|||
<field type="CharField" name="name">Individual Submissions</field>
|
||||
<field type="CharField" name="name_key">INDIVIDUAL SUBMISSIONS</field>
|
||||
</object>
|
||||
<object pk="1" model="idtracker.iddates">
|
||||
<field type="DateField" name="date">2010-07-05</field>
|
||||
<field type="CharField" name="description">1st Cut-Off Date (Initial version)</field>
|
||||
<field type="CharField" name="f_name">first</field>
|
||||
</object>
|
||||
<object pk="2" model="idtracker.iddates">
|
||||
<field type="DateField" name="date">2010-07-12</field>
|
||||
<field type="CharField" name="description">2nd Cut-Off Date (Update version)</field>
|
||||
<field type="CharField" name="f_name">second</field>
|
||||
</object>
|
||||
<object pk="3" model="idtracker.iddates">
|
||||
<field type="DateField" name="date">2010-07-26</field>
|
||||
<field type="CharField" name="description">Date of Monday of IETF</field>
|
||||
<field type="CharField" name="f_name">third</field>
|
||||
</object>
|
||||
<object pk="4" model="idtracker.iddates">
|
||||
<field type="DateField" name="date">2010-07-29</field>
|
||||
<field type="CharField" name="description">All I-Ds will be processed by</field>
|
||||
<field type="CharField" name="f_name">fourth</field>
|
||||
</object>
|
||||
<object pk="5" model="idtracker.iddates">
|
||||
<field type="DateField" name="date">2010-08-02</field>
|
||||
<field type="CharField" name="description">Date of Monday after IETF</field>
|
||||
<field type="CharField" name="f_name">fifth</field>
|
||||
</object>
|
||||
<object pk="6" model="idtracker.iddates">
|
||||
<field type="DateField" name="date">2010-06-28</field>
|
||||
<field type="CharField" name="description">Date for List of Approved V-00 Submissions from WG Chairs</field>
|
||||
<field type="CharField" name="f_name">sixth</field>
|
||||
</object>
|
||||
</django-objects>
|
|
@ -45,6 +45,7 @@ base.extend(IDState.objects.all())
|
|||
base.extend(WGType.objects.all())
|
||||
base.extend(TelechatDates.objects.all())
|
||||
base.extend(Acronym.objects.filter(acronym_id=Acronym.INDIVIDUAL_SUBMITTER))
|
||||
base.extend(IDDates.objects.all())
|
||||
|
||||
output("base", base)
|
||||
|
||||
|
|
38
ietf/idrfc/lastcall.py
Normal file
38
ietf/idrfc/lastcall.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# helpers for handling last calls on Internet Drafts
|
||||
|
||||
import datetime
|
||||
|
||||
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
|
||||
from ietf.idrfc.mails import *
|
||||
from ietf.idrfc.utils import *
|
||||
|
||||
def request_last_call(request, doc):
|
||||
try:
|
||||
ballot = doc.idinternal.ballot
|
||||
except BallotInfo.DoesNotExist:
|
||||
ballot = generate_ballot(request, doc)
|
||||
|
||||
send_last_call_request(request, doc, ballot)
|
||||
add_document_comment(request, doc, "Last Call was requested")
|
||||
|
||||
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 expire_last_call(doc):
|
||||
state = IDState.WAITING_FOR_WRITEUP
|
||||
|
||||
try:
|
||||
ballot = doc.idinternal.ballot
|
||||
if ballot.ballot_writeup and "What does this protocol do and why" not in ballot.ballot_writeup:
|
||||
state = IDState.WAITING_FOR_AD_GO_AHEAD
|
||||
except BallotInfo.DoesNotExist:
|
||||
pass
|
||||
|
||||
doc.idinternal.change_state(IDState.objects.get(document_state_id=state), None)
|
||||
doc.idinternal.event_date = datetime.date.today()
|
||||
doc.idinternal.save()
|
||||
|
||||
log_state_changed(None, doc, by="system", email_watch_list=False)
|
||||
|
||||
email_last_call_expired(doc)
|
|
@ -5,6 +5,7 @@ from datetime import datetime, date, time, timedelta
|
|||
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.utils.mail import send_mail, send_mail_text
|
||||
from ietf.idtracker.models import *
|
||||
|
@ -15,12 +16,12 @@ def email_state_changed(request, doc, text):
|
|||
"ID Tracker State Update Notice: %s" % doc.file_tag(),
|
||||
"idrfc/state_changed_email.txt",
|
||||
dict(text=text,
|
||||
url=request.build_absolute_uri(doc.idinternal.get_absolute_url())))
|
||||
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
|
||||
|
||||
def html_to_text(html):
|
||||
return strip_tags(html.replace("<", "<").replace(">", ">").replace("&", "&").replace("<br>", "\n"))
|
||||
|
||||
def email_owner(request, doc, owner, changed_by, text):
|
||||
def email_owner(request, doc, owner, changed_by, text, subject=None):
|
||||
if not owner or not changed_by or owner == changed_by:
|
||||
return
|
||||
|
||||
|
@ -28,10 +29,10 @@ def email_owner(request, doc, owner, changed_by, text):
|
|||
send_mail(request, to,
|
||||
"DraftTracker Mail System <iesg-secretary@ietf.org>",
|
||||
"%s updated by %s" % (doc.file_tag(), changed_by),
|
||||
"idrfc/changes_by_other.txt",
|
||||
"idrfc/change_notice.txt",
|
||||
dict(text=html_to_text(text),
|
||||
doc=doc,
|
||||
url=request.build_absolute_uri(doc.idinternal.get_absolute_url())))
|
||||
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
|
||||
|
||||
def full_intended_status(intended_status):
|
||||
s = str(intended_status)
|
||||
|
@ -70,11 +71,11 @@ def generate_last_call_announcement(request, doc):
|
|||
for d in docs:
|
||||
d.full_status = full_intended_status(d.intended_status)
|
||||
d.filled_title = textwrap.fill(d.title, width=70, subsequent_indent=" " * 3)
|
||||
urls.append(request.build_absolute_uri(d.idinternal.get_absolute_url()))
|
||||
urls.append(settings.IDTRACKER_BASE_URL + d.idinternal.get_absolute_url())
|
||||
|
||||
return render_to_string("idrfc/last_call_announcement.txt",
|
||||
dict(doc=doc,
|
||||
doc_url=request.build_absolute_uri(doc.idinternal.get_absolute_url()),
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.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,
|
||||
|
@ -131,7 +132,7 @@ def generate_approval_mail(request, doc):
|
|||
|
||||
return render_to_string("idrfc/approval_mail.txt",
|
||||
dict(doc=doc,
|
||||
doc_url=request.build_absolute_uri(doc.idinternal.get_absolute_url()),
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url(),
|
||||
cc=",\n ".join(cc),
|
||||
docs=docs,
|
||||
doc_type=doc_type,
|
||||
|
@ -150,7 +151,7 @@ def generate_approval_mail_rfc_editor(request, doc):
|
|||
|
||||
return render_to_string("idrfc/approval_mail_rfc_editor.txt",
|
||||
dict(doc=doc,
|
||||
doc_url=request.build_absolute_uri(doc.idinternal.get_absolute_url()),
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url(),
|
||||
doc_type=doc_type,
|
||||
status=status,
|
||||
full_status=full_status,
|
||||
|
@ -167,7 +168,7 @@ def send_last_call_request(request, doc, ballot):
|
|||
"Last Call: %s" % doc.file_tag(),
|
||||
"idrfc/last_call_request.txt",
|
||||
dict(docs=docs,
|
||||
doc_url=request.build_absolute_uri(doc.idinternal.get_absolute_url())))
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
|
||||
|
||||
def email_resurrect_requested(request, doc, by):
|
||||
to = "I-D Administrator <internet-drafts@ietf.org>"
|
||||
|
@ -177,7 +178,7 @@ def email_resurrect_requested(request, doc, by):
|
|||
"idrfc/resurrect_request_email.txt",
|
||||
dict(doc=doc,
|
||||
by=frm,
|
||||
url=request.build_absolute_uri(doc.idinternal.get_absolute_url())))
|
||||
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
|
||||
|
||||
def email_resurrection_completed(request, doc):
|
||||
to = u"%s <%s>" % doc.idinternal.resurrect_requested_by.person.email()
|
||||
|
@ -187,7 +188,7 @@ def email_resurrection_completed(request, doc):
|
|||
"idrfc/resurrect_completed_email.txt",
|
||||
dict(doc=doc,
|
||||
by=frm,
|
||||
url=request.build_absolute_uri(doc.idinternal.get_absolute_url())))
|
||||
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
|
||||
|
||||
def email_ballot_deferred(request, doc, by, telechat_date):
|
||||
to = "iesg@ietf.org"
|
||||
|
@ -248,7 +249,7 @@ def generate_issue_ballot_mail(request, doc):
|
|||
|
||||
return render_to_string("idrfc/issue_ballot_mail.txt",
|
||||
dict(doc=doc,
|
||||
doc_url=request.build_absolute_uri(doc.idinternal.get_absolute_url()),
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url(),
|
||||
status=status,
|
||||
active_ad_positions=active_ad_positions,
|
||||
inactive_ad_positions=inactive_ad_positions,
|
||||
|
@ -273,3 +274,16 @@ def email_iana(request, doc, to, msg):
|
|||
extra=extra,
|
||||
bcc="fenner@research.att.com")
|
||||
|
||||
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
|
||||
|
||||
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.idinternal.get_absolute_url()),
|
||||
cc="iesg-secretary@ietf.org")
|
||||
|
||||
|
|
|
@ -32,10 +32,12 @@
|
|||
|
||||
import unittest
|
||||
import StringIO
|
||||
import os, shutil
|
||||
from datetime import date, timedelta
|
||||
|
||||
import django.test
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.conf import settings
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
||||
|
@ -101,7 +103,7 @@ class ChangeStateTestCase(django.test.TestCase):
|
|||
self.assertTrue(draft.filename in mail_outbox[-1]['Subject'])
|
||||
|
||||
|
||||
def test_make_last_call(self):
|
||||
def test_request_last_call(self):
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
|
||||
self.client.login(remote_user="klm")
|
||||
|
@ -111,7 +113,8 @@ class ChangeStateTestCase(django.test.TestCase):
|
|||
|
||||
self.assertRaises(BallotInfo.DoesNotExist, lambda: draft.idinternal.ballot)
|
||||
r = self.client.post(url,
|
||||
dict(state="15", substate=""))
|
||||
dict(state=str(IDState.LAST_CALL_REQUESTED),
|
||||
substate=""))
|
||||
self.assertContains(r, "Your request to issue the Last Call")
|
||||
|
||||
# last call text
|
||||
|
@ -133,6 +136,7 @@ class ChangeStateTestCase(django.test.TestCase):
|
|||
|
||||
# comment
|
||||
self.assertTrue("Last Call was requested" in draft.idinternal.comments()[0].comment_text)
|
||||
|
||||
|
||||
class EditInfoTestCase(django.test.TestCase):
|
||||
fixtures = ['base', 'draft']
|
||||
|
@ -704,6 +708,11 @@ class MakeLastCallTestCase(django.test.TestCase):
|
|||
|
||||
def test_make_last_call(self):
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
draft.idinternal.cur_state = IDState.objects.get(document_state_id=IDState.LAST_CALL_REQUESTED)
|
||||
draft.idinternal.save()
|
||||
draft.lc_expiration_date = None
|
||||
draft.save()
|
||||
|
||||
url = urlreverse('doc_make_last_call', kwargs=dict(name=draft.filename))
|
||||
login_testing_unauthorized(self, "klm", url)
|
||||
|
||||
|
@ -715,23 +724,226 @@ class MakeLastCallTestCase(django.test.TestCase):
|
|||
|
||||
# make last call
|
||||
mailbox_before = len(mail_outbox)
|
||||
|
||||
expire_date = q('input[name=last_call_expiration_date]')[0].get("value")
|
||||
|
||||
r = self.client.post(url,
|
||||
dict(last_call_sent_date=q('input[name=last_call_sent_date]')[0].get("value"),
|
||||
last_call_expiration_date=q('input[name=last_call_expiration_date]')[0].get("value")
|
||||
last_call_expiration_date=expire_date
|
||||
))
|
||||
self.assertEquals(r.status_code, 302)
|
||||
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
self.assertEquals(draft.idinternal.cur_state_id, IDState.IN_LAST_CALL)
|
||||
|
||||
self.assertEquals(draft.lc_expiration_date.strftime("%Y-%m-%d"), expire_date)
|
||||
self.assertEquals(len(mail_outbox), mailbox_before + 4)
|
||||
|
||||
self.assertTrue("Last Call" in mail_outbox[-4]['Subject'])
|
||||
# the IANA copy
|
||||
self.assertTrue("Last Call" in mail_outbox[-3]['Subject'])
|
||||
|
||||
class ExpireIDsTestCase(django.test.TestCase):
|
||||
fixtures = ['base', 'draft']
|
||||
|
||||
def setUp(self):
|
||||
self.id_dir = os.path.abspath("tmp-id-dir")
|
||||
self.archive_dir = os.path.abspath("tmp-id-archive")
|
||||
os.mkdir(self.id_dir)
|
||||
os.mkdir(self.archive_dir)
|
||||
os.mkdir(os.path.join(self.archive_dir, "unknown_ids"))
|
||||
os.mkdir(os.path.join(self.archive_dir, "deleted_tombstones"))
|
||||
os.mkdir(os.path.join(self.archive_dir, "expired_without_tombstone"))
|
||||
|
||||
settings.INTERNET_DRAFT_PATH = self.id_dir
|
||||
settings.INTERNET_DRAFT_ARCHIVE_DIR = self.archive_dir
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.id_dir)
|
||||
shutil.rmtree(self.archive_dir)
|
||||
|
||||
def write_id_file(self, name, size):
|
||||
f = open(os.path.join(self.id_dir, name), 'w')
|
||||
f.write("a" * size)
|
||||
f.close()
|
||||
|
||||
def test_in_id_expire_freeze(self):
|
||||
from ietf.idrfc.expire import in_id_expire_freeze
|
||||
|
||||
self.assertTrue(not in_id_expire_freeze(datetime.datetime(2010, 7, 11, 0, 0)))
|
||||
self.assertTrue(not in_id_expire_freeze(datetime.datetime(2010, 7, 12, 8, 0)))
|
||||
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_expire_ids(self):
|
||||
from ietf.idrfc.expire import get_expired_ids, send_expire_notice_for_id, expire_id
|
||||
|
||||
# hack into 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 + 1)
|
||||
draft.idinternal.cur_state_id = IDState.AD_WATCHING
|
||||
draft.idinternal.save()
|
||||
draft.save()
|
||||
|
||||
draft = InternetDraft.objects.get(filename="draft-ah-rfc2141bis-urn")
|
||||
self.assertTrue(draft.idinternal == None)
|
||||
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 + 1)
|
||||
draft.save()
|
||||
|
||||
# test query
|
||||
documents = get_expired_ids()
|
||||
self.assertEquals(len(documents), 2)
|
||||
|
||||
for d in documents:
|
||||
# test notice
|
||||
mailbox_before = len(mail_outbox)
|
||||
|
||||
send_expire_notice_for_id(d)
|
||||
|
||||
self.assertEquals(InternetDraft.objects.get(filename=d.filename).dunn_sent_date, datetime.date.today())
|
||||
if d.idinternal:
|
||||
self.assertEquals(len(mail_outbox), mailbox_before + 1)
|
||||
self.assertTrue("expired" in mail_outbox[-1]["Subject"])
|
||||
|
||||
# test expiry
|
||||
txt = "%s-%s.txt" % (d.filename, d.revision_display())
|
||||
self.write_id_file(txt, 5000)
|
||||
|
||||
revision_before = d.revision
|
||||
|
||||
expire_id(d)
|
||||
|
||||
draft = InternetDraft.objects.get(filename=d.filename)
|
||||
self.assertEquals(draft.status.status, "Expired")
|
||||
self.assertEquals(int(draft.revision), int(revision_before) + 1)
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt)))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, txt)))
|
||||
new_txt = "%s-%s.txt" % (draft.filename, draft.revision)
|
||||
self.assertTrue(os.path.exists(os.path.join(self.id_dir, new_txt)))
|
||||
|
||||
def test_clean_up_id_files(self):
|
||||
from ietf.idrfc.expire import clean_up_id_files
|
||||
|
||||
# put unknown file
|
||||
unknown = "draft-i-am-unknown-01.txt"
|
||||
self.write_id_file(unknown, 5000)
|
||||
|
||||
clean_up_id_files()
|
||||
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, unknown)))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "unknown_ids", unknown)))
|
||||
|
||||
|
||||
# put file with malformed name (no revision)
|
||||
malformed = "draft-ietf-mipshop-pfmipv6.txt"
|
||||
self.write_id_file(malformed, 5000)
|
||||
|
||||
clean_up_id_files()
|
||||
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, malformed)))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "unknown_ids", malformed)))
|
||||
|
||||
|
||||
# RFC draft
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
draft.status_id = 3
|
||||
draft.save()
|
||||
|
||||
txt = "%s-%s.txt" % (draft.filename, draft.revision)
|
||||
self.write_id_file(txt, 5000)
|
||||
pdf = "%s-%s.pdf" % (draft.filename, draft.revision)
|
||||
self.write_id_file(pdf, 5000)
|
||||
|
||||
clean_up_id_files()
|
||||
|
||||
# txt files shouldn't be moved (for some reason)
|
||||
self.assertTrue(os.path.exists(os.path.join(self.id_dir, txt)))
|
||||
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, pdf)))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "unknown_ids", pdf)))
|
||||
|
||||
|
||||
# expired without tombstone
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
draft.status_id = 2
|
||||
draft.expiration_date = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE + 1)
|
||||
draft.save()
|
||||
|
||||
txt = "%s-%s.txt" % (draft.filename, draft.revision)
|
||||
self.write_id_file(txt, 5000)
|
||||
|
||||
clean_up_id_files()
|
||||
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt)))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "expired_without_tombstone", txt)))
|
||||
|
||||
|
||||
# expired with tombstone
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
draft.status_id = 2
|
||||
draft.expiration_date = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE + 1)
|
||||
draft.expired_tombstone = False
|
||||
draft.save()
|
||||
|
||||
revision_before = draft.revision
|
||||
|
||||
txt = "%s-%s.txt" % (draft.filename, draft.revision)
|
||||
self.write_id_file(txt, 1000)
|
||||
|
||||
clean_up_id_files()
|
||||
|
||||
self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt)))
|
||||
self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "deleted_tombstones", txt)))
|
||||
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
self.assertEquals(int(draft.revision), int(revision_before) - 1)
|
||||
self.assertTrue(draft.expired_tombstone)
|
||||
|
||||
class ExpireLastCallTestCase(django.test.TestCase):
|
||||
fixtures = ['base', 'draft']
|
||||
|
||||
def test_expire_last_call(self):
|
||||
from ietf.idrfc.lastcall import get_expired_last_calls, expire_last_call
|
||||
|
||||
# check that not expired drafts aren't expired
|
||||
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
draft.idinternal.cur_state = IDState.objects.get(document_state_id=IDState.IN_LAST_CALL)
|
||||
draft.idinternal.cur_substate = None
|
||||
draft.idinternal.save()
|
||||
draft.lc_expiration_date = datetime.date.today() + datetime.timedelta(days=2)
|
||||
draft.save()
|
||||
|
||||
self.assertEquals(len(get_expired_last_calls()), 0)
|
||||
|
||||
draft.lc_expiration_date = None
|
||||
draft.save()
|
||||
|
||||
self.assertEquals(len(get_expired_last_calls()), 0)
|
||||
|
||||
# test expired
|
||||
draft.lc_expiration_date = datetime.date.today()
|
||||
draft.save()
|
||||
|
||||
drafts = get_expired_last_calls()
|
||||
self.assertEquals(len(drafts), 1)
|
||||
|
||||
mailbox_before = len(mail_outbox)
|
||||
comments_before = draft.idinternal.comments().count()
|
||||
|
||||
expire_last_call(drafts[0])
|
||||
|
||||
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
|
||||
self.assertEquals(draft.idinternal.cur_state.document_state_id, IDState.WAITING_FOR_WRITEUP)
|
||||
self.assertEquals(draft.idinternal.comments().count(), comments_before + 1)
|
||||
self.assertEquals(len(mail_outbox), mailbox_before + 1)
|
||||
self.assertTrue("Last Call Expired" in mail_outbox[-1]["Subject"])
|
||||
|
||||
|
||||
|
||||
TEST_RFC_INDEX = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rfc-index xmlns="http://www.rfc-editor.org/rfc-index"
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
from ietf.idtracker.models import DocumentComment, BallotInfo, IESGLogin
|
||||
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
|
||||
from ietf.idrfc.mails import *
|
||||
|
||||
def add_document_comment(request, doc, text, include_by=True, ballot=None):
|
||||
login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
if include_by:
|
||||
text += " by %s" % login
|
||||
if request:
|
||||
login = IESGLogin.objects.get(login_name=request.user.username)
|
||||
if include_by:
|
||||
text += " by %s" % login
|
||||
else:
|
||||
login = None
|
||||
if include_by:
|
||||
text += " by %s" % "system"
|
||||
|
||||
c = DocumentComment()
|
||||
c.document = doc.idinternal
|
||||
|
@ -28,20 +33,11 @@ def generate_ballot(request, doc):
|
|||
doc.idinternal.ballot = ballot
|
||||
return ballot
|
||||
|
||||
def request_last_call(request, doc):
|
||||
try:
|
||||
ballot = doc.idinternal.ballot
|
||||
except BallotInfo.DoesNotExist:
|
||||
ballot = generate_ballot(request, doc)
|
||||
|
||||
send_last_call_request(request, doc, ballot)
|
||||
add_document_comment(request, doc, "Last Call was requested")
|
||||
|
||||
def log_state_changed(request, doc, by):
|
||||
def log_state_changed(request, doc, by, email_watch_list=True):
|
||||
change = u"State changed to <b>%s</b> from <b>%s</b> by %s" % (
|
||||
doc.idinternal.docstate(),
|
||||
format_document_state(doc.idinternal.prev_state, doc.
|
||||
idinternal.prev_sub_state),
|
||||
format_document_state(doc.idinternal.prev_state,
|
||||
doc.idinternal.prev_sub_state),
|
||||
by)
|
||||
|
||||
c = DocumentComment()
|
||||
|
@ -49,13 +45,15 @@ def log_state_changed(request, doc, by):
|
|||
c.public_flag = True
|
||||
c.version = doc.revision_display()
|
||||
c.comment_text = change
|
||||
c.created_by = by
|
||||
if isinstance(by, IESGLogin):
|
||||
c.created_by = by
|
||||
c.result_state = doc.idinternal.cur_state
|
||||
c.origin_state = doc.idinternal.prev_state
|
||||
c.rfc_flag = doc.idinternal.rfc_flag
|
||||
c.save()
|
||||
|
||||
email_state_changed(request, doc, strip_tags(change))
|
||||
if email_watch_list:
|
||||
email_state_changed(request, doc, strip_tags(change))
|
||||
|
||||
return change
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ from ietf.iesg.models import *
|
|||
from ietf.ipr.models import IprDetail
|
||||
from ietf.idrfc.mails import *
|
||||
from ietf.idrfc.utils import *
|
||||
|
||||
from ietf.idrfc.lastcall import request_last_call
|
||||
|
||||
BALLOT_CHOICES = (("yes", "Yes"),
|
||||
("noobj", "No Objection"),
|
||||
|
|
|
@ -20,6 +20,7 @@ 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
|
||||
|
||||
|
||||
class ChangeStateForm(forms.Form):
|
||||
|
|
|
@ -40,9 +40,12 @@ class IDState(models.Model):
|
|||
PUBLICATION_REQUESTED = 10
|
||||
LAST_CALL_REQUESTED = 15
|
||||
IN_LAST_CALL = 16
|
||||
WAITING_FOR_WRITEUP = 18
|
||||
WAITING_FOR_AD_GO_AHEAD = 19
|
||||
IESG_EVALUATION = 20
|
||||
IESG_EVALUATION_DEFER = 21
|
||||
APPROVED_ANNOUNCEMENT_SENT = 30
|
||||
AD_WATCHING = 42
|
||||
DEAD = 99
|
||||
DO_NOT_PUBLISH_STATES = (33, 34)
|
||||
|
||||
|
@ -1053,6 +1056,21 @@ class IRTFChair(models.Model):
|
|||
db_table = 'irtf_chairs'
|
||||
verbose_name="IRTF Research Group Chair"
|
||||
|
||||
class IDDates(models.Model):
|
||||
FIRST_CUT_OFF = 1
|
||||
SECOND_CUT_OFF = 2
|
||||
IETF_MONDAY = 3
|
||||
ALL_IDS_PROCESSED_BY = 4
|
||||
IETF_MONDAY_AFTER = 5
|
||||
APPROVED_V00_SUBMISSIONS = 6
|
||||
|
||||
date = models.DateField(db_column="id_date")
|
||||
description = models.CharField(max_length=255, db_column="date_name")
|
||||
f_name = models.CharField(max_length=255)
|
||||
|
||||
class Meta:
|
||||
db_table = 'id_dates'
|
||||
|
||||
# Not a model, but it's related.
|
||||
# This is used in the view to represent documents
|
||||
# in "I-D Exists".
|
||||
|
|
1
ietf/liaisons/management/.gitignore
vendored
Normal file
1
ietf/liaisons/management/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/*.pyc
|
1
ietf/liaisons/migrations/.gitignore
vendored
Normal file
1
ietf/liaisons/migrations/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/*.pyc
|
|
@ -143,6 +143,9 @@ INTERNAL_IPS = (
|
|||
'::1',
|
||||
)
|
||||
|
||||
# no slash at end
|
||||
IDTRACKER_BASE_URL = "http://datatracker.ietf.org"
|
||||
|
||||
# Valid values:
|
||||
# 'production', 'test', 'development'
|
||||
# Override this in settings_local.py if it's not true
|
||||
|
@ -165,6 +168,7 @@ IESG_TASK_FILE = '/a/www/www6/iesg/internal/task.txt'
|
|||
IESG_ROLL_CALL_FILE = '/a/www/www6/iesg/internal/rollcall.txt'
|
||||
IESG_MINUTES_FILE = '/a/www/www6/iesg/internal/minutes.txt'
|
||||
IESG_WG_EVALUATION_DIR = "/a/www/www6/iesg/evaluation"
|
||||
INTERNET_DRAFT_ARCHIVE_DIR = '/a/www/ietf/DRAFT_ARCHIVE'
|
||||
|
||||
# Override this in settings_local.py if needed
|
||||
CACHE_MIDDLEWARE_SECONDS = 300
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Please DO NOT reply on this email.
|
||||
Please DO NOT reply to this email.
|
||||
|
||||
I-D: {{ doc.file_tag|safe }}
|
||||
ID Tracker URL: {{ url }}
|
7
ietf/templates/idrfc/expire_text.txt
Normal file
7
ietf/templates/idrfc/expire_text.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% filter wordwrap:73 %}This Internet-Draft, {{ doc.filename }}-{{ doc.revision }}.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 %}
|
6
ietf/templates/idrfc/id_expired_email.txt
Normal file
6
ietf/templates/idrfc/id_expired_email.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
{{ doc.file_tag|safe }} was just expired.
|
||||
This draft is in the state {{ doc.idstate }} in ID Tracker.
|
||||
|
||||
|
||||
Thanks,
|
||||
IETF Secretariat.
|
|
@ -120,11 +120,18 @@ def send_mail(request, to, frm, subject, template, context, *args, **kwargs):
|
|||
txt = render_to_string(template, context, context_instance=mail_context(request))
|
||||
return send_mail_text(request, to, frm, subject, txt, *args, **kwargs)
|
||||
|
||||
|
||||
def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=None, bcc=None):
|
||||
"""Send plain text message."""
|
||||
if isinstance(txt, unicode):
|
||||
msg = MIMEText(txt.encode('utf-8'), 'plain', 'UTF-8')
|
||||
else:
|
||||
msg = MIMEText(txt)
|
||||
|
||||
send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=None, bcc=None)
|
||||
|
||||
def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=None, bcc=None):
|
||||
"""Send MIME message with content already filled in."""
|
||||
if isinstance(frm, tuple):
|
||||
frm = formataddr(frm)
|
||||
if isinstance(to, list) or isinstance(to, tuple):
|
||||
|
|
Loading…
Reference in a new issue