Ported last call and expire scripts with tests to new schema

- Legacy-Id: 2878
This commit is contained in:
Ole Laursen 2011-03-04 19:07:57 +00:00
parent 3ecd539f7b
commit bb2e2b10c5
12 changed files with 445 additions and 155 deletions

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

@ -9,6 +9,11 @@ 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
from doc.models import Document, Event, save_document_in_history
from name.models import IesgDocStateName, DocStateName, DocInfoTagName
from person.models import Email
INTERNET_DRAFT_DAYS_TO_EXPIRE = 185
def in_id_expire_freeze(when=None):
if when == None:
@ -32,6 +37,15 @@ 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():
cut_off = datetime.date.today() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE)
docs = Document.objects.filter(state="active").exclude(tags="rfc-rev").filter(Q(iesg_state=None) | Q(iesg_state__order__gte=42))
for d in docs:
e = d.latest_event(type="new_revision")
if e and e.time.date() <= cut_off:
yield d
def send_expire_notice_for_id(doc):
doc.dunn_sent_date = datetime.date.today()
doc.save()
@ -45,7 +59,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):
@ -82,6 +113,52 @@ def expire_id(doc):
add_document_comment(None, doc, "Document is expired by system")
def expire_idREDESIGN(doc):
system_email = Email.objects.get(address="(System)")
# clean up files
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)
file_types = ['txt', '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.INTERNET_DRAFT_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_email, prev)
e = Event(doc=doc, by=system_email)
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)
@ -120,3 +197,52 @@ 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.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 = 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:
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_expired_ids = get_expired_idsREDESIGN
send_expire_notice_for_id = send_expire_notice_for_idREDESIGN
expire_id = expire_idREDESIGN
clean_up_id_files = clean_up_id_filesREDESIGN

View file

@ -4,46 +4,55 @@
<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>
@ -55,365 +64,444 @@
- 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>
</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>
</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>
</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>
</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>
</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>
</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>
</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">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="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>
</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>
</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>
</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>
</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>
<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>
</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">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">In-active Area Director</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="wgeditor" model="name.rolename">
<field type="CharField" name="name">Working Group 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="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

