Skip to content

Commit 1ff4bc0

Browse files
committed
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
1 parent 6524aba commit 1ff4bc0

9 files changed

Lines changed: 192 additions & 170 deletions

File tree

ietf/doc/proxy.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -175,32 +175,6 @@ def review_by_rfc_editor(self):
175175
def expired_tombstone(self):
176176
return False
177177

178-
def calc_process_start_end(self):
179-
import datetime
180-
start, end = datetime.datetime.min, datetime.datetime.max
181-
e = self.latest_event(type="started_iesg_process")
182-
if e:
183-
start = e.time
184-
if self.get_state_slug() == "rfc" and self.name.startswith("draft") and not hasattr(self, "viewing_as_rfc"):
185-
previous_process = self.latest_event(type="started_iesg_process", time__lt=e.time)
186-
if previous_process:
187-
start = previous_process.time
188-
end = e.time
189-
self._process_start = start
190-
self._process_end = end
191-
192-
@property
193-
def process_start(self):
194-
if not hasattr(self, "_process_start"):
195-
self.calc_process_start_end()
196-
return self._process_start
197-
198-
@property
199-
def process_end(self):
200-
if not hasattr(self, "_process_end"):
201-
self.calc_process_start_end()
202-
return self._process_end
203-
204178
#shepherd = BrokenForeignKey('PersonOrOrgInfo', null=True, blank=True, null_values=(0, )) # same name
205179

206180
#idinternal = FKAsOneToOne('idinternal', reverse=True, query=models.Q(rfc_flag = 0))

ietf/doc/utils.py

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,10 @@ def active_ballot_positions(doc, ballot=None):
3535
active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active"))
3636
res = {}
3737

38-
# FIXME: do something with ballot
38+
if not ballot:
39+
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
3940

40-
start = datetime.datetime.min
41-
e = doc.latest_event(type="started_iesg_process")
42-
if e:
43-
start = e.time
44-
45-
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ad__in=active_ads, time__gte=start).select_related('ad').order_by("-time", "-id")
41+
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ad__in=active_ads, ballot=ballot).select_related('ad', 'pos').order_by("-time", "-id")
4642

4743
for pos in positions:
4844
if pos.ad not in res:
@@ -75,7 +71,7 @@ def needed_ballot_positions(doc, active_positions):
7571
if doc.type_id == "draft" and doc.intended_std_level_id in ("bcp", "ps", "ds", "std"):
7672
# For standards-track, need positions from 2/3 of the
7773
# non-recused current IESG.
78-
needed = len(active_positions) - recuse * 2 / 3
74+
needed = len(active_positions) - len(recuse) * 2 / 3
7975

8076
have = len(yes) + len(noobj) + len(blocking)
8177
if have < needed:
@@ -92,21 +88,6 @@ def needed_ballot_positions(doc, active_positions):
9288

9389
return " ".join(answer)
9490

95-
96-
def get_rfc_number(doc):
97-
qs = doc.docalias_set.filter(name__startswith='rfc')
98-
return qs[0].name[3:] if qs else None
99-
100-
def get_chartering_type(doc):
101-
chartering = ""
102-
if doc.get_state_slug() not in ("notrev", "approved"):
103-
if doc.group.state_id == "proposed":
104-
chartering = "initial"
105-
elif doc.group.state_id == "active":
106-
chartering = "rechartering"
107-
108-
return chartering
109-
11091
def ballot_open(doc, ballot_type_slug):
11192
e = doc.latest_event(BallotDocEvent, ballot_type__slug=ballot_type_slug)
11293
return e and not e.type == "closed_ballot"
@@ -126,6 +107,20 @@ def close_open_ballots(doc, by):
126107
e.desc = 'Closed "%s" ballot' % t.name
127108
e.save()
128109

110+
def get_rfc_number(doc):
111+
qs = doc.docalias_set.filter(name__startswith='rfc')
112+
return qs[0].name[3:] if qs else None
113+
114+
def get_chartering_type(doc):
115+
chartering = ""
116+
if doc.get_state_slug() not in ("notrev", "approved"):
117+
if doc.group.state_id == "proposed":
118+
chartering = "initial"
119+
elif doc.group.state_id == "active":
120+
chartering = "rechartering"
121+
122+
return chartering
123+
129124
def augment_with_telechat_date(docs):
130125
"""Add a telechat_date attribute to each document with the
131126
scheduled telechat or None if it's not scheduled."""

ietf/idrfc/idrfc_wrapper.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,9 @@ def _init(self):
733733

734734
new_revisions = list(NewRevisionDocEvent.objects.filter(doc=self.ballot, type="new_revision").order_by('-time', '-id'))
735735

736-
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'):
736+
ballot = self.ballot.latest_event(BallotDocEvent, type="created_ballot")
737+
738+
for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", ballot=ballot).select_related('ad').order_by("-time", '-id'):
737739
if pos.ad not in seen:
738740
p = dict(ad_name=pos.ad.plain_name(),
739741
ad_username=pos.ad.pk, # ought to rename this in doc_ballot_list

ietf/idrfc/mails.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,14 +532,13 @@ def formatted(val):
532532
)
533533
)
534534

