Port idrfc code to use new ballot setup with an explicit ballot on
positions, port and clean up ballot icon renderer, add preliminary migration script for inserting extra events in drafts - Legacy-Id: 4274
This commit is contained in:
parent
6524aba8dc
commit
1ff4bc0121
|
@ -175,32 +175,6 @@ class InternetDraft(Document):
|
|||
def expired_tombstone(self):
|
||||
return False
|
||||
|
||||
def calc_process_start_end(self):
|
||||
import datetime
|
||||
start, end = datetime.datetime.min, datetime.datetime.max
|
||||
e = self.latest_event(type="started_iesg_process")
|
||||
if e:
|
||||
start = e.time
|
||||
if self.get_state_slug() == "rfc" and self.name.startswith("draft") and not hasattr(self, "viewing_as_rfc"):
|
||||
previous_process = self.latest_event(type="started_iesg_process", time__lt=e.time)
|
||||
if previous_process:
|
||||
start = previous_process.time
|
||||
end = e.time
|
||||
self._process_start = start
|
||||
self._process_end = end
|
||||
|
||||
@property
|
||||
def process_start(self):
|
||||
if not hasattr(self, "_process_start"):
|
||||
self.calc_process_start_end()
|
||||
return self._process_start
|
||||
|
||||
@property
|
||||
def process_end(self):
|
||||
if not hasattr(self, "_process_end"):
|
||||
self.calc_process_start_end()
|
||||
return self._process_end
|
||||
|
||||
#shepherd = BrokenForeignKey('PersonOrOrgInfo', null=True, blank=True, null_values=(0, )) # same name
|
||||
|
||||
#idinternal = FKAsOneToOne('idinternal', reverse=True, query=models.Q(rfc_flag = 0))
|
||||
|
|
|
@ -35,14 +35,10 @@ def active_ballot_positions(doc, ballot=None):
|
|||
active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active"))
|
||||
res = {}
|
||||
|
||||
# FIXME: do something with ballot
|
||||
if not ballot:
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
|
||||
start = datetime.datetime.min
|
||||
e = doc.latest_event(type="started_iesg_process")
|
||||
if e:
|
||||
start = e.time
|
||||
|
||||
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ad__in=active_ads, time__gte=start).select_related('ad').order_by("-time", "-id")
|
||||
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ad__in=active_ads, ballot=ballot).select_related('ad', 'pos').order_by("-time", "-id")
|
||||
|
||||
for pos in positions:
|
||||
if pos.ad not in res:
|
||||
|
@ -75,7 +71,7 @@ def needed_ballot_positions(doc, active_positions):
|
|||
if doc.type_id == "draft" and doc.intended_std_level_id in ("bcp", "ps", "ds", "std"):
|
||||
# For standards-track, need positions from 2/3 of the
|
||||
# non-recused current IESG.
|
||||
needed = len(active_positions) - recuse * 2 / 3
|
||||
needed = len(active_positions) - len(recuse) * 2 / 3
|
||||
|
||||
have = len(yes) + len(noobj) + len(blocking)
|
||||
if have < needed:
|
||||
|
@ -92,21 +88,6 @@ def needed_ballot_positions(doc, active_positions):
|
|||
|
||||
return " ".join(answer)
|
||||
|
||||
|
||||
def get_rfc_number(doc):
|
||||
qs = doc.docalias_set.filter(name__startswith='rfc')
|
||||
return qs[0].name[3:] if qs else None
|
||||
|
||||
def get_chartering_type(doc):
|
||||
chartering = ""
|
||||
if doc.get_state_slug() not in ("notrev", "approved"):
|
||||
if doc.group.state_id == "proposed":
|
||||
chartering = "initial"
|
||||
elif doc.group.state_id == "active":
|
||||
chartering = "rechartering"
|
||||
|
||||
return chartering
|
||||
|
||||
def ballot_open(doc, ballot_type_slug):
|
||||
e = doc.latest_event(BallotDocEvent, ballot_type__slug=ballot_type_slug)
|
||||
return e and not e.type == "closed_ballot"
|
||||
|
@ -126,6 +107,20 @@ def close_open_ballots(doc, by):
|
|||
e.desc = 'Closed "%s" ballot' % t.name
|
||||
e.save()
|
||||
|
||||
def get_rfc_number(doc):
|
||||
qs = doc.docalias_set.filter(name__startswith='rfc')
|
||||
return qs[0].name[3:] if qs else None
|
||||
|
||||
def get_chartering_type(doc):
|
||||
chartering = ""
|
||||
if doc.get_state_slug() not in ("notrev", "approved"):
|
||||
if doc.group.state_id == "proposed":
|
||||
chartering = "initial"
|
||||
elif doc.group.state_id == "active":
|
||||
chartering = "rechartering"
|
||||
|
||||
return chartering
|
||||
|
||||
def augment_with_telechat_date(docs):
|
||||
"""Add a telechat_date attribute to each document with the
|
||||
scheduled telechat or None if it's not scheduled."""
|
||||
|
|
|
@ -733,7 +733,9 @@ class BallotWrapper:
|
|||
|
||||
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'):
|
||||
ballot = self.ballot.latest_event(BallotDocEvent, type="created_ballot")
|
||||
|
||||
for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", ballot=ballot).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
|
||||
|
|
|
@ -532,14 +532,13 @@ def generate_issue_ballot_mail(request, doc):
|
|||
)
|
||||
)
|
||||
|
||||
def generate_issue_ballot_mailREDESIGN(request, doc):
|
||||
def generate_issue_ballot_mailREDESIGN(request, doc, ballot):
|
||||
full_status = full_intended_status(doc.intended_std_level.name)
|
||||
status = full_status.replace("a ", "").replace("an ", "")
|
||||
|
||||
active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct()
|
||||
|
||||
e = doc.latest_event(type="started_iesg_process")
|
||||
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad')
|
||||
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad')
|
||||
|
||||
# format positions and setup discusses and comments
|
||||
ad_feedback = []
|
||||
|
|
|
@ -36,17 +36,12 @@ from django.conf import settings
|
|||
from ietf.idtracker.models import IDInternal, BallotInfo
|
||||
from ietf.idrfc.idrfc_wrapper import position_to_string, BALLOT_ACTIVE_STATES
|
||||
from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days
|
||||
from ietf.ietfauth.decorators import has_role
|
||||
from ietf.doc.utils import active_ballot_positions
|
||||
from ietf.doc.models import BallotDocEvent
|
||||
|
||||
register = template.Library()
|
||||
|
||||
def get_user_adid(context):
|
||||
if 'user' in context and in_group(context['user'], "Area_Director"):
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
return context['user'].get_profile().id
|
||||
return context['user'].get_profile().iesg_login_id()
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_user_name(context):
|
||||
if 'user' in context and context['user'].is_authenticated():
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
|
@ -60,105 +55,64 @@ def get_user_name(context):
|
|||
if person:
|
||||
return str(person)
|
||||
return None
|
||||
|
||||
def render_ballot_icon(context, doc):
|
||||
if isinstance(doc,IDInternal):
|
||||
try:
|
||||
ballot = doc.ballot
|
||||
if not ballot.ballot_issued:
|
||||
return ""
|
||||
except BallotInfo.DoesNotExist:
|
||||
return ""
|
||||
if str(doc.cur_state) not in BALLOT_ACTIVE_STATES:
|
||||
return ""
|
||||
if doc.rfc_flag and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
name = doc.document().filename()
|
||||
else:
|
||||
name = doc.document().filename
|
||||
else:
|
||||
if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot():
|
||||
ballot = doc._idinternal.ballot
|
||||
else:
|
||||
return ""
|
||||
if doc.is_rfc_wrapper:
|
||||
name = "rfc"+str(doc.rfc_number)
|
||||
else:
|
||||
name = doc.draft_name
|
||||
adId = get_user_adid(context)
|
||||
red = 0
|
||||
green = 0
|
||||
yellow = 0
|
||||
gray = 0
|
||||
blank = 0
|
||||
my = None
|
||||
for p in ballot.active_positions():
|
||||
if not p['pos']:
|
||||
blank = blank + 1
|
||||
elif (p['pos'].yes > 0) or (p['pos'].noobj > 0):
|
||||
green = green + 1
|
||||
elif (p['pos'].discuss > 0):
|
||||
red = red + 1
|
||||
elif (p['pos'].abstain > 0):
|
||||
yellow = yellow + 1
|
||||
elif (p['pos'].recuse > 0):
|
||||
gray = gray + 1
|
||||
else:
|
||||
blank = blank + 1
|
||||
if adId and (p['ad'].id == adId):
|
||||
my = position_to_string(p['pos'])
|
||||
return render_ballot_icon2(name, red,yellow,green,gray,blank, my, adId)+"<!-- adId="+str(adId)+" my="+str(my)+"-->"
|
||||
|
||||
def render_ballot_icon2(draft_name, red,yellow,green,gray,blank, my,adId):
|
||||
from ietf.doc.models import BallotDocEvent
|
||||
ballots = BallotDocEvent.objects.filter(doc__docalias__name=draft_name).order_by("-time", "-id")
|
||||
if ballots:
|
||||
edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=draft_name, ballot_id=ballots[0].pk))
|
||||
else:
|
||||
edit_position_url = ""
|
||||
if adId:
|
||||
res_cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
|
||||
else:
|
||||
res_cm = ''
|
||||
res = '<table class="ballot_icon" title="IESG Evaluation Record (click to show more, right-click to edit position)" onclick="showBallot(\'' + draft_name + '\',\'' + str(edit_position_url) + '\')"'+res_cm+'>'
|
||||
for y in range(3):
|
||||
res = res + "<tr>"
|
||||
for x in range(5):
|
||||
myMark = False
|
||||
if red > 0:
|
||||
c = "ballot_icon_red"
|
||||
red = red - 1
|
||||
myMark = (my == "Discuss")
|
||||
elif yellow > 0:
|
||||
c = "ballot_icon_yellow"
|
||||
yellow = yellow - 1
|
||||
myMark = (my == "Abstain")
|
||||
elif green > 0:
|
||||
c = "ballot_icon_green"
|
||||
green = green - 1
|
||||
myMark = (my == "Yes") or (my == "No Objection")
|
||||
elif gray > 0:
|
||||
c = "ballot_icon_gray"
|
||||
gray = gray - 1
|
||||
myMark = (my == "Recuse")
|
||||
else:
|
||||
c = ""
|
||||
myMark = (y == 2) and (x == 4) and (my == "No Record")
|
||||
if myMark:
|
||||
res = res + '<td class="'+c+' ballot_icon_my" />'
|
||||
my = None
|
||||
else:
|
||||
res = res + '<td class="'+c+'" />'
|
||||
res = res + '</tr>'
|
||||
res = res + '</table>'
|
||||
return res
|
||||
def render_ballot_icon(user, doc):
|
||||
if not doc:
|
||||
return ""
|
||||
|
||||
s = doc.get_state("draft-iesg")
|
||||
if s and s.name not in BALLOT_ACTIVE_STATES:
|
||||
return ""
|
||||
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if not ballot:
|
||||
return ""
|
||||
|
||||
edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
|
||||
|
||||
def sort_key(t):
|
||||
_, pos = t
|
||||
if not pos:
|
||||
return (2, 0)
|
||||
elif pos.pos.blocking:
|
||||
return (0, pos.pos.order)
|
||||
else:
|
||||
return (1, pos.pos.order)
|
||||
|
||||
positions = list(active_ballot_positions(doc, ballot).items())
|
||||
positions.sort(key=sort_key)
|
||||
|
||||
cm = ""
|
||||
if has_role(user, "Area Director"):
|
||||
cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
|
||||
|
||||
res = ['<table class="ballot_icon" title="IESG Evaluation Record (click to show more, right-click to edit position)" onclick="showBallot(\'' + doc.name + '\',\'' + str(edit_position_url) + '\')"' + cm + '>']
|
||||
|
||||
res.append("<tr>")
|
||||
|
||||
for i, (ad, pos) in enumerate(positions):
|
||||
if i > 0 and i % 5 == 0:
|
||||
res.append("</tr>")
|
||||
res.append("<tr>")
|
||||
|
||||
c = "position-%s" % (pos.pos.slug if pos else "norecord")
|
||||
|
||||
if ad == user.get_profile():
|
||||
c += " my"
|
||||
|
||||
res.append('<td class="%s" />' % c)
|
||||
|
||||
res.append("</tr>")
|
||||
res.append("</table>")
|
||||
|
||||
return "".join(res)
|
||||
|
||||
|
||||
class BallotIconNode(template.Node):
|
||||
def __init__(self, doc_var):
|
||||
self.doc_var = doc_var
|
||||
def render(self, context):
|
||||
doc = template.resolve_variable(self.doc_var, context)
|
||||
return render_ballot_icon(context, doc)
|
||||
return render_ballot_icon(context.get("user"), doc._idinternal)
|
||||
|
||||
def do_ballot_icon(parser, token):
|
||||
try:
|
||||
|
|
|
@ -26,6 +26,7 @@ from ietf.idrfc.utils import *
|
|||
from ietf.idrfc.lastcall import request_last_call
|
||||
from ietf.idrfc.idrfc_wrapper import BallotWrapper
|
||||
|
||||
from ietf.doc.utils import *
|
||||
from ietf.doc.models import *
|
||||
from ietf.name.models import BallotPositionName
|
||||
from ietf.person.models import Person
|
||||
|
@ -343,6 +344,7 @@ def edit_positionREDESIGN(request, name, ballot_id):
|
|||
form = EditPositionForm(initial=initial, ballot_type=ballot.ballot_type)
|
||||
|
||||
blocking_positions = dict((p.pk, p.name) for p in form.fields["position"].queryset.all() if p.blocking)
|
||||
print blocking_positions, form.fields["position"].queryset.all()
|
||||
|
||||
ballot_deferred = None
|
||||
if doc.get_state_slug("%s-iesg" % doc.type_id) == "defer":
|
||||
|
@ -933,9 +935,6 @@ class BallotWriteupFormREDESIGN(forms.Form):
|
|||
def ballot_writeupnotesREDESIGN(request, name):
|
||||
"""Editing of ballot write-up and notes"""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
started_process = doc.latest_event(type="started_iesg_process")
|
||||
if not started_process:
|
||||
raise Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
||||
|
@ -958,9 +957,13 @@ def ballot_writeupnotesREDESIGN(request, name):
|
|||
e.save()
|
||||
|
||||
if "issue_ballot" in request.POST:
|
||||
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, time__gte=started_process.time):
|
||||
create_ballot_if_not_open(doc, login, "approve")
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
|
||||
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, ballot=ballot):
|
||||
# sending the ballot counts as a yes
|
||||
pos = BallotPositionDocEvent(doc=doc, by=login)
|
||||
pos.ballot = ballot
|
||||
pos.type = "changed_ballot_position"
|
||||
pos.ad = login
|
||||
pos.pos_id = "yes"
|
||||
|
@ -971,7 +974,7 @@ def ballot_writeupnotesREDESIGN(request, name):
|
|||
if not approval:
|
||||
approval = generate_approval_mail(request, doc)
|
||||
|
||||
msg = generate_issue_ballot_mail(request, doc)
|
||||
msg = generate_issue_ballot_mail(request, doc, ballot)
|
||||
send_mail_preformatted(request, msg)
|
||||
|
||||
email_iana(request, doc, 'drafts-eval@icann.org', msg)
|
||||
|
|
|
@ -271,10 +271,10 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
|
|||
if latest.old_positions:
|
||||
prev = latest.old_positions[-1]
|
||||
else:
|
||||
prev = latest
|
||||
prev = latest.pos.name
|
||||
|
||||
if e.pos != prev.pos:
|
||||
latest.old_positions.append(e)
|
||||
if e.pos.name != prev:
|
||||
latest.old_positions.append(e.pos.name)
|
||||
|
||||
# add any missing ADs through fake No Record events
|
||||
norecord = BallotPositionName.objects.get(slug="norecord")
|
||||
|
@ -316,9 +316,6 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
|
|||
context_instance=RequestContext(request))
|
||||
|
||||
def document_ballot(request, name, ballot_id=None):
|
||||
if name.lower().startswith("draft") or name.lower().startswith("rfc"):
|
||||
return document_main_idrfc(request, name, "ballot")
|
||||
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
top = render_document_top(request, doc, "ballot")
|
||||
|
||||
|
|
102
ietf/wgcharter/migrate.py
Normal file
102
ietf/wgcharter/migrate.py
Normal file
|
@ -0,0 +1,102 @@
|
|||
import sys, os, re, datetime
|
||||
|
||||
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
|
||||
sys.path = [ basedir ] + sys.path
|
||||
|
||||
from ietf import settings
|
||||
from django.core import management
|
||||
management.setup_environ(settings)
|
||||
|
||||
from ietf.doc.models import *
|
||||
|
||||
# make sure ballot positions and types are right
|
||||
BallotPositionName.objects.get_or_create(slug="block",
|
||||
order=3,
|
||||
name="Block",
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
BallotPositionName.objects.filter(slug="discuss").update(blocking=True)
|
||||
|
||||
charter_positions = BallotPositionName.objects.filter(slug__in=["yes", "noobj", "block", "abstain", "norecord" ])
|
||||
|
||||
o,_ = BallotType.objects.get_or_create(doc_type_id="charter",
|
||||
slug="r-extrev",
|
||||
name="Ready for external review",
|
||||
question="Is this charter ready for external review?",
|
||||
order=1,
|
||||
)
|
||||
|
||||
o.positions = charter_positions
|
||||
|
||||
o,_ = BallotType.objects.get_or_create(doc_type_id="charter",
|
||||
slug="r-wo-ext",
|
||||
name="Ready w/o external review",
|
||||
question="Is this charter ready for external review? Is this charter ready for approval without external review?",
|
||||
order=2,
|
||||
)
|
||||
o.positions = charter_positions
|
||||
|
||||
o,_ = BallotType.objects.get_or_create(doc_type_id="charter",
|
||||
slug="approve",
|
||||
name="Approve",
|
||||
question="Do we approve of this charter?",
|
||||
order=3,
|
||||
)
|
||||
o.positions = charter_positions
|
||||
|
||||
draft_ballot,_ = BallotType.objects.get_or_create(doc_type_id="draft",
|
||||
slug="approve",
|
||||
name="Approve",
|
||||
question="",
|
||||
order=1,
|
||||
)
|
||||
draft_ballot.positions = BallotPositionName.objects.filter(slug__in=["yes", "noobj", "discuss", "abstain", "recuse", "norecord"])
|
||||
|
||||
|
||||
# add events for drafts
|
||||
|
||||
# prevent memory from leaking when settings.DEBUG=True
|
||||
from django.db import connection
|
||||
class DontSaveQueries(object):
|
||||
def append(self, x):
|
||||
pass
|
||||
connection.queries = DontSaveQueries()
|
||||
|
||||
relevant_docs = Document.objects.filter(type="draft", docevent__type__in=("changed_ballot_position", "sent_ballot_announcement")).distinct()
|
||||
|
||||
for d in relevant_docs[:1].iterator():
|
||||
# print ""
|
||||
# print d.name
|
||||
# for e in d.docevent_set.order_by("time", "id").select_related("ballotpositiondocevent"):
|
||||
# print e.time, e.type, "BINGO" if e.type == "sent_ballot_announcement" else ""
|
||||
|
||||
|
||||
ballot = None
|
||||
for e in d.docevent_set.order_by("time", "id").select_related("ballotpositiondocevent"):
|
||||
if e.type == "created_ballot":
|
||||
ballot = e
|
||||
|
||||
if e.type == "closed_ballot":
|
||||
ballot = None
|
||||
|
||||
if not ballot and e.type in ("sent_ballot_announcement", "changed_ballot_position"):
|
||||
ballot = BallotDocEvent(doc=e.doc, by=e.by)
|
||||
ballot.type = "created_ballot"
|
||||
ballot.ballot_type = draft_ballot
|
||||
# place new event just before
|
||||
ballot.time = e.time - datetime.timedelta(seconds=1)
|
||||
ballot.desc = u'Created "%s" ballot' % ballot.ballot_type.name
|
||||
ballot.save()
|
||||
|
||||
if e.type == "sent_ballot_announcement":
|
||||
print "added ballot for", d.name
|
||||
else:
|
||||
print "MISSING ballot issue event, added ballot for", d.name
|
||||
|
||||
if e.type == "changed_ballot_position" and not e.ballotpositiondocevent.ballot:
|
||||
e.ballotpositiondocevent.ballot = ballot
|
||||
e.ballotpositiondocevent.save()
|
||||
|
||||
|
||||
# FIXME: close ballot
|
|
@ -122,11 +122,7 @@ table.ietf-table { border-collapse:collapse; border:1px solid #7f7f7f; }
|
|||
|
||||
table.ballot_icon { empty-cells: show; padding: 0; border-spacing: 0; border: 1px solid black; border-collapse: collapse; table-layout:fixed; min-width:35px; background:white; }
|
||||
table.ballot_icon td { border: 1px solid black; height: 7px; width: 6px; padding: 0;}
|
||||
td.ballot_icon_green { background:#80ff80; }
|
||||
td.ballot_icon_red { background: #c00000; color: yellow; }
|
||||
td.ballot_icon_gray { background: #c0c0c0; }
|
||||
td.ballot_icon_yellow { background: #ffff00; }
|
||||
table.ballot_icon td.ballot_icon_my { border: 3px outset black;}
|
||||
table.ballot_icon td.my { border: 3px outset black;}
|
||||
|
||||
.ietf-small { font-size:85%; }
|
||||
.ietf-highlight-y { padding:0 2px;background:yellow;}
|
||||
|
|
Loading…
Reference in a new issue