@ -7,7 +7,10 @@ from django.conf import settings
from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin
from ietf.idrfc.mails import *
from ietf.idrfc.utils import *
from doc.models import Event
from doc.models import Document, Event, LastCallEvent, WriteupEvent, save_document_in_history
from name.models import IesgDocStateName
from person.models import Email
def request_last_call(request, doc):
try:
@ -42,6 +45,13 @@ 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(LastCallEvent, type="sent_last_call")
if e and e.expires.date() <= today:
yield d
def expire_last_call(doc):
state = IDState.WAITING_FOR_WRITEUP
@ -59,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(WriteupEvent, 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, Email.objects.get(address="(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

@ -593,3 +593,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

@ -42,7 +42,7 @@ from django.conf import settings
from pyquery import PyQuery
#from ietf.idrfc.models import *
from ietf.idtracker.models import IESGLogin, PersonOrOrgInfo, EmailAddress
from ietf.idtracker.models import IESGLogin, PersonOrOrgInfo, EmailAddress, IDDates
from doc.models import *
from name.models import *
from group.models import *
@ -73,6 +73,8 @@ def make_test_data():
)
# persons
Email.objects.get_or_create(address="(System)")
p = Person.objects.create(
name="Aread Irector",
ascii="Aread Irector",
@ -163,7 +165,7 @@ def make_test_data():
# draft
draft = Document.objects.create(
name="ietf-test",
name="draft-ietf-test",
time=datetime.datetime.now(),
type_id="draft",
title="Optimizing Martian Network Topologies",
@ -193,14 +195,15 @@ def make_test_data():
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)
super(dates.__class__, dates).save(force_insert=True) # work-around hard-coded save block
return draft
class ChangeStateTestCase(django.test.TestCase):
@ -364,8 +367,6 @@ class EditInfoTestCase(django.test.TestCase):
note="",
)
from ietf.iesg.models import TelechatDates
# add to telechat
self.assertTrue(not draft.latest_event(TelechatEvent, "scheduled_for_telechat"))
data["telechat_date"] = TelechatDates.objects.all()[0].date1.isoformat()
@ -965,7 +966,7 @@ class MakeLastCallTestCase(django.test.TestCase):
self.assertTrue("Last Call" in mail_outbox[-3]['Subject'])
class ExpireIDsTestCase(django.test.TestCase):
fixtures = ['base', 'draft']
fixtures = ['names']
def setUp(self):
self.id_dir = os.path.abspath("tmp-id-dir")
@ -991,6 +992,10 @@ class ExpireIDsTestCase(django.test.TestCase):
def test_in_id_expire_freeze(self):
from ietf.idrfc.expire import in_id_expire_freeze
# dummy id dates
IDDates.objects.create(id=IDDates.SECOND_CUT_OFF, date=datetime.date(2010, 7, 12), description="", f_name="")
IDDates.objects.create(id=IDDates.IETF_MONDAY, date=datetime.date(2010, 7, 26), description="", f_name="")
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)))
@ -998,57 +1003,62 @@ class ExpireIDsTestCase(django.test.TestCase):
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
from ietf.idrfc.expire import get_expired_ids, send_expire_notice_for_id, expire_id, INTERNET_DRAFT_DAYS_TO_EXPIRE
draft = make_test_data()
self.assertEquals(len(list(get_expired_ids())), 0)
# 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.iesg_state = None
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)
NewRevisionEvent.objects.create(
type="new_revision",
by=Email.objects.get(address="aread@ietf.org"),
doc=draft,
desc="New revision",
time=datetime.datetime.now() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE + 1),
rev="01"
)
self.assertEquals(len(list(get_expired_ids())), 1)
draft.iesg_state = IesgDocStateName.objects.get(slug="watching")
draft.save()
self.assertEquals(len(list(get_expired_ids())), 1)
# test query
documents = get_expired_ids()
self.assertEquals(len(documents), 2)
# test notice
mailbox_before = len(mail_outbox)
for d in documents:
# test notice
mailbox_before = len(mail_outbox)
send_expire_notice_for_id(draft)
send_expire_notice_for_id(d)
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("expired" in mail_outbox[-1]["Subject"])
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" % (draft.name, draft.rev)
self.write_id_file(txt, 5000)
# test expiry
txt = "%s-%s.txt" % (d.filename, d.revision_display())
self.write_id_file(txt, 5000)
revision_before = draft.rev
revision_before = d.revision
expire_id(d)
expire_id(draft)
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.name, draft.revision)
self.assertTrue(os.path.exists(os.path.join(self.id_dir, new_txt)))
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.state_id, "expired")
self.assertEquals(int(draft.rev), int(revision_before) + 1)
self.assertEquals(draft.iesg_state_id, "dead")
self.assertTrue(draft.latest_event(type="expired_document"))
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.name, draft.rev)
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
draft = make_test_data()
from ietf.idrfc.expire import clean_up_id_files, INTERNET_DRAFT_DAYS_TO_EXPIRE
# put unknown file
unknown = "draft-i-am-unknown-01.txt"
@ -1061,7 +1071,7 @@ class ExpireIDsTestCase(django.test.TestCase):
# put file with malformed name (no revision)
malformed = "draft-ietf-mipshop-pfmipv6.txt"
malformed = draft.name + ".txt"
self.write_id_file(malformed, 5000)
clean_up_id_files()
@ -1071,13 +1081,12 @@ class ExpireIDsTestCase(django.test.TestCase):
# RFC draft
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft.status_id = 3
draft.state = DocStateName.objects.get(slug="rfc")
draft.save()
txt = "%s-%s.txt" % (draft.name, draft.revision)
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 5000)
pdf = "%s-%s.pdf" % (draft.name, draft.revision)
pdf = "%s-%s.pdf" % (draft.name, draft.rev)
self.write_id_file(pdf, 5000)
clean_up_id_files()
@ -1089,13 +1098,20 @@ class ExpireIDsTestCase(django.test.TestCase):
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)
# expire draft
draft.state = DocStateName.objects.get(slug="expired")
draft.save()
txt = "%s-%s.txt" % (draft.name, draft.revision)
e = Event()
e.doc = draft
e.by = Email.objects.get(address="(System)")
e.type = "expired_document"
e.text = "Document has expired"
e.time = datetime.date.today() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE + 1)
e.save()
# expired without tombstone
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 5000)
clean_up_id_files()
@ -1105,62 +1121,64 @@ class ExpireIDsTestCase(django.test.TestCase):
# 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.rev
revision_before = draft.revision
txt = "%s-%s.txt" % (draft.name, draft.revision)
self.write_id_file(txt, 1000)
txt = "%s-%s.txt" % (draft.name, draft.rev)
self.write_id_file(txt, 1000) # < 1500 means tombstone
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)
draft = Document.objects.get(name=draft.name)
self.assertEquals(int(draft.rev), int(revision_before) - 1)
self.assertTrue(draft.tags.filter(slug="exp-tomb"))
class ExpireLastCallTestCase(django.test.TestCase):
fixtures = ['base', 'draft']
fixtures = ['names']
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
# check that non-expirable 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 = make_test_data()
draft.iesg_state_id = "lc"
draft.save()
self.assertEquals(len(get_expired_last_calls()), 0)
self.assertEquals(len(list(get_expired_last_calls())), 0)
e = LastCallEvent()
e.doc = draft
e.by = Email.objects.get(address="sec.retary@ietf.org")
e.type = "sent_last_call"
e.text = "Last call sent"
e.expires = datetime.datetime.now() + datetime.timedelta(days=14)
e.save()
self.assertEquals(len(list(get_expired_last_calls())), 0)
# test expired
draft.lc_expiration_date = datetime.date.today()
draft.save()
e = LastCallEvent()
e.doc = draft
e.by = Email.objects.get(address="sec.retary@ietf.org")
e.type = "sent_last_call"
e.text = "Last call sent"
e.expires = datetime.datetime.now()
e.save()
drafts = get_expired_last_calls()
drafts = list(get_expired_last_calls())
self.assertEquals(len(drafts), 1)
# expire it
mailbox_before = len(mail_outbox)
events_before = draft.event_set.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)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.iesg_state.slug, "writeupw")
self.assertEquals(draft.event_set.count(), events_before + 1)
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("Last Call Expired" in mail_outbox[-1]["Subject"])