535-
def generate_issue_ballot_mailREDESIGN(request, doc):
535+
def generate_issue_ballot_mailREDESIGN(request, doc, ballot):
536536
full_status = full_intended_status(doc.intended_std_level.name)
537537
status = full_status.replace("a ", "").replace("an ", "")
538538

539539
active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct()
540540

541-
e = doc.latest_event(type="started_iesg_process")
542-
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad')
541+
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad')
543542

544543
# format positions and setup discusses and comments
545544
ad_feedback = []

ietf/idrfc/templatetags/ballot_icon.py

Lines changed: 55 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,12 @@
3636
from ietf.idtracker.models import IDInternal, BallotInfo
3737
from ietf.idrfc.idrfc_wrapper import position_to_string, BALLOT_ACTIVE_STATES
3838
from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days
39+
from ietf.ietfauth.decorators import has_role
40+
from ietf.doc.utils import active_ballot_positions
41+
from ietf.doc.models import BallotDocEvent
3942

4043
register = template.Library()
4144

42-
def get_user_adid(context):
43-
if 'user' in context and in_group(context['user'], "Area_Director"):
44-
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
45-
return context['user'].get_profile().id
46-
return context['user'].get_profile().iesg_login_id()
47-
else:
48-
return None
49-
5045
def get_user_name(context):
5146
if 'user' in context and context['user'].is_authenticated():
5247
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
@@ -60,105 +55,64 @@ def get_user_name(context):
6055
if person:
6156
return str(person)
6257
return None
63-
64-
def render_ballot_icon(context, doc):
65-
if isinstance(doc,IDInternal):
66-
try:
67-
ballot = doc.ballot
68-
if not ballot.ballot_issued:
69-
return ""
70-
except BallotInfo.DoesNotExist:
71-
return ""
72-
if str(doc.cur_state) not in BALLOT_ACTIVE_STATES:
73-
return ""
74-
if doc.rfc_flag and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
75-
name = doc.document().filename()
76-
else:
77-
name = doc.document().filename
78-
else:
79-
if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot():
80-
ballot = doc._idinternal.ballot
81-
else:
82-
return ""
83-
if doc.is_rfc_wrapper:
84-
name = "rfc"+str(doc.rfc_number)
85-
else:
86-
name = doc.draft_name
87-
adId = get_user_adid(context)
88-
red = 0
89-
green = 0
90-
yellow = 0
91-
gray = 0
92-
blank = 0
93-
my = None
94-
for p in ballot.active_positions():
95-
if not p['pos']:
96-
blank = blank + 1
97-
elif (p['pos'].yes > 0) or (p['pos'].noobj > 0):
98-
green = green + 1
99-
elif (p['pos'].discuss > 0):
100-
red = red + 1
101-
elif (p['pos'].abstain > 0):
102-
yellow = yellow + 1
103-
elif (p['pos'].recuse > 0):
104-
gray = gray + 1
58+
59+
def render_ballot_icon(user, doc):
60+
if not doc:
61+
return ""
62+
63+
s = doc.get_state("draft-iesg")
64+
if s and s.name not in BALLOT_ACTIVE_STATES:
65+
return ""
66+
67+
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
68+
if not ballot:
69+
return ""
70+
71+
edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
72+
73+
def sort_key(t):
74+
_, pos = t
75+
if not pos:
76+
return (2, 0)
77+
elif pos.pos.blocking:
78+
return (0, pos.pos.order)
10579
else:
106-
blank = blank + 1
107-
if adId and (p['ad'].id == adId):
108-
my = position_to_string(p['pos'])
109-
return render_ballot_icon2(name, red,yellow,green,gray,blank, my, adId)+"<!-- adId="+str(adId)+" my="+str(my)+"-->"
110-
111-
def render_ballot_icon2(draft_name, red,yellow,green,gray,blank, my,adId):
112-
from ietf.doc.models import BallotDocEvent
113-
ballots = BallotDocEvent.objects.filter(doc__docalias__name=draft_name).order_by("-time", "-id")
114-
if ballots:
115-
edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=draft_name, ballot_id=ballots[0].pk))
116-
else:
117-
edit_position_url = ""
118-
if adId:
119-
res_cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
120-
else:
121-
res_cm = ''
122-
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+'>'
123-
for y in range(3):
124-
res = res + "<tr>"
125-
for x in range(5):
126-
myMark = False
127-
if red > 0:
128-
c = "ballot_icon_red"
129-
red = red - 1
130-
myMark = (my == "Discuss")
131-
elif yellow > 0:
132-
c = "ballot_icon_yellow"
133-
yellow = yellow - 1
134-
myMark = (my == "Abstain")
135-
elif green > 0:
136-
c = "ballot_icon_green"
137-
green = green - 1
138-
myMark = (my == "Yes") or (my == "No Objection")
139-
elif gray > 0:
140-
c = "ballot_icon_gray"
141-
gray = gray - 1
142-
myMark = (my == "Recuse")
143-
else:
144-
c = ""
145-
myMark = (y == 2) and (x == 4) and (my == "No Record")
146-
if myMark:
147-
res = res + '<td class="'+c+' ballot_icon_my" />'
148-
my = None
149-
else:
150-
res = res + '<td class="'+c+'" />'
151-
res = res + '</tr>'
152-
res = res + '</table>'
153-
return res
154-
155-
80+
return (1, pos.pos.order)
81+
82+
positions = list(active_ballot_positions(doc, ballot).items())
83+
positions.sort(key=sort_key)
84+
85+
cm = ""
86+
if has_role(user, "Area Director"):
87+
cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
88+
89+
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 + '>']
90+
91+
res.append("<tr>")
92+
93+
for i, (ad, pos) in enumerate(positions):
94+
if i > 0 and i % 5 == 0:
95+
res.append("</tr>")
96+
res.append("<tr>")
97+
98+
c = "position-%s" % (pos.pos.slug if pos else "norecord")
99+
100+
if ad == user.get_profile():
101+
c += " my"
102+
103+
res.append('<td class="%s" />' % c)
104+
105+
res.append("</tr>")
106+
res.append("</table>")
107+
108+
return "".join(res)
109+
156110
class BallotIconNode(template.Node):
157111
def __init__(self, doc_var):
158112
self.doc_var = doc_var
159113
def render(self, context):
160114
doc = template.resolve_variable(self.doc_var, context)
161-
return render_ballot_icon(context, doc)
115+
return render_ballot_icon(context.get("user"), doc._idinternal)
162116

