Port change_state view + helpers + tests to new schema, retaining the old view via settings; adjusted relationship model to use source/target as attribute names

- Legacy-Id: 2808
This commit is contained in:
Ole Laursen 2011-02-03 19:40:38 +00:00
parent 294740d270
commit d0f3b5e628
19 changed files with 2363 additions and 75 deletions

View file

@ -0,0 +1,414 @@
<?xml version="1.0" encoding="utf-8"?>
<django-objects version="1.0">
<object pk="yes" model="name.ballotpositionname">
<field type="CharField" name="name">Yes</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</object>
<object pk="ad-f-up" model="name.docinfotagname">
<field type="CharField" name="name">AD Followup</field>
<field type="TextField" name="desc">A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include:
- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG.
- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently.
- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes.</field>
<field type="BooleanField" name="used">1</field>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
<field type="BooleanField" name="used">1</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</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>
</object>
</django-objects>

View file

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

View file

@ -2,9 +2,12 @@
import datetime
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
def request_last_call(request, doc):
try:
@ -15,6 +18,26 @@ def request_last_call(request, doc):
send_last_call_request(request, doc, ballot)
add_document_comment(request, doc, "Last Call was requested")
def request_last_callREDESIGN(request, doc):
if not doc.latest_event(type="changed_ballot_writeup_text"):
generate_ballot_writeup(request, doc)
if not doc.latest_event(type="changed_ballot_approval_text"):
generate_approval_mail(request, doc)
if not doc.latest_event(type="changed_last_call_text"):
generate_last_call_announcement(request, doc)
send_last_call_request(request, doc)
e = Event()
e.type = "requested_last_call"
e.by = request.user.get_profile().email()
e.doc = doc
e.desc = "Last call was requested by %s" % e.by.get_name()
e.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
request_last_call = request_last_callREDESIGN
def get_expired_last_calls():
return InternetDraft.objects.filter(lc_expiration_date__lte=datetime.date.today(),
idinternal__cur_state__document_state_id=IDState.IN_LAST_CALL)

View file

