From bb2e2b10c5db4169e05e1d9257689eddb76aad20 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 4 Mar 2011 19:07:57 +0000 Subject: [PATCH] Ported last call and expire scripts with tests to new schema - Legacy-Id: 2878 --- ietf/bin/expire-ids | 2 +- ietf/bin/expire-last-calls | 2 +- ietf/idrfc/expire.py | 128 ++++++++++- ietf/idrfc/fixtures/names.xml | 214 +++++++++++++------ ietf/idrfc/lastcall.py | 37 +++- ietf/idrfc/mails.py | 16 ++ ietf/idrfc/testsREDESIGN.py | 186 ++++++++-------- ietf/settings.py | 2 +- ietf/templates/idrfc/expire_textREDESIGN.txt | 7 + ietf/templates/idrfc/id_expired_email.txt | 2 +- redesign/doc/models.py | 1 - redesign/doc/proxy.py | 3 +- 12 files changed, 445 insertions(+), 155 deletions(-) create mode 100644 ietf/templates/idrfc/expire_textREDESIGN.txt diff --git a/ietf/bin/expire-ids b/ietf/bin/expire-ids index 0096b844b..c95b46365 100755 --- a/ietf/bin/expire-ids +++ b/ietf/bin/expire-ids @@ -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() diff --git a/ietf/bin/expire-last-calls b/ietf/bin/expire-last-calls index e854f40ac..57b637084 100755 --- a/ietf/bin/expire-last-calls +++ b/ietf/bin/expire-last-calls @@ -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)) diff --git a/ietf/idrfc/expire.py b/ietf/idrfc/expire.py index a98b484bd..e8b6dbfa8 100644 --- a/ietf/idrfc/expire.py +++ b/ietf/idrfc/expire.py @@ -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 ", 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 ", + 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 diff --git a/ietf/idrfc/fixtures/names.xml b/ietf/idrfc/fixtures/names.xml index 6cc9e577d..39838b7ee 100644 --- a/ietf/idrfc/fixtures/names.xml +++ b/ietf/idrfc/fixtures/names.xml @@ -4,46 +4,55 @@ Yes 1 + 0 No Objection 1 + 0 Abstain 1 + 0 Discuss 1 + 0 Recuse 1 + 0 No record 1 + 0 External Party 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. 1 + 0 Revised ID Needed An updated ID is needed to address the issues that have been raised. 1 + 0 IANA-coord 1 + 0 AD Followup @@ -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. 1 + 0 Point Raised - writeup needed 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. 1 + 0 MissingRef 1 + 0 FastTrack 1 + 0 Review by RFC Editor 1 + 0 Via RFC Editor 1 + 0 Expired tombstone 1 + 0 Approved in minute 1 + 0 Has errata 1 + 0 Updates 1 + 0 Replaces 1 + 0 Obsoletes 1 + 0 Reviews 1 + 0 References 1 + 0 RFC 1 + 0 Expired 1 + 0 Replaced 1 + 0 Active 1 + 0 Withdrawn by Submitter 1 + 0 Withdrawn by IETF 1 + 0 IETF 1 + 0 Independent Submission 1 + 0 Legacy 1 + 0 IAB 1 + 0 IRTF 1 + 0 Draft 1 + 0 External 1 + 0 BOF 1 + 0 Proposed 1 + 0 Active 1 + 0 Dormant 1 + 0 Concluded 1 + 0 Unknown 1 + 0 IETF 1 + 0 Area 1 + 0 WG 1 + 0 RG 1 + 0 Team 1 + 0 Individual 1 - - - RFC Published - The ID has been published as an RFC. - 1 - - - Dead - 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.) - 1 - - - Approved-announcement sent - The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor. - 1 - - - AD is watching - 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). - 1 - - - IESG Evaluation - 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. - 1 - - - AD Evaluation - 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. - 1 - - - In Last Call - 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. - 1 + 0 Publication Requested 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. 1 + 10 - - RFC Ed Queue - The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html). - 1 - - - IESG Evaluation - Defer - 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. - 1 - - - Waiting for Writeup - 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. - 1 - - - Waiting for AD Go-Ahead - 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. - 1 - - - Approved-announcement to be sent - The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message. + + AD Evaluation + 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. 1 + 11 Expert Review 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. 1 - - - DNP-waiting for AD note - 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. - 1 + 12 Last Call requested The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet. 1 + 15 + + + In Last Call + 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. + 1 + 16 + + + Waiting for Writeup + 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. + 1 + 18 + + + Waiting for AD Go-Ahead + 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. + 1 + 19 + + + IESG Evaluation + 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. + 1 + 20 + + + IESG Evaluation - Defer + 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. + 1 + 21 + + + Approved-announcement to be sent + The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message. + 1 + 27 + + + Approved-announcement sent + The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor. + 1 + 30 + + + RFC Ed Queue + The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html). + 1 + 31 + + + RFC Published + The ID has been published as an RFC. + 1 + 32 + + + DNP-waiting for AD note + 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. + 1 + 33 DNP-announcement to be sent 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. 1 + 34 + + + AD is watching + 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). + 1 + 42 + + + Dead + 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.) + 1 + 99 Best Current Practice 1 + 0 Draft Standard 1 + 0 Experimental 1 + 0 Historic 1 + 0 Informational 1 + 0 Proposed Standard 1 + 0 Standard 1 + 0 Area Director 1 + 0 Ex-Area Director In-active Area Director 1 + 0 + + + Working Group Editor + + 1 + 0 Standard 1 + 0 Draft Standard 1 + 0 Proposed Standard 1 + 0 Informational 1 + 0 Experimental 1 + 0 Best Current Practice 1 + 0 Historic 1 + 0 Unknown 1 + 0 \ No newline at end of file diff --git a/ietf/idrfc/lastcall.py b/ietf/idrfc/lastcall.py index 63e937e43..880b001af 100644 --- a/ietf/idrfc/lastcall.py +++ b/ietf/idrfc/lastcall.py @@ -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 + diff --git a/ietf/idrfc/mails.py b/ietf/idrfc/mails.py index 3233700f1..799d7dea3 100644 --- a/ietf/idrfc/mails.py +++ b/ietf/idrfc/mails.py @@ -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 ", + "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 + diff --git a/ietf/idrfc/testsREDESIGN.py b/ietf/idrfc/testsREDESIGN.py index 6b7741a95..d8831a700 100644 --- a/ietf/idrfc/testsREDESIGN.py +++ b/ietf/idrfc/testsREDESIGN.py @@ -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"]) diff --git a/ietf/settings.py b/ietf/settings.py index f4c4027fd..110773850 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -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. diff --git a/ietf/templates/idrfc/expire_textREDESIGN.txt b/ietf/templates/idrfc/expire_textREDESIGN.txt new file mode 100644 index 000000000..c14c7b5f6 --- /dev/null +++ b/ietf/templates/idrfc/expire_textREDESIGN.txt @@ -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 %} diff --git a/ietf/templates/idrfc/id_expired_email.txt b/ietf/templates/idrfc/id_expired_email.txt index 5ea37273c..5eda3f0ac 100644 --- a/ietf/templates/idrfc/id_expired_email.txt +++ b/ietf/templates/idrfc/id_expired_email.txt @@ -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, diff --git a/redesign/doc/models.py b/redesign/doc/models.py index 822e66570..06f4eb349 100644 --- a/redesign/doc/models.py +++ b/redesign/doc/models.py @@ -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"), diff --git a/redesign/doc/proxy.py b/redesign/doc/proxy.py index bf069a873..58763a13f 100644 --- a/redesign/doc/proxy.py +++ b/redesign/doc/proxy.py @@ -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