163117
def do_ballot_icon(parser, token):
164118
try:

ietf/idrfc/views_ballot.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ietf.idrfc.lastcall import request_last_call
2727
from ietf.idrfc.idrfc_wrapper import BallotWrapper
2828

29+
from ietf.doc.utils import *
2930
from ietf.doc.models import *
3031
from ietf.name.models import BallotPositionName
3132
from ietf.person.models import Person
@@ -343,6 +344,7 @@ def edit_positionREDESIGN(request, name, ballot_id):
343344
form = EditPositionForm(initial=initial, ballot_type=ballot.ballot_type)
344345

345346
blocking_positions = dict((p.pk, p.name) for p in form.fields["position"].queryset.all() if p.blocking)
347+
print blocking_positions, form.fields["position"].queryset.all()
346348

347349
ballot_deferred = None
348350
if doc.get_state_slug("%s-iesg" % doc.type_id) == "defer":
@@ -933,9 +935,6 @@ def clean_ballot_writeup(self):
933935
def ballot_writeupnotesREDESIGN(request, name):
934936
"""Editing of ballot write-up and notes"""
935937
doc = get_object_or_404(Document, docalias__name=name)
936-
started_process = doc.latest_event(type="started_iesg_process")
937-
if not started_process:
938-
raise Http404()
939938

940939
login = request.user.get_profile()
941940

@@ -958,9 +957,13 @@ def ballot_writeupnotesREDESIGN(request, name):
958957
e.save()
959958

960959
if "issue_ballot" in request.POST:
961-
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, time__gte=started_process.time):
960+
create_ballot_if_not_open(doc, login, "approve")
961+
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
962+
963+
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, ballot=ballot):
962964
# sending the ballot counts as a yes
963965
pos = BallotPositionDocEvent(doc=doc, by=login)
966+
pos.ballot = ballot
964967
pos.type = "changed_ballot_position"
965968
pos.ad = login
966969
pos.pos_id = "yes"
@@ -971,7 +974,7 @@ def ballot_writeupnotesREDESIGN(request, name):
971974
if not approval:
972975
approval = generate_approval_mail(request, doc)
973976

974-
msg = generate_issue_ballot_mail(request, doc)
977+
msg = generate_issue_ballot_mail(request, doc, ballot)
975978
send_mail_preformatted(request, msg)
976979

977980
email_iana(request, doc, 'drafts-eval@icann.org', msg)

ietf/idrfc/views_doc.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,10 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
271271
if latest.old_positions:
272272
prev = latest.old_positions[-1]
273273
else:
274-
prev = latest
274+
prev = latest.pos.name
275275

276-
if e.pos != prev.pos:
277-
latest.old_positions.append(e)
276+
if e.pos.name != prev:
277+
latest.old_positions.append(e.pos.name)
278278

279279
# add any missing ADs through fake No Record events
280280
norecord = BallotPositionName.objects.get(slug="norecord")
@@ -316,9 +316,6 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
316316
context_instance=RequestContext(request))
317317

318318
def document_ballot(request, name, ballot_id=None):
319-
if name.lower().startswith("draft") or name.lower().startswith("rfc"):
320-
return document_main_idrfc(request, name, "ballot")
321-
322319
doc = get_object_or_404(Document, docalias__name=name)
323320
top = render_document_top(request, doc, "ballot")
324321

0 commit comments

Comments
 (0)