@ -9,6 +9,8 @@ from django.conf import settings
from ietf.utils.mail import send_mail, send_mail_text
from ietf.idtracker.models import *
from doc.models import Text
from person.models import Email
def email_state_changed(request, doc, text):
to = [x.strip() for x in doc.idinternal.state_change_notice_to.replace(';', ',').split(',')]
@ -18,6 +20,17 @@ def email_state_changed(request, doc, text):
dict(text=text,
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def email_state_changedREDESIGN(request, doc, text):
to = [x.strip() for x in doc.notify.replace(';', ',').split(',')]
send_mail(request, to, None,
"ID Tracker State Update Notice: %s" % doc.file_tag(),
"idrfc/state_changed_email.txt",
dict(text=text,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_state_changed = email_state_changedREDESIGN
def html_to_text(html):
return strip_tags(html.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&").replace("<br>", "\n"))
@ -34,6 +47,23 @@ def email_owner(request, doc, owner, changed_by, text, subject=None):
doc=doc,
url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def email_ownerREDESIGN(request, doc, owner, changed_by, text, subject=None):
if not owner or not changed_by or owner == changed_by:
return
to = owner.formatted_email()
send_mail(request, to,
"DraftTracker Mail System <iesg-secretary@ietf.org>",
"%s updated by %s" % (doc.file_tag(), changed_by),
"idrfc/change_notice.txt",
dict(text=html_to_text(text),
doc=doc,
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_owner = email_ownerREDESIGN
def full_intended_status(intended_status):
s = str(intended_status)
# FIXME: this should perhaps be defined in the db
@ -54,6 +84,15 @@ def full_intended_status(intended_status):
else:
return "a %s" % s
def generate_ballot_writeup(request, doc):
e = Text()
e.type = "changed_ballot_writeup_text"
e.by = request.user.get_profile().email()
e.doc = doc
e.desc = u"Ballot writeup was generated by %s" % e.by.get_name()
e.content = unicode(render_to_string("idrfc/ballot_writeup.txt"))
e.save()
def generate_last_call_announcement(request, doc):
status = full_intended_status(doc.intended_status).replace("a ", "").replace("an ", "")
@ -86,6 +125,49 @@ def generate_last_call_announcement(request, doc):
)
)
def generate_last_call_announcementREDESIGN(request, doc):
doc.full_status = full_intended_status(doc.intended_std_level)
status = doc.full_status.replace("a ", "").replace("an ", "")
expiration_date = date.today() + timedelta(days=14)
cc = []
if doc.group.type_id == "individ":
group = "an individual submitter"
expiration_date += timedelta(days=14)
else:
group = "the %s WG (%s)" % (doc.group.name, doc.group.acronym)
if doc.group.list_email:
cc.append(doc.group.list_email)
doc.filled_title = textwrap.fill(doc.title, width=70, subsequent_indent=" " * 3)
url = settings.IDTRACKER_BASE_URL + doc.get_absolute_url()
mail = render_to_string("idrfc/last_call_announcement.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
expiration_date=expiration_date.strftime("%Y-%m-%d"), #.strftime("%B %-d, %Y"),
cc=", ".join("<%s>" % e for e in cc),
group=group,
docs=[doc],
urls=[url],
status=status,
impl_report="Draft" in status or "Full" in status,
)
)
from doc.models import Text
e = Text()
e.type = "changed_last_call_text"
e.by = request.user.get_profile().email()
e.doc = doc
e.desc = u"Last call announcement was generated by %s" % e.by.get_name()
e.content = unicode(mail)
e.save()
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_last_call_announcement = generate_last_call_announcementREDESIGN
def generate_approval_mail(request, doc):
if doc.idinternal.cur_state_id in IDState.DO_NOT_PUBLISH_STATES or doc.idinternal.via_rfc_editor:
return generate_approval_mail_rfc_editor(request, doc)
@ -114,7 +196,7 @@ def generate_approval_mail(request, doc):
if len(docs) > 1:
made_by = "These documents have been reviewed in the IETF but are not the products of an IETF Working Group."
else:
made_by = "This document has been reviewed in the IETF but is not the product of an IETF Working Group.";
made_by = "This document has been reviewed in the IETF but is not the product of an IETF Working Group."
else:
if len(docs) > 1:
made_by = "These documents are products of the %s." % doc.group.name_with_wg
@ -159,6 +241,95 @@ def generate_approval_mail_rfc_editor(request, doc):
)
)
DO_NOT_PUBLISH_IESG_STATES = ("nopubadw", "nopubanw")
def generate_approval_mailREDESIGN(request, doc):
if doc.iesg_state_id in DO_NOT_PUBLISH_IESG_STATES or doc.tags.filter(slug='via-rfc'):
mail = generate_approval_mail_rfc_editor(request, doc)
else:
mail = generate_approval_mail_approved(request, doc)
from doc.models import Text
e = Text()
e.type = "changed_ballot_approval_text"
e.by = request.user.get_profile().email()
e.doc = doc
e.desc = u"Ballot approval text was generated by %s" % e.by.get_name()
e.content = unicode(mail)
e.save()
def generate_approval_mail_approved(request, doc):
doc.full_status = full_intended_status(doc.intended_std_level.name)
status = doc.full_status.replace("a ", "").replace("an ", "")
if "an " in status:
action_type = "Document"
else:
action_type = "Protocol"
cc = ["Internet Architecture Board <iab@iab.org>", "RFC Editor <rfc-editor@rfc-editor.org>"]
# the second check catches some area working groups (like
# Transport Area Working Group)
if doc.group.type_id != "area" and not doc.group.name.endswith("Working Group"):
doc.group.name_with_wg = doc.group.name + " Working Group"
if doc.group.list_email:
cc.append("%s mailing list <%s>" % (doc.group.acronym, doc.group.list_email))
cc.append("%s chair <%s-chairs@tools.ietf.org>" % (doc.group.acronym, doc.group.acronym))
else:
doc.group.name_with_wg = doc.group.name
doc.filled_title = textwrap.fill(doc.title, width=70, subsequent_indent=" " * 3)
if doc.group.type_id == "individ":
made_by = "This document has been reviewed in the IETF but is not the product of an IETF Working Group."
else:
made_by = "This document is the product of the %s." % doc.group.name_with_wg
director = doc.ad
other_director = Email.objects.filter(role__group__role__email=director, role__group__role__name="ad").exclude(pk=director.pk)
if doc.group.type_id != "individ" and other_director:
contacts = "The IESG contact persons are %s and %s." % (director.get_name(), other_director[0].get_name())
else:
contacts = "The IESG contact person is %s." % director.get_name()
doc_type = "RFC" if doc.state_id == "rfc" else "Internet Draft"
return render_to_string("idrfc/approval_mail.txt",
dict(doc=doc,
docs=[doc],
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
cc=",\n ".join(cc),
doc_type=doc_type,
made_by=made_by,
contacts=contacts,
status=status,
action_type=action_type,
)
)
def generate_approval_mail_rfc_editorREDESIGN(request, doc):
full_status = full_intended_status(doc.intended_std_level.name)
status = full_status.replace("a ", "").replace("an ", "")
disapproved = doc.iesg_state in DO_NOT_PUBLISH_IESG_STATES
doc_type = "RFC" if doc.state_id == "rfc" else "Internet Draft"
return render_to_string("idrfc/approval_mail_rfc_editor.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url(),
doc_type=doc_type,
status=status,
full_status=full_status,
disapproved=disapproved,
)
)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_approval_mail = generate_approval_mailREDESIGN
generate_approval_mail_rfc_editor = generate_approval_mail_rfc_editorREDESIGN
def send_last_call_request(request, doc, ballot):
to = "iesg-secretary@ietf.org"
frm = '"DraftTracker Mail System" <iesg-secretary@ietf.org>'
@ -170,6 +341,19 @@ def send_last_call_request(request, doc, ballot):
dict(docs=docs,
doc_url=settings.IDTRACKER_BASE_URL + doc.idinternal.get_absolute_url()))
def send_last_call_requestREDESIGN(request, doc):
to = "iesg-secretary@ietf.org"
frm = '"DraftTracker Mail System" <iesg-secretary@ietf.org>'
send_mail(request, to, frm,
"Last Call: %s" % doc.file_tag(),
"idrfc/last_call_request.txt",
dict(docs=[doc],
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
send_last_call_request = send_last_call_requestREDESIGN
def email_resurrect_requested(request, doc, by):
to = "I-D Administrator <internet-drafts@ietf.org>"
frm = u"%s <%s>" % by.person.email()

View file

@ -1277,3 +1277,5 @@ class MirrorScriptTestCases(unittest.TestCase,RealDatabaseTest):
self.assertEquals(len(refs), 3)
print "OK"
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
from testsREDESIGN import *

1428
ietf/idrfc/testsREDESIGN.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -22,6 +22,8 @@ from ietf.idrfc.mails import *
from ietf.idrfc.utils import *
from ietf.idrfc.lastcall import request_last_call
from doc.models import Document, Event, save_document_in_history, DocHistory
from name.models import IesgDocStateName, get_next_iesg_states
class ChangeStateForm(forms.Form):
state = forms.ModelChoiceField(IDState.objects.all(), empty_label=None, required=True)
@ -56,7 +58,8 @@ def change_state(request, name):
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
dict(doc=doc),
dict(doc=doc,
url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
return HttpResponseRedirect(internal.get_absolute_url())
@ -77,6 +80,79 @@ def change_state(request, name):
next_states=next_states),
context_instance=RequestContext(request))
class ChangeStateFormREDESIGN(forms.Form):
state = forms.ModelChoiceField(IesgDocStateName.objects.all(), empty_label=None, required=True)
# FIXME: no tags yet
#substate = forms.ModelChoiceField(IDSubState.objects.all(), required=False)
@group_required('Area_Director','Secretariat')
def change_stateREDESIGN(request, name):
"""Change state of Internet Draft, notifying parties as necessary
and logging the change as a comment."""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.latest_event(type="started_iesg_process") or doc.state_id == "expired":
raise Http404()
login = request.user.get_profile().email()
if request.method == 'POST':
form = ChangeStateForm(request.POST)
if form.is_valid():
state = form.cleaned_data['state']
if state != doc.iesg_state:
save_document_in_history(doc)
prev_state = doc.iesg_state
doc.iesg_state = state
e = Event()
e.type = "changed_document"
e.by = login
e.doc = doc
e.desc = u"State changed to <b>%s</b> from <b>%s</b> by %s" % (
doc.iesg_state.name,
prev_state.name if prev_state else "None",
login.get_name())
e.save()
doc.time = e.time
doc.save()
email_state_changed(request, doc, strip_tags(e.desc))
email_owner(request, doc, doc.ad, login, e.desc)
if doc.iesg_state_id == "lc-req":
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
dict(doc=doc,
url=doc.get_absolute_url()),
context_instance=RequestContext(request))
return HttpResponseRedirect(doc.get_absolute_url())
else:
form = ChangeStateForm(initial=dict(state=doc.iesg_state_id))
next_states = get_next_iesg_states(doc.iesg_state)
prev_state = None
hists = DocHistory.objects.filter(doc=doc).exclude(iesg_state=doc.iesg_state).order_by("-time")[:1]
if hists:
prev_state = hists[0].iesg_state
return render_to_response('idrfc/change_stateREDESIGN.html',
dict(form=form,
doc=doc,
prev_state=prev_state,
next_states=next_states),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
change_state = change_stateREDESIGN
ChangeStateForm = ChangeStateFormREDESIGN
def dehtmlify_textarea_text(s):
return s.replace("<br>", "\n").replace("<b>", "").replace("</b>", "").replace(" ", " ")

View file

@ -409,16 +409,16 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES:
r.obsoleted_by_list = []
r.updated_by_list = []
xed_by = RelatedDocument.objects.filter(doc_alias__name__in=rfc_aliases.values(), relationship__in=("obs", "updates")).select_related('doc_alias__document_id')
rel_rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[rel.document_id for rel in xed_by]).values_list('document_id', 'name'))
xed_by = RelatedDocument.objects.filter(target__name__in=rfc_aliases.values(), relationship__in=("obs", "updates")).select_related('target__document_id')
rel_rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc", document__in=[rel.source_id for rel in xed_by]).values_list('document_id', 'name'))
for rel in xed_by:
r = result_map[rel.doc_alias.document_id]
r = result_map[rel.target.document_id]
if rel.relationship_id == "obs":
attr = "obsoleted_by_list"
else:
attr = "updated_by_list"
getattr(r, attr).append(int(rel_rfc_aliases[rel.document_id][3:]))
getattr(r, attr).append(int(rel_rfc_aliases[rel.source_id][3:]))
# sort

