From 87ebddd0ba6aa8d758a93577f950a1ca74d240d8 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Fri, 17 Aug 2012 20:45:32 +0000 Subject: [PATCH] Simplified access to current BallotDocevent from a Document. Added functions to BallotDocEvents to faciliate access to BallotPositionDocEvents, both for all positions, and current AD postions. Updated the moderator package to use the Documents from _agenda_data. Added a filter to assist with rendering the moderator package. Fixed a bug where different functions in idrfc/views_ballot were using log_state_changed expecting different implementations (a cleanup task should reconcile the _three_ implementations in the codebase of that function). - Legacy-Id: 4768 --- ietf/doc/models.py | 73 ++++++++++++++----- ietf/doc/proxy.py | 2 +- ietf/idrfc/templatetags/ballot_icon.py | 2 +- .../templatetags/ballot_icon_redesign.py | 2 +- ietf/idrfc/views_ballot.py | 17 +++-- ietf/idrfc/views_doc.py | 35 +-------- ietf/idtracker/templatetags/ietf_filters.py | 9 +++ ietf/submit/utils.py | 2 +- ietf/templates/iesg/moderator_doc.html | 30 ++++---- ietf/templates/iesg/moderator_package.html | 2 +- ietf/templates/iesg/scribe_doc.html | 2 +- ietf/templates/iesg/scribe_doc2.html | 2 +- 12 files changed, 100 insertions(+), 78 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 7f65b78cc..94ae31a44 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -271,23 +271,9 @@ class Document(DocumentInfo): open = self.ballot_open(ballot.ballot_type.slug) if ballot else False return ballot if open else None - def active_ballot_positions(self): - """Return dict mapping each active AD to a current ballot position (or None if they haven't voted).""" - active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active")) - res = {} - - ballot = self.latest_event(BallotDocEvent, type="created_ballot") - positions = BallotPositionDocEvent.objects.filter(doc=self, 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: - res[pos.ad] = pos - - for ad in active_ads: - if ad not in res: - res[ad] = None - - return res + def most_recent_ietflc(self): + """Returns the most recent IETF LastCallDocEvent for this document""" + return self.latest_event(LastCallDocEvent,type="sent_last_call") def displayname_with_link(self): return '%s-%s' % (self.get_absolute_url(), self.name , self.rev) @@ -532,6 +518,59 @@ class BallotType(models.Model): class BallotDocEvent(DocEvent): ballot_type = models.ForeignKey(BallotType) + def active_ad_positions(self): + """Return dict mapping each active AD to a current ballot position (or None if they haven't voted).""" + active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active")) + res = {} + + if self.doc.latest_event(BallotDocEvent, type="created_ballot") == self: + + positions = BallotPositionDocEvent.objects.filter(type="changed_ballot_position",ad__in=active_ads, ballot=self).select_related('ad', 'pos').order_by("-time", "-id") + + for pos in positions: + if pos.ad not in res: + res[pos.ad] = pos + + for ad in active_ads: + if ad not in res: + res[ad] = None + return res + + def all_positions(self): + """Return array holding the current and past positions per AD""" + + positions = [] + seen = {} + active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active").distinct()) + for e in BallotPositionDocEvent.objects.filter(type="changed_ballot_position", ballot=self).select_related('ad', 'pos').order_by("-time", '-id'): + if e.ad not in seen: + e.old_ad = e.ad not in active_ads + e.old_positions = [] + positions.append(e) + seen[e.ad] = e + else: + latest = seen[e.ad] + if latest.old_positions: + prev = latest.old_positions[-1] + else: + prev = latest.pos + + if e.pos != prev: + latest.old_positions.append(e.pos) + + # add any missing ADs through fake No Record events + norecord = BallotPositionName.objects.get(slug="norecord") + for ad in active_ads: + if ad not in seen: + e = BallotPositionDocEvent(type="changed_ballot_position", doc=self.doc, ad=ad) + e.pos = norecord + e.old_ad = False + e.old_positions = [] + positions.append(e) + + positions.sort(key=lambda p: (p.old_ad, p.ad.last_name())) + return positions + class BallotPositionDocEvent(DocEvent): ballot = models.ForeignKey(BallotDocEvent, null=True, default=None) # default=None is a temporary migration period fix, should be removed when charter branch is live ad = models.ForeignKey(Person) diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py index 6e7cdcbbc..c34956792 100644 --- a/ietf/doc/proxy.py +++ b/ietf/doc/proxy.py @@ -552,7 +552,7 @@ class InternetDraft(Document): from ietf.person.proxy import IESGLogin as IESGLoginProxy res = [] - for ad, pos in self.active_ballot_positions().iteritems(): + for ad, pos in self.active_ballot().active_ad_positions().iteritems(): res.append(dict(ad=IESGLoginProxy().from_object(ad), pos=Position().from_object(pos) if pos else None)) res.sort(key=lambda x: x["ad"].last_name) diff --git a/ietf/idrfc/templatetags/ballot_icon.py b/ietf/idrfc/templatetags/ballot_icon.py index 0f797e20a..2afdd29bf 100644 --- a/ietf/idrfc/templatetags/ballot_icon.py +++ b/ietf/idrfc/templatetags/ballot_icon.py @@ -82,7 +82,7 @@ def render_ballot_icon(user, doc): else: return (1, pos.pos.order) - positions = list(doc.active_ballot_positions().items()) + positions = list(doc.active_ballot().active_ad_positions().items()) positions.sort(key=sort_key) cm = "" diff --git a/ietf/idrfc/templatetags/ballot_icon_redesign.py b/ietf/idrfc/templatetags/ballot_icon_redesign.py index cfbbd2776..753d31bd2 100644 --- a/ietf/idrfc/templatetags/ballot_icon_redesign.py +++ b/ietf/idrfc/templatetags/ballot_icon_redesign.py @@ -89,7 +89,7 @@ def render_ballot_icon(user, doc): else: return (1, pos.pos.order) - positions = list(doc.active_ballot_positions().items()) + positions = list(doc.active_ballot().active_ad_positions().items()) positions.sort(key=sort_key) cm = "" diff --git a/ietf/idrfc/views_ballot.py b/ietf/idrfc/views_ballot.py index 2afc08bab..79c63fef6 100644 --- a/ietf/idrfc/views_ballot.py +++ b/ietf/idrfc/views_ballot.py @@ -35,7 +35,8 @@ from ietf.name.models import BallotPositionName from ietf.message.utils import infer_message from ietf.person.models import Person -from ietf.doc.utils import log_state_changed +from ietf.doc.utils import log_state_changed as docutil_log_state_changed +from ietf.idrfc.utils import log_state_changed as idrfcutil_log_state_changed BALLOT_CHOICES = (("yes", "Yes"), ("noobj", "No Objection"), @@ -431,7 +432,7 @@ def defer_ballotREDESIGN(request, name): elif doc.type_id == 'conflrev': doc.set_state(State.objects.get(type='conflrev', slug='defer')) - e = log_state_changed(request, doc, login, doc.friendly_state(), prev_state) + e = docutil_log_state_changed(request, doc, login, doc.friendly_state(), prev_state) doc.time = e.time doc.save() @@ -481,7 +482,7 @@ def undefer_ballotREDESIGN(request, name): elif doc.type_id == 'conflrev': doc.set_state(State.objects.get(type='conflrev',slug='iesgeval')) - e = log_state_changed(request, doc, login, doc.friendly_state(), prev_state) + e = docutil_log_state_changed(request, doc, login, doc.friendly_state(), prev_state) doc.time = e.time doc.save() @@ -557,7 +558,7 @@ def lastcalltext(request, name): if "send_last_call_request" in request.POST: doc.idinternal.change_state(IDState.objects.get(document_state_id=IDState.LAST_CALL_REQUESTED), None) - change = log_state_changed(request, doc, login) + change = idrfcutil_log_state_changed(request, doc, login) email_owner(request, doc, doc.idinternal.job_owner, login, change) request_last_call(request, doc) @@ -646,7 +647,7 @@ def lastcalltextREDESIGN(request, name): if prev_tag: doc.tags.remove(prev_tag) - e = log_state_changed(request, doc, login, prev, prev_tag) + e = idrfcutil_log_state_changed(request, doc, login, prev, prev_tag) doc.time = e.time doc.save() @@ -1093,7 +1094,7 @@ def approve_ballotREDESIGN(request, name): change_description = e.desc + " and state has been changed to %s" % doc.get_state("draft-iesg").name - e = log_state_changed(request, doc, login, prev, prev_tag) + e = idrfcutil_log_state_changed(request, doc, login, prev, prev_tag) doc.time = e.time doc.save() @@ -1153,7 +1154,7 @@ def make_last_call(request, name): doc.idinternal.event_date = date.today() doc.idinternal.save() - log_state_changed(request, doc, login) + idrfcutil_log_state_changed(request, doc, login) doc.lc_sent_date = form.cleaned_data['last_call_sent_date'] doc.lc_expiration_date = form.cleaned_data['last_call_expiration_date'] @@ -1216,7 +1217,7 @@ def make_last_callREDESIGN(request, name): if prev_tag: doc.tags.remove(prev_tag) - e = log_state_changed(request, doc, login, prev, prev_tag) + e = idrfcutil_log_state_changed(request, doc, login, prev, prev_tag) doc.time = e.time doc.save() diff --git a/ietf/idrfc/views_doc.py b/ietf/idrfc/views_doc.py index f7bc194a7..80a4f8104 100644 --- a/ietf/idrfc/views_doc.py +++ b/ietf/idrfc/views_doc.py @@ -143,7 +143,7 @@ def document_main(request, name, rev=None): ballot_summary = None if doc.get_state_slug() in ("intrev", "iesgrev"): - ballot_summary = needed_ballot_positions(doc, doc.active_ballot_positions().values()) + ballot_summary = needed_ballot_positions(doc, doc.active_ballot().active_ad_positions().values()) return render_to_response("idrfc/document_charter.html", dict(doc=doc, @@ -171,7 +171,7 @@ def document_main(request, name, rev=None): ballot_summary = None if doc.get_state_slug() in ("iesgeval"): - ballot_summary = needed_ballot_positions(doc, doc.active_ballot_positions().values()) + ballot_summary = needed_ballot_positions(doc, doc.active_ballot().active_ad_positions().values()) return render_to_response("idrfc/document_conflict_review.html", dict(doc=doc, @@ -292,36 +292,7 @@ def document_ballot_content(request, doc, ballot_id, editable=True): deferred = doc.active_defer_event() - # collect positions - active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active").distinct()) - - positions = [] - seen = {} - for e in BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).select_related('ad', 'pos').order_by("-time", '-id'): - if e.ad not in seen: - e.old_ad = e.ad not in active_ads - e.old_positions = [] - positions.append(e) - seen[e.ad] = e - else: - latest = seen[e.ad] - if latest.old_positions: - prev = latest.old_positions[-1] - else: - prev = latest.pos.name - - 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") - for ad in active_ads: - if ad not in seen: - e = BallotPositionDocEvent(type="changed_ballot_position", doc=doc, ad=ad) - e.pos = norecord - e.old_ad = False - e.old_positions = [] - positions.append(e) + positions = doc.active_ballot().all_positions() # put into position groups position_groups = [] diff --git a/ietf/idtracker/templatetags/ietf_filters.py b/ietf/idtracker/templatetags/ietf_filters.py index 73220b7d5..5ee10d619 100644 --- a/ietf/idtracker/templatetags/ietf_filters.py +++ b/ietf/idtracker/templatetags/ietf_filters.py @@ -146,6 +146,15 @@ def square_brackets(value): else: return "[ ]" +@register.filter(name='bracketpos') +def bracketpos(pos,posslug): + if pos.pos.slug==posslug: + return "[ X ]" + elif posslug in [x.slug for x in pos.old_positions]: + return "[ . ]" + else: + return "[ ]" + @register.filter(name='fill') def fill(text, width): """Wraps each paragraph in text (a string) so every line diff --git a/ietf/submit/utils.py b/ietf/submit/utils.py index 479eef528..f5e2f30b7 100644 --- a/ietf/submit/utils.py +++ b/ietf/submit/utils.py @@ -261,7 +261,7 @@ def announce_new_versionREDESIGN(request, submission, draft, state_change_msg): if draft.ad: to_email.append(draft.ad.role_email("ad").address) - for ad, pos in draft.active_ballot_positions().iteritems(): + for ad, pos in draft.active_ballot().active_ad_positions().iteritems(): if pos and pos.pos_id == "discuss": to_email.append(ad.role_email("ad").address) diff --git a/ietf/templates/iesg/moderator_doc.html b/ietf/templates/iesg/moderator_doc.html index 476a8626d..a20dfd9a5 100644 --- a/ietf/templates/iesg/moderator_doc.html +++ b/ietf/templates/iesg/moderator_doc.html @@ -40,19 +40,21 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {{ title2 }}
{{ title3 }} ({{ forloop.counter }} of {{ section_docs|length }}) -

{{doc.obj.document.filename}}{% if not doc.obj.rfc_flag %}-{{doc.obj.document.revision}}{% endif %}
-({{ doc.obj.document.title|escape }})
-Intended status: {{ doc.obj.document.intended_status }}
-Token: {{ doc.obj.token_name|escape }}
-Last call ends: {{ doc.obj.document.lc_expiration_date|default:"(none)" }}

+

{{doc.obj.name}}-{{doc.obj.rev}}
+({{ doc.obj.title|escape }})
+Intended status: {{ doc.obj.intended_std_level }}
+Token: {{ doc.obj.ad.plain_name|escape }}
+{% if doc.obj.type.slug == "draft" %} +Last call ends: {{ doc.obj.most_recent_ietflc.expires.date|default:"(none)" }} +{% if doc.obj.most_recent_ietflc.expires.date|timesince_days < 3 %}!!!{% endif %} +{% endif %}

-{% if doc.obj.ballot.active %} -{% filter linebreaks_crlf %}
-                      Yes  No-Objection  Discuss  Abstain
-{% for curpos in doc.obj.ballot.active_positions %}{{ curpos.ad|ljust:"20" }} {{ curpos.pos.yes|bracket }}     {{ curpos.pos.noobj|bracket }}     {{ curpos.pos.discuss|bracket }}     {{ curpos.pos.abstain_ind|bracket }}
+{% if doc.obj.active_ballot %}
+
+                        Yes  No-Objection  Discuss  Abstain  Recuse
+{% for pos in doc.obj.active_ballot.all_positions %}{% if pos.old_ad %}{{pos.ad.plain_name|bracket|ljust:"22"}}{%else%}{{pos.ad.plain_name|ljust:"22"}}{%endif%} {{pos|bracketpos:"yes"}}    {{pos|bracketpos:"noobj"}}       {{pos|bracketpos:"discuss"}}    {{pos|bracketpos:"abstain"}}    {{pos|bracketpos:"recuse"}}
 {% endfor %}
-{% for position in doc.obj.ballot.positions.all|dictsort:"ad.last_name" %}{% if not position.ad.is_current_ad %}{{ position.ad|ljust:"20" }} {{ position.yes|bracket }}     {{ position.noobj|bracket }}     {{ position.discuss|bracket }}     {{ position.abstain_ind|bracket }}
-{% endif %}{% endfor %}
{% endfilter %} +
{% if title1|startswith:"2." %}

____ open positions
@@ -67,16 +69,16 @@ Last call ends: {{ doc.obj.document.lc_expiration_date|default:"(none)" }}Does anyone have an[y further] objection to this document being published as an {{ doc.obj.document.intended_status }} RFC?

+

Does anyone have an[y further] objection to this document being published as an {{ doc.obj.intended_std_level }} RFC?

{% endif %} {% if title3|startswith:"3.3.1" or title3|startswith:"3.3.2" %} -

Does anyone have an objection to the RFC Editor publishing this document as an {{ doc.obj.document.intended_status }} RFC?

+

Does anyone have an objection to the RFC Editor publishing this document as an {{ doc.obj.intended_std_level }} RFC?

{% endif %} {% if title3|startswith:"3.3.3" %}

Who will do the review of this document?

{% endif %} -

Current State: {{ doc.obj.cur_state }}, sub state: {{ doc.obj.cur_sub_state|default:"(none)" }}
+

Current State: {{ doc.obj.friendly_state }}
Next State:
Sub State:

diff --git a/ietf/templates/iesg/moderator_package.html b/ietf/templates/iesg/moderator_package.html index 65407923d..7926425e3 100644 --- a/ietf/templates/iesg/moderator_package.html +++ b/ietf/templates/iesg/moderator_package.html @@ -85,7 +85,7 @@ teleconference. The Secretariat will post them in the public archive.

{{ minutes }} {% endfilter %} -

1. Administrivia
1.4 List of Remaining Action Items from Last Telehat

+

1. Administrivia
1.4 List of Remaining Action Items from Last Telechat

{% filter linebreaks_crlf %}
 {{ action_items }}
diff --git a/ietf/templates/iesg/scribe_doc.html b/ietf/templates/iesg/scribe_doc.html
index 75617579b..7d6e4da95 100644
--- a/ietf/templates/iesg/scribe_doc.html
+++ b/ietf/templates/iesg/scribe_doc.html
@@ -65,7 +65,7 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved.
         {% if doc.obj.active_ballot %}
             
Discusses/comments [ballot]: