datatracker/ietf/idrfc/idrfc_wrapper.py
2012-01-24 17:17:24 +00:00

944 lines
34 KiB
Python

# Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# * Neither the name of the Nokia Corporation and/or its
# subsidiary(-ies) nor the names of its contributors may be used
# to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, IESGDiscuss, IESGLogin, DocumentComment, Acronym, IDState
from ietf.idrfc.models import RfcEditorQueue
from ietf.ipr.models import IprRfc, IprDraft, IprDetail
import re
from datetime import date
from django.utils import simplejson as json
from django.db.models import Q
from django.db import models
from django.core.urlresolvers import reverse
from django.conf import settings
import types
BALLOT_ACTIVE_STATES = ['In Last Call',
'Waiting for Writeup',
'Waiting for AD Go-Ahead',
'IESG Evaluation',
'IESG Evaluation - Defer']
def jsonify_helper(obj, keys):
result = {}
for k in keys:
if hasattr(obj, k):
v = getattr(obj, k)
if callable(v):
v = v()
if v == None:
pass
elif isinstance(v, (types.StringType, types.IntType, types.BooleanType, types.LongType, types.ListType, types.UnicodeType)):
result[k] = v
elif isinstance(v, date):
result[k] = str(v)
else:
result[k] = 'Unknown type '+str(type(v))
return result
# Wrappers to make writing templates less painful
# ---------------------------------------------------------------------------
class IdWrapper:
_draft = None
_idinternal = None
is_id_wrapper = True
is_rfc_wrapper = False
draft_name = None
# Active/Expired/RFC/Withdrawn by Submitter/Replaced/Withdrawn by IETF
draft_status = None
# Revision is sometimes incorrect (+1 too large) if status != Active
latest_revision = None
# Set if and only if draft_status is "RFC"
rfc_number = None
title = None
tracker_id = None
publication_date = None
ietf_process = None
def __init__(self, draft):
self.id = self
if isinstance(draft, IDInternal) and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
self._idinternal = draft
self._draft = self._idinternal.draft
else:
self._draft = draft
if draft.idinternal:
self._idinternal = draft.idinternal
if self._idinternal:
self.ietf_process = IetfProcessData(self._idinternal)
self.draft_name = self._draft.filename
self.draft_status = str(self._draft.status)
if self.draft_status == "RFC":
if self._draft.rfc_number:
self.rfc_number = self._draft.rfc_number
else:
# Handle incorrect database entries
self.draft_status = "Expired"
self.latest_revision = self._draft.revision_display()
self.title = self._draft.title
self.tracker_id = self._draft.id_document_tag
self.resurrect_requested_by = self._idinternal.resurrect_requested_by if self._idinternal else None
self.publication_date = self._draft.revision_date
if not self.publication_date:
# should never happen -- but unfortunately it does. Return an
# obviously bogus date
self.publication_date = date(1990,1,1)
def rfc_editor_state(self):
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
s = self._draft.get_state("draft-rfceditor")
if s:
# extract possible extra states
tags = self._draft.tags.filter(slug__in=("iana-crd", "ref", "missref"))
return " ".join([s.name] + [t.slug.replace("-crd", "").upper() for t in tags])
else:
return None
try:
qs = self._draft.rfc_editor_queue_state
return qs.state
except RfcEditorQueue.DoesNotExist:
pass
return None
def replaced_by(self):
try:
if self._draft.replaced_by:
return [self._draft.replaced_by.filename]
except InternetDraft.DoesNotExist:
pass
return None
def replaces(self):
r = [str(r.filename) for r in self._draft.replaces_set.all()]
if len(r) > 0:
return r
else:
return None
def in_ietf_process(self):
return self.ietf_process != None
def submission(self):
if self._idinternal and self._idinternal.via_rfc_editor:
return "Via IRTF or RFC Editor"
if self._draft.group_id == Acronym.INDIVIDUAL_SUBMITTER:
return "Individual"
a = self.group_acronym()
if a:
return "<a href='/wg/%s/'>%s WG</a>" % (a, a)
return ""
submission.allow_tags = True
def search_archive(self):
if self._idinternal and self._idinternal.via_rfc_editor:
return "www.ietf.org/mail-archive/web/"
if self._draft.group_id == Acronym.INDIVIDUAL_SUBMITTER:
return "www.ietf.org/mail-archive/web/"
a = self._draft.group_ml_archive()
if a:
return a
return ""
def file_types(self):
return self._draft.file_type.split(",")
def group_acronym(self):
if self._draft.group_id != 0 and self._draft.group != None and str(self._draft.group) != "none":
return str(self._draft.group)
else:
return None
# TODO: Returning integers here isn't nice
# 0=Unknown, 1=IETF, 2=IAB, 3=IRTF, 4=Independent
def stream_id(self):
if self.draft_name.startswith("draft-iab-"):
return 2
elif self.draft_name.startswith("draft-irtf-"):
return 3
elif self._idinternal:
if self._idinternal.via_rfc_editor > 0:
return 4
else:
return 1
elif self.group_acronym():
return 1
else:
return 0
def draft_name_and_revision(self):
return self.draft_name+"-"+self.latest_revision
def friendly_state(self):
if self.draft_status == "RFC":
return "<a href=\"%s\">RFC %d</a>" % (reverse('doc_view', args=['rfc%d' % self.rfc_number]), self.rfc_number)
elif self.draft_status == "Replaced":
rs = self.replaced_by()
if rs:
return "Replaced by <a href=\"%s\">%s</a>" % (reverse('doc_view', args=[rs[0]]),rs[0], )
else:
return "Replaced"
elif self.draft_status == "Active":
if self.in_ietf_process():
if self.ietf_process.main_state == "Dead":
# Many drafts in "Dead" state are not dead; they're
# just not currently under IESG processing. Show
# them as "I-D Exists (IESG: Dead)" instead...
return "I-D Exists (IESG: "+self.ietf_process.state+")"
elif self.ietf_process.main_state == "In Last Call":
return self.ietf_process.state + " (ends "+str(self._idinternal.document().lc_expiration_date)+")"
else:
return self.ietf_process.state
else:
return "I-D Exists"
else:
if self.in_ietf_process() and self.ietf_process.main_state == "Dead":
return self.draft_status+" (IESG: "+self.ietf_process.state+")"
# Expired/Withdrawn by Submitter/IETF
return self.draft_status
def abstract(self):
return self._draft.clean_abstract()
# TODO: ugly hack
def authors(self):
return self._draft.authors
def expected_expiration_date(self):
if self.draft_status == "Active" and self._draft.can_expire():
return self._draft.expiration()
else:
return None
def ad_name(self):
if self.in_ietf_process():
return self.ietf_process.ad_name()
else:
return None
def get_absolute_url(self):
return "/doc/"+self.draft_name+"/"
def displayname_with_link(self):
return '<a href="%s">%s</a>' % (self.get_absolute_url(), self.draft_name_and_revision())
def to_json(self):
result = jsonify_helper(self, ['draft_name', 'draft_status', 'latest_revision', 'rfc_number', 'title', 'tracker_id', 'publication_date','rfc_editor_state', 'replaced_by', 'replaces', 'in_ietf_process', 'file_types', 'group_acronym', 'stream_id','friendly_state', 'abstract', 'ad_name'])
if self.in_ietf_process():
result['ietf_process'] = self.ietf_process.dict()
return json.dumps(result, indent=2)
# ---------------------------------------------------------------------------
class RfcWrapper:
_rfc = None
_rfcindex = None
_idinternal = None
is_id_wrapper = False
is_rfc_wrapper = True
rfc_number = None
title = None
publication_date = None
maturity_level = None
ietf_process = None
draft_name = None
def __init__(self, rfcindex, rfc=None, idinternal=None):
self._rfcindex = rfcindex
self._rfc = rfc
self._idinternal = idinternal
self.rfc = self
if not self._idinternal:
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
pub = rfcindex.rfc_published_date
started = rfcindex.started_iesg_process if hasattr(rfcindex, 'started_iesg_process') else rfcindex.latest_event(type="started_iesg_process")
if pub and started and pub < started.time.date():
self._idinternal = rfcindex
else:
try:
self._idinternal = IDInternal.objects.get(rfc_flag=1, draft=self._rfcindex.rfc_number)
except IDInternal.DoesNotExist:
pass
if self._idinternal:
self.ietf_process = IetfProcessData(self._idinternal)
self.rfc_number = self._rfcindex.rfc_number
self.title = self._rfcindex.title
self.publication_date = self._rfcindex.rfc_published_date
self.maturity_level = self._rfcindex.current_status
if not self.maturity_level:
self.maturity_level = "Unknown"
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
if not rfcindex.name.startswith('rfc'):
self.draft_name = rfcindex.name
return # we've already done the lookup while importing so skip the rest
ids = InternetDraft.objects.filter(rfc_number=self.rfc_number)
if len(ids) >= 1:
self.draft_name = ids[0].filename
elif self._rfcindex and self._rfcindex.draft:
# rfcindex occasionally includes drafts that were not
# really submitted to IETF (e.g. April 1st)
ids = InternetDraft.objects.filter(filename=self._rfcindex.draft)
if len(ids) > 0:
self.draft_name = self._rfcindex.draft
def _rfc_doc_list(self, name):
if (not self._rfcindex) or (not getattr(self._rfcindex, name)):
return None
else:
s = getattr(self._rfcindex, name)
s = s.replace(",", ", ")
s = re.sub("([A-Z])([0-9])", "\\1 \\2", s)
return s
def obsoleted_by(self):
return self._rfc_doc_list("obsoleted_by")
def obsoletes(self):
return self._rfc_doc_list("obsoletes")
def updated_by(self):
return self._rfc_doc_list("updated_by")
def updates(self):
return self._rfc_doc_list("updates")
def also(self):
return self._rfc_doc_list("also")
def has_errata(self):
return self._rfcindex and (self._rfcindex.has_errata > 0)
def stream_name(self):
if not self._rfcindex:
return None
else:
x = self._rfcindex.stream
if x == "INDEPENDENT":
return "Independent Submission Stream"
elif x == "LEGACY":
return "Legacy Stream"
else:
return x+" Stream"
def in_ietf_process(self):
return self.ietf_process != None
def file_types(self):
types = self._rfcindex.file_formats
types = types.replace("ascii","txt")
return ["."+x for x in types.split(",")]
def friendly_state(self):
if self.in_ietf_process():
s = self.ietf_process.main_state
if not s in ["RFC Published", "AD is watching", "Dead"]:
return "RFC %d (%s)<br/>%s (to %s)" % (self.rfc_number, self.maturity_level, self.ietf_process.state, self.ietf_process.intended_maturity_level())
return "RFC %d (%s)" % (self.rfc_number, self.maturity_level)
def ad_name(self):
if self.in_ietf_process():
return self.ietf_process.ad_name()
else:
# TODO: get AD name of the draft
return None
@models.permalink
def get_absolute_url(self):
return ('ietf.idrfc.views_doc.document_main', ['rfc%s' % (str(self.rfc_number))])
def displayname_with_link(self):
return '<a href="%s">RFC %d</a>' % (self.get_absolute_url(), self.rfc_number)
def to_json(self):
result = jsonify_helper(self, ['rfc_number', 'title', 'publication_date', 'maturity_level', 'obsoleted_by','obsoletes','updated_by','updates','also','has_errata','stream_name','file_types','in_ietf_process', 'friendly_state'])
if self.in_ietf_process():
result['ietf_process'] = self.ietf_process.dict()
return json.dumps(result, indent=2)
# ---------------------------------------------------------------------------
class IetfProcessData:
_idinternal = None
main_state = None
sub_state = None
state = None
_ballot = None
def __init__(self, idinternal):
self._idinternal = idinternal
i = self._idinternal
self.main_state = str(i.cur_state)
if i.cur_sub_state_id > 0:
self.sub_state = str(i.cur_sub_state)
self.state = self.main_state + "::" + self.sub_state
else:
self.sub_state = None
self.state = self.main_state
def has_iesg_ballot(self):
try:
if self._idinternal.ballot.ballot_issued:
return True
except BallotInfo.DoesNotExist:
pass
return False
def has_active_iesg_ballot(self):
if not self.has_iesg_ballot():
return False
if not self.main_state in BALLOT_ACTIVE_STATES:
return False
if (not self._idinternal.rfc_flag) and self._idinternal.draft.status_id != 1:
# Active
return False
return True
# don't call this unless has_[active_]iesg_ballot returns True
def iesg_ballot(self):
if not self._ballot:
self._ballot = BallotWrapper(self._idinternal)
return self._ballot
# don't call this unless has_[active_]iesg_ballot returns True
def iesg_ballot_needed( self ):
standardsTrack = 'Standard' in self.intended_maturity_level() or \
self.intended_maturity_level() in ("BCP", "Best Current Practice")
return self.iesg_ballot().ballot.needed( standardsTrack )
def ad_name(self):
return str(self._idinternal.job_owner)
def iesg_note(self):
if self._idinternal.note:
n = self._idinternal.note
# Hide unnecessary note of form "RFC 1234"
if re.match("^RFC\s*\d+$", n):
return None
return n
else:
return None
def state_date(self):
try:
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
return self._idinternal.docevent_set.filter(
Q(desc__istartswith="Draft Added by ")|
Q(desc__istartswith="Draft Added in state ")|
Q(desc__istartswith="Draft added in state ")|
Q(desc__istartswith="State changed to ")|
Q(desc__istartswith="State Changes to ")|
Q(desc__istartswith="Sub state has been changed to ")|
Q(desc__istartswith="State has been changed to ")|
Q(desc__istartswith="IESG has approved and state has been changed to")).order_by('-time')[0].time.date()
return self._idinternal.comments().filter(
Q(comment_text__istartswith="Draft Added by ")|
Q(comment_text__istartswith="Draft Added in state ")|
Q(comment_text__istartswith="Draft added in state ")|
Q(comment_text__istartswith="State changed to ")|
Q(comment_text__istartswith="State Changes to ")|
Q(comment_text__istartswith="Sub state has been changed to ")|
Q(comment_text__istartswith="State has been changed to ")|
Q(comment_text__istartswith="IESG has approved and state has been changed to")).order_by('-id')[0].date
except IndexError:
# should never happen -- return an obviously bogus date
return date(1990,1,1)
def dict(self):
result = {'main_state':self.main_state,
'sub_state':self.sub_state,
'state':self.state,
'state_date':str(self.state_date()),
'has_iesg_ballot':self.has_iesg_ballot(),
'has_active_iesg_ballot':self.has_active_iesg_ballot(),
'ad_name':self.ad_name(),
'intended_maturity_level':self.intended_maturity_level(),
'telechat_date':self.telechat_date()}
if result['telechat_date']:
result['telechat_date'] = str(result['telechat_date'])
result['telechat_returning_item'] = self.telechat_returning_item()
if self.iesg_note():
result['iesg_note'] = self.iesg_note()
if self.has_iesg_ballot():
result['iesg_ballot'] = self.iesg_ballot().dict()
return result
def intended_maturity_level(self):
if self._idinternal.rfc_flag:
s = str(self._idinternal.document().intended_status)
# rfc_intend_status table uses different names, argh!
if s == "Proposed":
s = "Proposed Standard"
elif s == "Draft":
s = "Draft Standard"
elif s == "None":
s = None
else:
s = str(self._idinternal.draft.intended_status)
if s == "None":
s = None
elif s == "Request":
s = None
return s
def telechat_date(self):
# return date only if it's on upcoming agenda
if self._idinternal.agenda:
return self._idinternal.telechat_date
else:
return None
def telechat_returning_item(self):
# should be called only if telechat_date() returns non-None
return bool(self._idinternal.returning_item)
def state_change_notice_to(self):
return self._idinternal.state_change_notice_to
# comment_log?
# ---------------------------------------------------------------------------
class IdRfcWrapper:
rfc = None
id = None
iprCount = None
iprUrl = None
def __init__(self, id, rfc):
self.id = id
self.rfc = rfc
if id:
iprs = IprDraft.objects.filter(document=self.id.tracker_id, ipr__status__in=[1,3])
self.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(self.id.tracker_id)
elif rfc:
iprs = IprRfc.objects.filter(document=self.rfc.rfc_number, ipr__status__in=[1,3])
self.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(self.rfc.rfc_number)
else:
raise ValueError("Construction with null id and rfc")
# iprs is a list of docs which contain IPR
self.iprCount = len(iprs)
def title(self):
if self.rfc:
return self.rfc.title
else:
return self.id.title
def friendly_state(self):
if self.rfc:
return self.rfc.friendly_state()
else:
return self.id.friendly_state()
def get_absolute_url(self):
if self.rfc:
return self.rfc.get_absolute_url()
else:
return self.id.get_absolute_url()
def comment_count(self):
if self.rfc:
return DocumentComment.objects.filter(document=self.rfc.rfc_number,rfc_flag=1).count()
else:
return DocumentComment.objects.filter(document=self.id.tracker_id).exclude(rfc_flag=1).count()
def ad_name(self):
if self.rfc:
s = self.rfc.ad_name()
if s:
return s
if self.id:
return self.id.ad_name()
return None
def publication_date(self):
if self.rfc:
return self.rfc.publication_date
else:
return self.id.publication_date
def telechat_date(self):
if self.rfc and self.rfc.in_ietf_process():
return self.rfc.ietf_process.telechat_date()
elif self.id and self.id.in_ietf_process():
return self.id.ietf_process.telechat_date()
else:
return None
def view_sort_group(self):
if self.rfc:
return 'RFC'
elif self.id.draft_status == "Active":
return 'Active Internet-Draft'
else:
return 'Old Internet-Draft'
def view_sort_group_byad(self):
if self.rfc:
return 'RFC'
elif self.id.draft_status == "Active":
if self.id.in_ietf_process():
if self.id.ietf_process._idinternal.cur_state_id == IDState.DEAD:
return 'IESG Dead Internet-Draft'
else:
return "%s Internet-Draft" % self.id.ietf_process._idinternal.cur_state
else:
return 'Active Internet-Draft'
else:
return 'Old Internet-Draft'
def view_sort_key(self, sort_by=None):
if sort_by is None:
if self.rfc:
return "2%04d" % self.rfc.rfc_number
elif self.id.draft_status == "Active":
return "1"+self.id.draft_name
else:
return "3"+self.id.draft_name
else:
if self.rfc:
sort_key = "2"
elif self.id.draft_status == "Active":
sort_key = "1"
else:
sort_key = "3"
# Depending on what we're sorting on, we may
# need to do some conversion.
if sort_by == "title":
sort_key += self.title()
elif sort_by == "date":
sort_key = sort_key + str(self.publication_date())
elif sort_by == "status":
if self.rfc:
sort_key += "%04d" % self.rfc.rfc_number
else:
sort_key += self.id.draft_status
elif sort_by == "ipr":
sort_key += self.iprUrl
elif sort_by == "ad":
return self.view_sort_key_byad()
else:
# sort default or unknown sort value, revert to default
if self.rfc:
sort_key += "%04d" % self.rfc.rfc_number
else:
sort_key += self.id.draft_name
return sort_key
def view_sort_key_byad(self):
if self.rfc:
return "2%04d" % self.rfc.rfc_number
elif self.id.draft_status == "Active":
if self.id.in_ietf_process():
return "11%02d" % (self.id.ietf_process._idinternal.cur_state_id)
else:
return "10"
else:
return "3"
# ---------------------------------------------------------------------------
class BallotWrapper:
_idinternal = None
ballot = None
ballot_active = False
_positions = None
position_values = ["Discuss", "Yes", "No Objection", "Abstain", "Recuse", "No Record"]
def __init__(self, idinternal):
self._idinternal = idinternal
self.ballot = idinternal.ballot
if not idinternal.rfc_flag:
self.ballot_active = self.ballot.ballot_issued and (str(idinternal.cur_state) in BALLOT_ACTIVE_STATES) and str(idinternal.draft.status)=="Active";
else:
self.ballot_active = self.ballot.ballot_issued and (str(idinternal.cur_state) in BALLOT_ACTIVE_STATES)
self._ballot_set = None
def approval_text(self):
return self.ballot.approval_text
def ballot_writeup(self):
return self.ballot.ballot_writeup
def is_active(self):
return self.ballot_active
def ballot_id(self):
return self._idinternal.ballot_id
def was_deferred(self):
return self.ballot.defer
def deferred_by(self):
return self.ballot.defer_by
def deferred_date(self):
return self.ballot.defer_date
def is_ballot_set(self):
if not self._ballot_set:
self._ballot_set = self._idinternal.ballot_set()
return len(list(self._ballot_set)) > 1
def ballot_set_other(self):
if not self.is_ballot_set():
return []
else:
return self._ballot_set.exclude(draft=self._idinternal)
def _init(self):
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
self.old_init()
return
from ietf.person.models import Person
from ietf.doc.models import BallotPositionDocEvent, NewRevisionDocEvent
active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct()
positions = []
seen = {}
new_revisions = list(NewRevisionDocEvent.objects.filter(doc=self.ballot, type="new_revision").order_by('-time', '-id'))
for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", time__gte=self.ballot.process_start, time__lte=self.ballot.process_end).select_related('ad').order_by("-time", '-id'):
if pos.ad not in seen:
p = dict(ad_name=pos.ad.plain_name(),
ad_username=pos.ad.pk, # ought to rename this in doc_ballot_list
position=pos.pos.name,
is_old_ad=pos.ad not in active_ads,
old_positions=[])
rev = pos.doc.rev
for n in new_revisions:
if n.time <= pos.time:
rev = n.rev
break
if pos.pos.slug == "discuss":
p["has_text"] = True
p["discuss_text"] = pos.discuss
p["discuss_date"] = pos.discuss_time.date()
p["discuss_revision"] = rev
if pos.comment:
p["has_text"] = True
p["comment_text"] = pos.comment
p["comment_date"] = pos.comment_time.date()
p["comment_revision"] = rev
positions.append(p)
seen[pos.ad] = p
else:
latest = seen[pos.ad]
if latest["old_positions"]:
prev = latest["old_positions"][-1]
else:
prev = latest["position"]
if prev != pos.pos.name:
seen[pos.ad]["old_positions"].append(pos.pos.name)
# add any missing ADs as No Record
if self.ballot_active:
for ad in active_ads:
if ad not in seen:
d = dict(ad_name=ad.plain_name(),
ad_username=ad.pk,
position="No Record",
)
positions.append(d)
self._positions = positions
def old_init(self):
try:
ads = set()
except NameError:
# for Python 2.3
from sets import Set as set
ads = set()
positions = []
all_comments = self.ballot.comments.all().select_related('ad')
for p in self.ballot.positions.all().select_related('ad'):
po = create_position_object(self.ballot, p, all_comments)
#if not self.ballot_active:
# if 'is_old_ad' in po:
# del po['is_old_ad']
ads.add(str(p.ad))
positions.append(po)
for c in all_comments:
if (str(c.ad) not in ads) and c.ad.is_current_ad():
positions.append({'has_text':True,
'comment_text':c.text,
'comment_date':c.date,
'comment_revision':str(c.revision),
'ad_name':str(c.ad),
'ad_username': c.ad.login_name,
'position':'No Record',
'is_old_ad':False})
ads.add(str(c.ad))
if self.ballot_active:
for ad in IESGLogin.active_iesg():
if str(ad) not in ads:
positions.append(dict(ad_name=str(ad),
ad_username=ad.login_name,
position="No Record"))
self._positions = positions
def position_for_ad(self, ad_name):
pl = self.position_list()
for p in pl:
if p["ad_name"] == ad_name:
return p["position"]
return None
def position_list(self):
if not self._positions:
self._init()
return self._positions
def get(self, v):
return [p for p in self.position_list() if p['position']==v]
def get_discuss(self):
return self.get("Discuss")
def get_yes(self):
return self.get("Yes")
def get_no_objection(self):
return self.get("No Objection")
def get_abstain(self):
return self.get("Abstain")
def get_recuse(self):
return self.get("Recuse")
def get_no_record(self):
return self.get("No Record")
def get_texts(self):
return [p for p in self.position_list() if ('has_text' in p) and p['has_text']]
def dict(self):
summary = {}
for key in self.position_values:
tag = key.lower().replace(" ", "_")
summary[tag] = [ pos["ad_name"] for pos in self.get(key) ]
positions = self.position_list()
for i in range(len(positions)):
for key in ["comment_date", "discuss_date", ]:
if key in positions[i]:
positions[i][key] = positions[i][key].strftime("%Y-%m-%d")
return {
"active": self.is_active(),
"approval_text": self.approval_text(),
"ballot_writeup": self.ballot_writeup(),
"ballot_id": self.ballot_id(),
"deferred_by": unicode(self.deferred_by()),
"deferred_date": self.deferred_date() and self.deferred_date().strftime("%Y-%m-%d") ,
"positions": positions,
"summary": summary,
"was_deferred": self.was_deferred(),
}
def position_to_string(position):
positions = {"yes":"Yes",
"noobj":"No Objection",
"discuss":"Discuss",
"abstain":"Abstain",
"recuse":"Recuse"}
if not position:
return "No Record"
p = None
for k,v in positions.iteritems():
if getattr(position, k) > 0:
p = v
if not p:
p = "No Record"
return p
def create_position_object(ballot, position, all_comments):
positions = {"yes":"Yes",
"noobj":"No Objection",
"discuss":"Discuss",
"abstain":"Abstain",
"recuse":"Recuse"}
p = None
for k,v in positions.iteritems():
if position.__dict__[k] > 0:
p = v
if not p:
p = "No Record"
r = dict(ad_name=str(position.ad),
ad_username=position.ad.login_name,
position=p)
if not position.ad.is_current_ad():
r['is_old_ad'] = True
else:
r['is_old_ad'] = False
was = [v for k,v in positions.iteritems() if position.__dict__[k] < 0]
if len(was) > 0:
r['old_positions'] = was
comment = None
for c in all_comments:
if c.ad == position.ad:
comment = c
break
if comment and comment.text:
r['has_text'] = True
r['comment_text'] = comment.text
r['comment_date'] = comment.date
r['comment_revision'] = str(comment.revision)
if p == "Discuss":
try:
discuss = ballot.discusses.get(ad=position.ad)
if discuss.text:
r['discuss_text'] = discuss.text
else:
r['discuss_text'] = '(empty)'
r['discuss_revision'] = str(discuss.revision)
r['discuss_date'] = discuss.date
except IESGDiscuss.DoesNotExist:
# this should never happen, but unfortunately it does
# fill in something to keep other parts of the code happy
r['discuss_text'] = "(error: discuss text not found)"
r['discuss_revision'] = "00"
r['discuss_date'] = date(2000, 1,1)
r['has_text'] = True
return r