More clean up and fix a bunch of bugs.
- Legacy-Id: 4261
This commit is contained in:
parent
75ac144432
commit
41fb923d01
|
@ -217,17 +217,17 @@ def document_writeup(request, name):
|
|||
e = doc.latest_event(WriteupDocEvent, type="changed_review_announcement")
|
||||
writeups.append(("WG Review Announcement",
|
||||
e.text if e else "",
|
||||
urlreverse("ietf.wgcharter.views_ballot.announcement_text", kwargs=dict(name=doc.group.acronym, ann="review"))))
|
||||
urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.group.acronym, ann="review"))))
|
||||
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
|
||||
writeups.append(("WG Action Announcement",
|
||||
e.text if e else "",
|
||||
urlreverse("ietf.wgcharter.views_ballot.announcement_text", kwargs=dict(name=doc.group.acronym, ann="action"))))
|
||||
urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.group.acronym, ann="action"))))
|
||||
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
|
||||
writeups.append(("Ballot Announcement",
|
||||
e.text if e else "",
|
||||
urlreverse("ietf.wgcharter.views_ballot.ballot_writeupnotes", kwargs=dict(name=doc.group.acronym))))
|
||||
urlreverse("ietf.wgcharter.views.ballot_writeupnotes", kwargs=dict(name=doc.group.acronym))))
|
||||
|
||||
if not writeups:
|
||||
raise Http404()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% load ietf_filters %}{% autoescape off %}From: The IESG <iesg-secretary@ietf.org>
|
||||
To: IETF-Announce <ietf-announce@ietf.org>
|
||||
Subject: WG Action: {{ action_type }} {{ wg.name }} ({{wg.acronym}})
|
||||
Subject: WG Action: {{ action_type }} {{ wg.name }} ({{ wg.acronym }})
|
||||
|
||||
{% filter wordwrap:73 %}{% ifequal action_type "Formed" %}A new IETF working group has been formed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal action_type "Rechartered" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF has been rechartered.{% endifequal %} For additional information please contact the Area Directors or the WG Chair.
|
||||
{% filter wordwrap:73 %}{% ifequal action_type "Formed" %}A new IETF working group has been formed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal action_type "Rechartered" %}The {{ wg.name }} ({{ wg.acronym }}) working group in the {{ wg.parent.name }} of the IETF has been rechartered.{% endifequal %} For additional information please contact the Area Directors or the WG Chair.
|
||||
|
||||
{% include "wgcharter/wg_info.txt" %}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ form #id_announcement_text {
|
|||
</div>
|
||||
|
||||
{% load ietf_filters %}
|
||||
{% if user|in_group:"Secretariat" %}
|
||||
{% if user|has_role:"Secretariat" %}
|
||||
<div class="actions">
|
||||
<input type="submit" name="send_text" value="Send WG {{ announcement }} announcement" />
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Ballot for {{ charter.chartered_group }} issued{% endblock %}
|
||||
{% block title %}Ballot for {{ doc.name }} issued{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Ballot for {{ charter.chartered_group }} issued</h1>
|
||||
<h1>Ballot for {{ doc.name }} issued</h1>
|
||||
|
||||
<p>Ballot has been sent out.</p>
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ back_url }}">Back to WG</a>
|
||||
<a href="{% url doc_writeup name=doc.name %}">Back to writeups</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -20,7 +20,7 @@ form #id_ballot_writeup {
|
|||
{{ ballot_writeup_form.ballot_writeup }}
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ charter.get_absolute_url }}">Back</a>
|
||||
<a href="{% url doc_writeup name=charter.name %}">Back</a>
|
||||
<input type="submit" name="save_ballot_writeup" value="Save Ballot Writeup" />
|
||||
<input style="margin-left: 8px" type="submit" name="issue_ballot" value="Save and {% if reissue %}Re-{% endif %}Issue Ballot" />
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{% autoescape off %}To: Internet Engineering Steering Group <iesg@ietf.org>
|
||||
From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Subject: Evaluation: {{ charter.chartered_group }} ({{ charter.chartered_group.acronym }})
|
||||
Subject: Evaluation: {{ doc.name }}
|
||||
|
||||
{% filter wordwrap:73 %}Evaluation for {{ charter.chartered_group }} ({{ charter.chartered_group.acronym }}) can be found at {{ charter_url }}
|
||||
{% filter wordwrap:73 %}Evaluation for {{ doc.title }} can be found at {{ doc_url }}
|
||||
{% endfilter %}
|
||||
Please return the full line with your position.
|
||||
|
||||
|
@ -20,8 +20,8 @@ BLOCKING AND NON-BLOCKING COMMENTS
|
|||
==================================
|
||||
{% filter wordwrap:79 %}{% for p in ad_feedback %}{{ p.ad }}:
|
||||
|
||||
{% if p.block_comment %}Blocking comment [{{ p.time }}]:
|
||||
{{ p.block_comment }}
|
||||
{% if p.discuss %}Blocking comment [{{ p.time }}]:
|
||||
{{ p.discuss }}
|
||||
|
||||
{% endif %}{% if p.comment %}Comment [{{ p.time }}]:
|
||||
{{ p.comment }}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{% load ietf_filters %}{% autoescape off %}From: The IESG <iesg-secretary@ietf.org>
|
||||
To: IETF-Announce <ietf-announce@ietf.org>
|
||||
Subject: WG Review: {{ wg.name }} ({{wg.acronym}})
|
||||
Subject: WG Review: {{ wg.name }} ({{ wg.acronym }})
|
||||
|
||||
{% filter wordwrap:73 %}{% ifequal review_type "new" %}A new IETF working group has been proposed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal review_type "recharter" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF is undergoing rechartering.{% endifequal %} The IESG has not made any determination yet. The following draft charter was submitted, and is provided for informational purposes only. Please send your comments to the IESG mailing list (iesg at ietf.org) by {{ info.bydate }}.
|
||||
{% filter wordwrap:73 %}{% ifequal review_type "new" %}A new IETF working group has been proposed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal review_type "recharter" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF is undergoing rechartering.{% endifequal %} The IESG has not made any determination yet. The following draft charter was submitted, and is provided for informational purposes only. Please send your comments to the IESG mailing list (iesg at ietf.org) by {{ review_date }}.
|
||||
|
||||
{% include "wgcharter/wg_info.txt" %}
|
||||
|
||||
|
|
|
@ -3,22 +3,22 @@
|
|||
Current Status: {{ wg.state.name }} Working Group
|
||||
|
||||
Chairs:
|
||||
{% for p in info.chairs %} {{ p.plain_name }} <{{p.email}}>
|
||||
{% for r in chairs %} {{ r.person.plain_name }} <{{r.email.address}}>
|
||||
{% endfor %}
|
||||
Secretaries:
|
||||
{% for p in info.secr %} {{ p.plain_name }} <{{p.email}}>
|
||||
{% for r in secr %} {{ r.person.plain_name }} <{{r.email.address}}>
|
||||
{% endfor %}
|
||||
Technical advisors:
|
||||
{% for p in info.techadv %} {{ p.plain_name }} <{{p.email}}>
|
||||
{% for r in techadv %} {{ r.person.plain_name }} <{{r.email.address}}>
|
||||
{% endfor %}
|
||||
Assigned Area Director:
|
||||
{{ info.ad.0.plain_name }} <{{ info.ad.0.email }}>
|
||||
{% if wg.ad %} {{ wg.ad.plain_name }} <{{ ad_email }}>{% endif %}
|
||||
|
||||
Mailing list:
|
||||
Address: {{ info.list.0 }}
|
||||
To Subscribe: {{ info.list_subscribe.0 }}
|
||||
Archive: {{ info.list_archive.0 }}
|
||||
Address: {{ wg.list_email }}
|
||||
To Subscribe: {{ wg.list_subscribe }}
|
||||
Archive: {{ wg.list_archive }}
|
||||
|
||||
Charter:
|
||||
|
||||
{{ info.charter_txt }}
|
||||
{{ charter_text }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# generation of mails
|
||||
|
||||
import textwrap
|
||||
import textwrap, datetime
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
|
@ -8,10 +8,10 @@ from django.conf import settings
|
|||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.utils.mail import send_mail, send_mail_text
|
||||
from ietf.idtracker.models import *
|
||||
from ietf.ipr.search import iprs_from_docs
|
||||
from ietf.doc.models import WriteupDocEvent, DocAlias
|
||||
from ietf.doc.models import WriteupDocEvent, DocAlias, BallotPositionDocEvent
|
||||
from ietf.person.models import Person
|
||||
from ietf.wgcharter.utils import *
|
||||
|
||||
def email_secretariat(request, wg, type, text):
|
||||
to = ["iesg-secretary@ietf.org"]
|
||||
|
@ -48,14 +48,51 @@ def generate_ballot_writeup(request, doc):
|
|||
|
||||
return e
|
||||
|
||||
def generate_issue_ballot_mail(request, charter):
|
||||
raise NotImplemented
|
||||
def default_action_text(wg, charter, user, action):
|
||||
e = WriteupDocEvent(doc=charter, by=user)
|
||||
e.by = user
|
||||
e.type = "changed_action_announcement"
|
||||
e.desc = "WG action text was changed"
|
||||
e.text = render_to_string("wgcharter/action_text.txt",
|
||||
dict(wg=wg,
|
||||
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
|
||||
charter_text=read_charter_text(charter),
|
||||
chairs=wg.role_set.filter(name="chair"),
|
||||
secr=wg.role_set.filter(name="secr"),
|
||||
techadv=wg.role_set.filter(name="techadv"),
|
||||
ad_email=wg.ad.role_email("ad") if wg.ad else None,
|
||||
action_type=action,
|
||||
))
|
||||
|
||||
e.save()
|
||||
return e
|
||||
|
||||
def default_review_text(wg, charter, user):
|
||||
e = WriteupDocEvent(doc=charter, by=user)
|
||||
e.by = user
|
||||
e.type = "changed_review_announcement"
|
||||
e.desc = "WG review text was changed"
|
||||
e.text = render_to_string("wgcharter/review_text.txt",
|
||||
dict(wg=wg,
|
||||
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
|
||||
charter_text=read_charter_text(charter),
|
||||
chairs=wg.role_set.filter(name="chair"),
|
||||
secr=wg.role_set.filter(name="secr"),
|
||||
techadv=wg.role_set.filter(name="techadv"),
|
||||
ad_email=wg.ad.role_email("ad") if wg.ad else None,
|
||||
review_date=(datetime.date.today() + datetime.timedelta(weeks=1)).isoformat(),
|
||||
review_type="new" if wg.state_id == "proposed" else "recharter",
|
||||
)
|
||||
)
|
||||
e.save()
|
||||
return e
|
||||
|
||||
def generate_issue_ballot_mail(request, doc, ballot):
|
||||
active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct()
|
||||
|
||||
e = charter.latest_event(type="started_iesg_process")
|
||||
seen = []
|
||||
positions = []
|
||||
for p in GroupBallotPositionDocEvent.objects.filter(doc=charter, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad'):
|
||||
for p in BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad'):
|
||||
if p.ad not in seen:
|
||||
positions.append(p)
|
||||
seen.append(p.ad)
|
||||
|
@ -87,9 +124,9 @@ def generate_issue_ballot_mail(request, charter):
|
|||
|
||||
if p.ad in active_ads:
|
||||
active_ad_positions.append(fmt)
|
||||
if not p.pos_id == "block":
|
||||
p.block_comment = ""
|
||||
if p.comment or p.block_comment:
|
||||
if not p.pos or not p.pos.blocking:
|
||||
p.discuss = ""
|
||||
if p.comment or p.discuss:
|
||||
ad_feedback.append(p)
|
||||
else:
|
||||
inactive_ad_positions.append(fmt)
|
||||
|
@ -98,15 +135,15 @@ def generate_issue_ballot_mail(request, charter):
|
|||
inactive_ad_positions.sort()
|
||||
ad_feedback.sort(key=lambda p: p.ad.plain_name())
|
||||
|
||||
e = charter.latest_event(WriteupDocEvent, type="changed_action_announcement")
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
|
||||
approval_text = e.text if e else ""
|
||||
|
||||
e = charter.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
|
||||
ballot_writeup = e.text if e else ""
|
||||
|
||||
return render_to_string("wgcharter/issue_ballot_mail.txt",
|
||||
dict(charter=charter,
|
||||
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
|
||||
dict(doc=doc,
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
|
||||
active_ad_positions=active_ad_positions,
|
||||
inactive_ad_positions=inactive_ad_positions,
|
||||
ad_feedback=ad_feedback,
|
||||
|
|
|
@ -170,13 +170,14 @@ class CharterApproveBallotTestCase(django.test.TestCase):
|
|||
|
||||
p = Person.objects.get(name="Aread Irector")
|
||||
|
||||
e = DocEvent()
|
||||
e.type = "started_iesg_process"
|
||||
e.by = p
|
||||
e.doc = charter
|
||||
e.desc = "IESG process started"
|
||||
e.save()
|
||||
|
||||
BallotDocEvent.objects.create(
|
||||
type="created_ballot",
|
||||
ballot_type=BallotType.objects.get(doc_type="charter", slug="approve"),
|
||||
by=p,
|
||||
doc=charter,
|
||||
desc="Created ballot",
|
||||
)
|
||||
|
||||
charter.set_state(State.objects.get(type="charter", slug="iesgrev"))
|
||||
|
||||
# normal get
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import re, datetime
|
||||
import re, datetime, os
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
@ -102,6 +102,14 @@ def next_approved_revision(rev):
|
|||
m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
|
||||
return "%#02d" % (int(m.group('major')) + 1)
|
||||
|
||||
def read_charter_text(doc):
|
||||
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (doc.canonical_name(), doc.rev))
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
return f.read()
|
||||
except IOError:
|
||||
return "Error: couldn't read charter text"
|
||||
|
||||
def update_telechat(request, doc, by, new_telechat_date):
|
||||
# FIXME: fix auto-setting returning item problem and reuse
|
||||
# function in idrfc/utils.py instead of this one
|
||||
|
|
|
@ -294,71 +294,6 @@ def submit(request, name):
|
|||
'wg': wg},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def default_action_text(wg, charter, user, action):
|
||||
e = WriteupDocEvent(doc=charter, by=user)
|
||||
e.by = user
|
||||
e.type = "changed_action_announcement"
|
||||
e.desc = "WG action text was changed"
|
||||
|
||||
info = {}
|
||||
info['chairs'] = [{ 'name': x.person.plain_name(), 'email': x.email.address} for x in wg.role_set.filter(name="Chair")]
|
||||
info['secr'] = [{ 'name': x.person.plain_name(), 'email': x.email.address} for x in wg.role_set.filter(name="Secr")]
|
||||
info['techadv'] = [{ 'name': x.person.plain_name(), 'email': x.email.address} for x in wg.role_set.filter(name="Techadv")]
|
||||
info['ad'] = {'name': wg.ad.plain_name(), 'email': wg.ad.role_email("ad").address } if wg.ad else None,
|
||||
info['list'] = wg.list_email if wg.list_email else None,
|
||||
info['list_subscribe'] = str(wg.list_subscribe) if wg.list_subscribe else None,
|
||||
info['list_archive'] = str(wg.list_archive) if wg.list_archive else None,
|
||||
|
||||
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (wg.charter.canonical_name(), wg.charter.rev))
|
||||
try:
|
||||
charter_text = open(filename, 'r')
|
||||
info['charter_txt'] = charter_text.read()
|
||||
except IOError:
|
||||
info['charter_txt'] = "Error: couldn't read charter text"
|
||||
|
||||
e.text = render_to_string("wgcharter/action_text.txt",
|
||||
dict(wg=wg,
|
||||
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
|
||||
action_type=action,
|
||||
info=info,
|
||||
))
|
||||
|
||||
e.save()
|
||||
return e
|
||||
|
||||
def default_review_text(wg, charter, user):
|
||||
e = WriteupDocEvent(doc=charter, by=user)
|
||||
e.by = user
|
||||
e.type = "changed_review_announcement"
|
||||
e.desc = "WG review text was changed"
|
||||
info = {}
|
||||
info['chairs'] = [{ 'name': x.person.plain_name(), 'email': x.email.address} for x in wg.role_set.filter(name="Chair")]
|
||||
info['secr'] = [{ 'name': x.person.plain_name(), 'email': x.email.address} for x in wg.role_set.filter(name="Secr")]
|
||||
info['techadv'] = [{ 'name': x.person.plain_name(), 'email': x.email.address} for x in wg.role_set.filter(name="Techadv")]
|
||||
info['ad'] = {'name': wg.ad.plain_name(), 'email': wg.ad.role_email("ad").address } if wg.ad else None,
|
||||
info['list'] = wg.list_email if wg.list_email else None,
|
||||
info['list_subscribe'] = wg.list_subscribe if wg.list_subscribe else None,
|
||||
info['list_archive'] = wg.list_archive if wg.list_archive else None,
|
||||
|
||||
info['bydate'] = (date.today() + timedelta(weeks=1)).isoformat()
|
||||
|
||||
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (wg.charter.canonical_name(), wg.charter.rev))
|
||||
try:
|
||||
charter_text = open(filename, 'r')
|
||||
info['charter_txt'] = charter_text.read()
|
||||
except IOError:
|
||||
info['charter_txt'] = "Error: couldn't read charter text"
|
||||
|
||||
e.text = render_to_string("wgcharter/review_text.txt",
|
||||
dict(wg=wg,
|
||||
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
|
||||
info=info,
|
||||
review_type="new" if wg.state_id == "proposed" else "recharter",
|
||||
)
|
||||
)
|
||||
e.save()
|
||||
return e
|
||||
|
||||
class AnnouncementTextForm(forms.Form):
|
||||
announcement_text = forms.CharField(widget=forms.Textarea, required=True)
|
||||
|
||||
|
@ -436,7 +371,7 @@ def announcement_text(request, name, ann):
|
|||
return render_to_response('wgcharter/announcement_text.html',
|
||||
dict(charter=charter,
|
||||
announcement=ann,
|
||||
back_url=charter.get_absolute_url(),
|
||||
back_url=urlreverse("doc_writeup", kwargs=dict(name=charter.name)),
|
||||
announcement_text_form=form,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
@ -459,10 +394,10 @@ def ballot_writeupnotes(request, name):
|
|||
else:
|
||||
raise Http404
|
||||
|
||||
charter = set_or_create_charter(wg)
|
||||
charter = wg.charter
|
||||
|
||||
started_process = charter.latest_event(type="started_iesg_process")
|
||||
if not started_process:
|
||||
ballot = charter.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if not ballot:
|
||||
raise Http404()
|
||||
|
||||
login = request.user.get_profile()
|
||||
|
@ -477,7 +412,7 @@ def ballot_writeupnotes(request, name):
|
|||
|
||||
form = BallotWriteupForm(initial=dict(ballot_writeup=existing.text))
|
||||
|
||||
if request.method == 'POST' and "save_ballot_writeup" in request.POST or "issue_ballot" in request.POST:
|
||||
if request.method == 'POST' and ("save_ballot_writeup" in request.POST or "issue_ballot" in request.POST):
|
||||
form = BallotWriteupForm(request.POST)
|
||||
if form.is_valid():
|
||||
t = form.cleaned_data["ballot_writeup"]
|
||||
|
@ -490,16 +425,16 @@ def ballot_writeupnotes(request, name):
|
|||
e.save()
|
||||
|
||||
if "issue_ballot" in request.POST and approval:
|
||||
if has_role(request.user, "Area Director") and not charter.latest_event(BallotPositionDocEvent, ad=login, time__gte=started_process.time):
|
||||
if has_role(request.user, "Area Director") and not charter.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=login, ballot=ballot):
|
||||
# sending the ballot counts as a yes
|
||||
pos = GroupBallotPositionDocEvent(doc=charter, by=login)
|
||||
pos = BallotPositionDocEvent(doc=charter, by=login)
|
||||
pos.type = "changed_ballot_position"
|
||||
pos.ad = login
|
||||
pos.pos_id = "yes"
|
||||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
|
||||
pos.save()
|
||||
|
||||
msg = generate_issue_ballot_mail(request, charter)
|
||||
msg = generate_issue_ballot_mail(request, charter, ballot)
|
||||
send_mail_preformatted(request, msg)
|
||||
|
||||
e = DocEvent(doc=charter, by=login)
|
||||
|
@ -509,14 +444,13 @@ def ballot_writeupnotes(request, name):
|
|||
e.save()
|
||||
|
||||
return render_to_response('wgcharter/ballot_issued.html',
|
||||
dict(charter=charter,
|
||||
back_url=charter.get_absolute_url()),
|
||||
dict(doc=charter,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
return render_to_response('wgcharter/ballot_writeupnotes.html',
|
||||
dict(charter=charter,
|
||||
back_url=charter.get_absolute_url(),
|
||||
ballot_issued=bool(charter.latest_event(type="sent_ballot_announcement")),
|
||||
ballot_writeup_form=form,
|
||||
reissue=reissue,
|
||||
|
|
Loading…
Reference in a new issue