View file

@ -196,7 +196,7 @@ LIAISON_ATTACH_PATH = '/a/www/ietf-datatracker/documents/LIAISON/'
LIAISON_ATTACH_URL = '/documents/LIAISON/'
# DB redesign
USE_DB_REDESIGN_PROXY_CLASSES=True
USE_DB_REDESIGN_PROXY_CLASSES = True
# Put SECRET_KEY in here, or any other sensitive or site-specific
# changes. DO NOT commit settings_local.py to svn.

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

@ -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

@ -201,7 +201,6 @@ EVENT_TYPES = [
# misc document events
("added_comment", "Added comment"),
("added_tombstone", "Added tombstone"),
("expired_document", "Expired document"),
("requested_resurrect", "Requested resurrect"),
("completed_resurrect", "Completed resurrect"),

View file

@ -76,7 +76,8 @@ class InternetDraft(Document):
#expiration_date = models.DateField()
@property
def expiration_date(self):
return self.expiration()
e = self.latest_event(type__in=('expired_document', 'new_revision', "completed_resurrect"))
return e.time.date() if e and e.type == "expired_document" else None
#abstract = models.TextField() # same name
#dunn_sent_date = models.DateField(null=True, blank=True) # unused
#extension_date = models.DateField(null=True, blank=True) # unused