View file

@ -249,11 +249,11 @@ class PersonOrOrgInfo(models.Model):
date_created = models.DateField(auto_now_add=True, null=True)
created_by = models.CharField(blank=True, null=True, max_length=8)
address_type = models.CharField(blank=True, null=True, max_length=4)
def save(self):
def save(self, **kwargs):
self.first_name_key = self.first_name.upper()
self.middle_initial_key = self.middle_initial.upper()
self.last_name_key = self.last_name.upper()
super(PersonOrOrgInfo, self).save()
super(PersonOrOrgInfo, self).save(**kwargs)
def __str__(self):
# For django.VERSION 0.96
if self.first_name == '' and self.last_name == '':

View file

@ -61,6 +61,11 @@ class IetfUserProfile(models.Model):
except:
return None
def email(self):
# quick hack to bind new and old schema together for the time being
from person.models import Email
return Email.objects.get(address=self.person().email()[1])
def __str__(self):
return "IetfUserProfile(%s)" % (self.user,)

View file

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

View file

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

View file

@ -12,7 +12,7 @@ class DocHistoryAdmin(admin.ModelAdmin):
list_display = ['doc', 'rev', 'state', 'group', 'pages', 'intended_std_level', 'author_list', 'time']
search_fields = ['doc__name']
ordering = ['time', 'doc', 'rev']
raw_id_fields = ['authors', 'related']
raw_id_fields = ['doc', 'authors', 'related', 'group', 'shepherd', 'ad']
admin.site.register(DocHistory, DocHistoryAdmin)
class DocAliasAdmin(admin.ModelAdmin):

View file

@ -1,6 +1,8 @@
# Copyright The IETF Trust 2007, All Rights Reserved
from django.db import models
from django.core.urlresolvers import reverse as urlreverse
from redesign.group.models import *
from redesign.name.models import *
from redesign.person.models import Email
@ -19,17 +21,17 @@ class DocumentInfo(models.Model):
tags = models.ManyToManyField(DocInfoTagName, blank=True, null=True) # Revised ID Needed, ExternalParty, AD Followup, ...
stream = models.ForeignKey(DocStreamName, blank=True, null=True) # IETF, IAB, IRTF, Independent Submission
group = models.ForeignKey(Group, blank=True, null=True) # WG, RG, IAB, IESG, Edu, Tools
wg_state = models.ForeignKey(WgDocStateName, blank=True, null=True) # Not/Candidate/Active/Parked/LastCall/WriteUp/Submitted/Dead
iesg_state = models.ForeignKey(IesgDocStateName, blank=True, null=True) #
iana_state = models.ForeignKey(IanaDocStateName, blank=True, null=True)
rfc_state = models.ForeignKey(RfcDocStateName, blank=True, null=True)
wg_state = models.ForeignKey(WgDocStateName, verbose_name="WG state", blank=True, null=True) # Not/Candidate/Active/Parked/LastCall/WriteUp/Submitted/Dead
iesg_state = models.ForeignKey(IesgDocStateName, verbose_name="IESG state", blank=True, null=True) #
iana_state = models.ForeignKey(IanaDocStateName, verbose_name="IANA state", blank=True, null=True)
rfc_state = models.ForeignKey(RfcDocStateName, verbose_name="RFC state", blank=True, null=True)
# Other
abstract = models.TextField()
rev = models.CharField(max_length=16)
rev = models.CharField(verbose_name="revision", max_length=16)
pages = models.IntegerField(blank=True, null=True)
intended_std_level = models.ForeignKey(IntendedStdLevelName, blank=True, null=True)
std_level = models.ForeignKey(StdLevelName, blank=True, null=True)
ad = models.ForeignKey(Email, related_name='ad_%(class)s_set', blank=True, null=True)
ad = models.ForeignKey(Email, verbose_name="area director", related_name='ad_%(class)s_set', blank=True, null=True)
shepherd = models.ForeignKey(Email, related_name='shepherd_%(class)s_set', blank=True, null=True)
notify = models.CharField(max_length=255, blank=True)
external_url = models.URLField(blank=True) # Should be set for documents with type 'External'.
@ -50,11 +52,11 @@ class DocumentInfo(models.Model):
return e[0] if e else None
class RelatedDocument(models.Model):
document = models.ForeignKey('Document') # source
doc_alias = models.ForeignKey('DocAlias') # target
source = models.ForeignKey('Document')
target = models.ForeignKey('DocAlias')
relationship = models.ForeignKey(DocRelationshipName)
def __unicode__(self):
return u"%s %s %s" % (self.document.name, self.relationship.name.lower(), self.doc_alias.name)
return u"%s %s %s" % (self.source.name, self.relationship.name.lower(), self.target.name)
class DocumentAuthor(models.Model):
document = models.ForeignKey('Document')
@ -62,7 +64,7 @@ class DocumentAuthor(models.Model):
order = models.IntegerField()
def __unicode__(self):
return u"%s %s (%s)" % (self.document.name, self.email.get_name(), self.order)
return u"%s %s (%s)" % (self.document.name, self.author.get_name(), self.order)
class Meta:
ordering = ["document", "order"]
@ -71,55 +73,28 @@ class Document(DocumentInfo):
name = models.CharField(max_length=255, primary_key=True) # immutable
related = models.ManyToManyField('DocAlias', through=RelatedDocument, blank=True, related_name="reversely_related_document_set")
authors = models.ManyToManyField(Email, through=DocumentAuthor, blank=True)
def __unicode__(self):
return self.name
def values(self):
try:
fields = dict([(field.name, getattr(self, field.name))
for field in self._meta.fields
if field is not self._meta.pk])
except:
for field in self._meta.fields:
print "* %24s"%field.name,
print getattr(self, field.name)
raise
many2many = dict([(field.name, getattr(self, field.name).all())
for field in self._meta.many_to_many ])
return fields, many2many
def save_with_history(self, force_insert=False, force_update=False):
fields, many2many = self.values()
fields["doc"] = self
try:
snap = DocHistory.objects.get(**dict((k,v) for k,v in fields.items() if k != 'time'))
# FIXME: what if there are two with the same set of values
# at different points in time?
if snap.time > fields["time"]:
snap.time = fields["time"]
snap.save()
except DocHistory.DoesNotExist:
snap = DocHistory(**fields)
snap.save()
for m in many2many:
# FIXME: check that this works with related/authors
#print "m2m:", m, many2many[m]
rel = getattr(snap, m)
for item in many2many[m]:
rel.add(item)
except DocHistory.MultipleObjectsReturned:
list = DocHistory.objects.filter(**dict((k,v) for k,v in fields.items() if k != 'time'))
list.delete()
snap = DocHistory(**fields)
snap.save()
print "Deleted list:", snap
super(Document, self).save(force_insert, force_update)
def get_absolute_url(self):
name = self.name
if self.state == "rfc":
aliases = self.docalias_set.filter(name__startswith="rfc")
if aliases:
name = aliases[0].name
return urlreverse('doc_view', kwargs={ 'name': name })
def file_tag(self):
# FIXME: compensate for tombstones?
return u"<%s-%s.txt>" % (self.name, self.rev)
class RelatedDocHistory(models.Model):
document = models.ForeignKey('DocHistory') # source
doc_alias = models.ForeignKey('DocAlias', related_name="reversely_related_document_history_set") # target
source = models.ForeignKey('DocHistory')
target = models.ForeignKey('DocAlias', related_name="reversely_related_document_history_set")
relationship = models.ForeignKey(DocRelationshipName)
def __unicode__(self):
return u"%s %s %s" % (self.document.doc.name, self.relationship.name.lower(), self.doc_alias.name)
return u"%s %s %s" % (self.source.doc.name, self.relationship.name.lower(), self.target.name)
class DocHistoryAuthor(models.Model):
document = models.ForeignKey('DocHistory')
@ -127,18 +102,58 @@ class DocHistoryAuthor(models.Model):
order = models.IntegerField()
def __unicode__(self):
return u"%s %s (%s)" % (self.document.doc.name, self.email.get_name(), self.order)
return u"%s %s (%s)" % (self.document.doc.name, self.author.get_name(), self.order)
class Meta:
ordering = ["document", "order"]
class DocHistory(DocumentInfo):
doc = models.ForeignKey(Document) # ID of the Document this relates to
# Django won't let us define these in the base class, so we have
# to repeat them
related = models.ManyToManyField('DocAlias', through=RelatedDocHistory, blank=True)
authors = models.ManyToManyField(Email, through=DocHistoryAuthor, blank=True)
def __unicode__(self):
return unicode(self.doc.name)
def save_document_in_history(doc):
def get_model_fields_as_dict(obj):
return dict((field.name, getattr(obj, field.name))
for field in obj._meta.fields
if field is not obj._meta.pk)
# copy fields
fields = get_model_fields_as_dict(doc)
fields["doc"] = doc
dochist = DocHistory(**fields)
dochist.save()
# copy many to many
for field in doc._meta.many_to_many:
if not field.rel.through:
# just add the attributes
rel = getattr(dochist, field.name)
for item in getattr(doc, field.name).all():
rel.add(item)
# copy remaining tricky many to many
def transfer_fields(obj, HistModel):
mfields = get_model_fields_as_dict(item)
# map doc -> dochist
for k, v in mfields.iteritems():
if v == doc:
mfields[k] = dochist
HistModel.objects.create(**mfields)
for item in RelatedDocument.objects.filter(source=doc):
transfer_fields(item, RelatedDocHistory)
for item in DocumentAuthor.objects.filter(document=doc):
transfer_fields(item, DocHistoryAuthor)
return dochist
class DocAlias(models.Model):
"""This is used for documents that may appear under multiple names,
and in particular for RFCs, which for continuity still keep the
@ -194,8 +209,10 @@ EVENT_TYPES = [
# IESG events
("started_iesg_process", "Started IESG process on document"),
("sent_ballot_announcement", "Sent ballot announcement"),
("changed_ballot_position", "Changed ballot position"),
("changed_ballot_approval_text", "Changed ballot approval text"),
("changed_ballot_writeup_text", "Changed ballot writeup text"),

View file

@ -145,7 +145,7 @@ class InternetDraft(Document):
#replaced_by = models.ForeignKey('self', db_column='replaced_by', blank=True, null=True, related_name='replaces_set')
@property
def replaced_by(self):
r = InternetDraft.objects.filter(relateddocument__doc_alias__document=self, relateddocument__relationship="replaces")
r = InternetDraft.objects.filter(relateddocument__target__document=self, relateddocument__relationship="replaces")
return r[0] if r else None
#replaces = FKAsOneToOne('replaces', reverse=True)
@ -156,7 +156,7 @@ class InternetDraft(Document):
@property
def replaces_set(self):
return InternetDraft.objects.filter(docalias__relateddocument__document=self, docalias__relateddocument__relationship="replaces")
return InternetDraft.objects.filter(docalias__relateddocument__source=self, docalias__relateddocument__relationship="replaces")
#review_by_rfc_editor = models.BooleanField()
@property
@ -631,25 +631,25 @@ class InternetDraft(Document):
#updates = models.CharField(max_length=200,blank=True,null=True)
@property
def updates(self):
return ",".join("RFC%s" % n for n in sorted(d.rfc_number for d in InternetDraft.objects.filter(docalias__relateddocument__document=self, docalias__relateddocument__relationship="updates")))
return ",".join("RFC%s" % n for n in sorted(d.rfc_number for d in InternetDraft.objects.filter(docalias__relateddocument__source=self, docalias__relateddocument__relationship="updates")))
#updated_by = models.CharField(max_length=200,blank=True,null=True)
@property
def updated_by(self):
if not hasattr(self, "updated_by_list"):
self.updated_by_list = [d.rfc_number for d in InternetDraft.objects.filter(relateddocument__doc_alias__document=self, relateddocument__relationship="updates")]
self.updated_by_list = [d.rfc_number for d in InternetDraft.objects.filter(relateddocument__target__document=self, relateddocument__relationship="updates")]
return ",".join("RFC%s" % n for n in sorted(self.updated_by_list))
#obsoletes = models.CharField(max_length=200,blank=True,null=True)
@property
def obsoletes(self):
return ",".join("RFC%s" % n for n in sorted(d.rfc_number for d in InternetDraft.objects.filter(docalias__relateddocument__document=self, docalias__relateddocument__relationship="obs")))
return ",".join("RFC%s" % n for n in sorted(d.rfc_number for d in InternetDraft.objects.filter(docalias__relateddocument__source=self, docalias__relateddocument__relationship="obs")))
#obsoleted_by = models.CharField(max_length=200,blank=True,null=True)
@property
def obsoleted_by(self):
if not hasattr(self, "obsoleted_by_list"):
self.obsoleted_by_list = [d.rfc_number for d in InternetDraft.objects.filter(relateddocument__doc_alias__document=self, relateddocument__relationship="obs")]
self.obsoleted_by_list = [d.rfc_number for d in InternetDraft.objects.filter(relateddocument__target__document=self, relateddocument__relationship="obs")]
return ",".join("RFC%s" % n for n in sorted(self.obsoleted_by_list))
#also = models.CharField(max_length=50,blank=True,null=True)

View file

@ -49,5 +49,5 @@ class Role(models.Model):
email = models.ForeignKey(Email)
auth = models.CharField(max_length=255, blank=True)
def __unicode__(self):
return self.name
return u"%s is %s in %s" % (self.email.get_name(), self.name.name, self.grop.acronym)

View file

@ -890,7 +890,7 @@ for index, o in enumerate(all_drafts.iterator()):
# replacements
if o.replaced_by:
replacement, _ = Document.objects.get_or_create(name=o.replaced_by.filename, defaults=dict(time=datetime.datetime(1970, 1, 1, 0, 0, 0)))
RelatedDocument.objects.get_or_create(document=replacement, doc_alias=d_alias, relationship=relationship_replaces)
RelatedDocument.objects.get_or_create(source=replacement, target=d_alias, relationship=relationship_replaces)
# the RFC-related attributes are imported when we handle the RFCs below
@ -987,9 +987,9 @@ for index, o in enumerate(all_rfcs.iterator()):
other_number = int(other_rfc.replace("RFC", ""))
other, other_alias = get_or_create_rfc_document(other_number)
if reverse:
RelatedDocument.objects.get_or_create(document=other, doc_alias=d_alias, relationship=rel_type)
RelatedDocument.objects.get_or_create(source=other, target=d_alias, relationship=rel_type)
else:
RelatedDocument.objects.get_or_create(document=d, doc_alias=other_alias, relationship=rel_type)
RelatedDocument.objects.get_or_create(source=d, target=other_alias, relationship=rel_type)
def parse_relation_list(s):
if not s:

View file

@ -55,3 +55,31 @@ class IntendedStdLevelName(NameModel):
Practice, Historic, ..."""
class BallotPositionName(NameModel):
""" Yes, NoObjection, Abstain, Discuss, Recuse """
def get_next_iesg_states(iesg_state):
if not iesg_state:
return ()
next = {
"pub-req": ("ad-eval", "watching", "dead"),
"ad-eval": ("watching", "lc-req", "review-e", "iesg-eva"),
"review-e": ("ad-eval", ),
"lc-req": ("lc", ),
"lc": ("writeupw", "goaheadw"),
"writeupw": ("goaheadw", ),
"goaheadw": ("iesg-eva", ),
"iesg-eva": ("nopubadw", "defer", "ann"),
"defer": ("iesg-eva", ),
"ann": ("approved", ),
"approved": ("rfcqueue", ),
"rfcqueue": ("pub", ),
"pub": ("dead", ),
"nopubadw": ("nopubanw", ),
"nopubanw": ("dead", ),
"watching": ("pub-req", ),
"dead": ("pub-req", ),
}
return IesgDocStateName.objects.filter(slug__in=next.get(iesg_state.slug, ()))

View file

@ -65,3 +65,10 @@ class Email(models.Model):
def get_name(self):
return self.person.name if self.person else self.address
def formatted_email(self):
if self.person and self.person.name:
return u"%s <%s>" % (self.person.name, self.address)
else:
return self.address