Handle the new-work message

- Legacy-Id: 10066
This commit is contained in:
Robert Sparks 2015-08-27 21:27:10 +00:00
parent 12a03d299b
commit 150106619b
10 changed files with 305 additions and 91 deletions

View file

@ -9,7 +9,7 @@ from django.core.urlresolvers import reverse as urlreverse
from ietf.doc.models import ( Document, State, BallotDocEvent, BallotType, NewRevisionDocEvent,
TelechatDocEvent, WriteupDocEvent )
from ietf.doc.utils_charter import next_revision, default_review_text, default_action_text
from ietf.doc.utils_charter import next_revision, default_review_text, default_action_text
from ietf.group.models import Group, GroupMilestone
from ietf.iesg.models import TelechatDate
from ietf.person.models import Person
@ -273,46 +273,101 @@ class EditCharterTests(TestCase):
self.assertEqual(f.read(),
"Windows line\nMac line\nUnix line\n" + utf_8_snippet)
def test_edit_announcement_text(self):
def test_edit_review_announcement_text(self):
draft = make_test_data()
charter = draft.group.charter
for ann in ("action", "review"):
url = urlreverse('ietf.doc.views_charter.announcement_text', kwargs=dict(name=charter.name, ann=ann))
self.client.logout()
login_testing_unauthorized(self, "secretary", url)
url = urlreverse('ietf.doc.views_charter.review_announcement_text', kwargs=dict(name=charter.name))
self.client.logout()
login_testing_unauthorized(self, "secretary", url)
# normal get
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('textarea[name=announcement_text]')), 1)
# as Secretariat, we can send
if ann == "review":
mailbox_before = len(outbox)
by = Person.objects.get(user__username="secretary")
r = self.client.post(url, dict(
announcement_text=default_review_text(draft.group, charter, by).text,
send_text="1"))
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue('WG Review' in outbox[-1]['Subject'])
self.assertTrue('ietf-announce@' in outbox[-1]['To'])
self.assertTrue('mars-wg@' in outbox[-1]['Cc'])
# normal get
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('textarea[name=announcement_text]')), 1)
self.assertEqual(len(q('textarea[name=new_work_text]')), 1)
# save
r = self.client.post(url, dict(
announcement_text="This is a simple test.",
save_text="1"))
self.assertEqual(r.status_code, 302)
self.assertTrue("This is a simple test" in charter.latest_event(WriteupDocEvent, type="changed_%s_announcement" % ann).text)
by = Person.objects.get(user__username="secretary")
# test regenerate
r = self.client.post(url, dict(
announcement_text="This is a simple test.",
regenerate_text="1"))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(draft.group.name in charter.latest_event(WriteupDocEvent, type="changed_%s_announcement" % ann).text)
(e1, e2) = default_review_text(draft.group, charter, by)
announcement_text = e1.text
new_work_text = e2.text
empty_outbox()
r = self.client.post(url, dict(
announcement_text=announcement_text,
new_work_text=new_work_text,
send_both="1"))
self.assertEqual(len(outbox), 2)
self.assertTrue(all(['WG Review' in m['Subject'] for m in outbox]))
self.assertTrue('ietf-announce@' in outbox[0]['To'])
self.assertTrue('mars-wg@' in outbox[0]['Cc'])
self.assertTrue('new-work@' in outbox[1]['To'])
empty_outbox()
r = self.client.post(url, dict(
announcement_text=announcement_text,
new_work_text=new_work_text,
send_annc_only="1"))
self.assertEqual(len(outbox), 1)
self.assertTrue('ietf-announce@' in outbox[0]['To'])
empty_outbox()
r = self.client.post(url, dict(
announcement_text=announcement_text,
new_work_text=new_work_text,
send_nw_only="1"))
self.assertEqual(len(outbox), 1)
self.assertTrue('new-work@' in outbox[0]['To'])
# save
r = self.client.post(url, dict(
announcement_text="This is a simple test.",
new_work_text="New work gets something different.",
save_text="1"))
self.assertEqual(r.status_code, 302)
self.assertTrue("This is a simple test" in charter.latest_event(WriteupDocEvent, type="changed_review_announcement").text)
self.assertTrue("New work gets something different." in charter.latest_event(WriteupDocEvent, type="changed_new_work_text").text)
# test regenerate
r = self.client.post(url, dict(
announcement_text="This is a simple test.",
new_work_text="Too simple perhaps?",
regenerate_text="1"))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(draft.group.name in charter.latest_event(WriteupDocEvent, type="changed_review_announcement").text)
self.assertTrue(draft.group.name in charter.latest_event(WriteupDocEvent, type="changed_new_work_text").text)
def test_edit_action_announcement_text(self):
draft = make_test_data()
charter = draft.group.charter
url = urlreverse('ietf.doc.views_charter.action_announcement_text', kwargs=dict(name=charter.name))
self.client.logout()
login_testing_unauthorized(self, "secretary", url)
# normal get
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('textarea[name=announcement_text]')), 1)
# save
r = self.client.post(url, dict(
announcement_text="This is a simple test.",
save_text="1"))
self.assertEqual(r.status_code, 302)
self.assertTrue("This is a simple test" in charter.latest_event(WriteupDocEvent, type="changed_action_announcement").text)
# test regenerate
r = self.client.post(url, dict(
announcement_text="This is a simple test.",
regenerate_text="1"))
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertTrue(draft.group.name in charter.latest_event(WriteupDocEvent, type="changed_action_announcement").text)
def test_edit_ballot_writeupnotes(self):
draft = make_test_data()

View file

@ -9,7 +9,8 @@ urlpatterns = patterns('',
url(r'^telechat/$', "ietf.doc.views_doc.telechat_date", name='charter_telechat_date'),
url(r'^notify/$', "ietf.doc.views_doc.edit_notify", name='charter_edit_notify'),
url(r'^ad/$', "ietf.doc.views_charter.edit_ad", name='charter_edit_ad'),
url(r'^(?P<ann>action|review)/$', "ietf.doc.views_charter.announcement_text", name="charter_edit_announcement"),
url(r'^action/$', "ietf.doc.views_charter.action_announcement_text"),
url(r'^review/$', "ietf.doc.views_charter.review_announcement_text"),
url(r'^ballotwriteupnotes/$', "ietf.doc.views_charter.ballot_writeupnotes"),
url(r'^approve/$', "ietf.doc.views_charter.approve", name='charter_approve'),
url(r'^submit/(?:(?P<option>initcharter|recharter)/)?$', "ietf.doc.views_charter.submit", name='charter_submit'),

View file

@ -6,6 +6,7 @@ from django.conf import settings
from ietf.doc.models import NewRevisionDocEvent, WriteupDocEvent, BallotPositionDocEvent
from ietf.person.models import Person
from ietf.utils.history import find_history_active_at
from ietf.utils.mail import parse_preformatted
from ietf.mailtoken.utils import gather_address_lists
def charter_name_for_group(group):
@ -120,28 +121,51 @@ def default_action_text(group, charter, by):
e.save()
return e
def derive_new_work_text(review_text,group):
addrs= gather_address_lists('charter_external_review_new_work',group=group).as_strings()
(m,_,_) = parse_preformatted(review_text,
override={'To':addrs.to,
'Cc':addrs.cc,
'From':'The IESG <iesg@ietf.org>',
'Reply_to':'<iesg@ietf.org>'})
if not addrs.cc:
del m['Cc']
return m.as_string()
def default_review_text(group, charter, by):
now = datetime.datetime.now()
addrs=gather_address_lists('charter_external_review',group=group).as_strings(compact=False)
e = WriteupDocEvent(doc=charter, by=by)
e.by = by
e.type = "changed_review_announcement"
e.desc = "%s review text was changed" % group.type.name
e.text = render_to_string("doc/charter/review_text.txt",
e1 = WriteupDocEvent(doc=charter, by=by)
e1.by = by
e1.type = "changed_review_announcement"
e1.desc = "%s review text was changed" % group.type.name
e1.text = render_to_string("doc/charter/review_text.txt",
dict(group=group,
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
charter_text=read_charter_text(charter),
chairs=group.role_set.filter(name="chair"),
secr=group.role_set.filter(name="secr"),
techadv=group.role_set.filter(name="techadv"),
milestones=group.groupmilestone_set.filter(state="charter"),
review_date=(datetime.date.today() + datetime.timedelta(weeks=1)).isoformat(),
review_type="new" if group.state_id == "proposed" else "recharter",
to=addrs.to,
cc=addrs.cc,
charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
charter_text=read_charter_text(charter),
chairs=group.role_set.filter(name="chair"),
secr=group.role_set.filter(name="secr"),
techadv=group.role_set.filter(name="techadv"),
milestones=group.groupmilestone_set.filter(state="charter"),
review_date=(datetime.date.today() + datetime.timedelta(weeks=1)).isoformat(),
review_type="new" if group.state_id == "proposed" else "recharter",
to=addrs.to,
cc=addrs.cc,
)
)
e.save()
return e
e1.time = now
e1.save()
e2 = WriteupDocEvent(doc=charter, by=by)
e2.by = by
e2.type = "changed_new_work_text"
e2.desc = "%s review text was changed" % group.type.name
e2.text = derive_new_work_text(e1.text,group)
e2.time = now
e2.save()
return (e1,e2)
def generate_issue_ballot_mail(request, doc, ballot):
active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active", email__role__group__type="area").distinct()

View file

@ -27,7 +27,7 @@ from ietf.ietfauth.utils import has_role, role_required
from ietf.name.models import GroupStateName
from ietf.person.models import Person
from ietf.utils.history import find_history_active_at
from ietf.utils.mail import send_mail_preformatted
from ietf.utils.mail import send_mail_preformatted
from ietf.utils.textupload import get_cleaned_text_file_content
from ietf.group.mails import email_admin_re_charter
@ -427,42 +427,124 @@ def submit(request, name=None, option=None):
'name': name },
context_instance=RequestContext(request))
class AnnouncementTextForm(forms.Form):
class ActionAnnouncementTextForm(forms.Form):
announcement_text = forms.CharField(widget=forms.Textarea, required=True)
def clean_announcement_text(self):
return self.cleaned_data["announcement_text"].replace("\r", "")
class ReviewAnnouncementTextForm(forms.Form):
announcement_text = forms.CharField(widget=forms.Textarea, required=True)
new_work_text = forms.CharField(widget=forms.Textarea, required=True)
def clean_announcement_text(self):
return self.cleaned_data["announcement_text"].replace("\r", "")
@role_required('Area Director','Secretariat')
def announcement_text(request, name, ann):
"""Editing of announcement text"""
def review_announcement_text(request, name):
"""Editing of review announcement text"""
charter = get_object_or_404(Document, type="charter", name=name)
group = charter.group
login = request.user.person
if ann in ("action", "review"):
existing = charter.latest_event(WriteupDocEvent, type="changed_%s_announcement" % ann)
existing = charter.latest_event(WriteupDocEvent, type="changed_review_announcement")
existing_new_work = charter.latest_event(WriteupDocEvent, type="changed_new_work_text")
if not existing:
if ann == "action":
existing = default_action_text(group, charter, login)
elif ann == "review":
existing = default_review_text(group, charter, login)
(existing, existing_new_work) = default_review_text(group, charter, login)
if not existing:
raise Http404
form = AnnouncementTextForm(initial=dict(announcement_text=existing.text))
new_work_text = existing_new_work.text
form = ReviewAnnouncementTextForm(initial=dict(announcement_text=existing.text,new_work_text=new_work_text))
if request.method == 'POST':
form = AnnouncementTextForm(request.POST)
form = ReviewAnnouncementTextForm(request.POST)
if "save_text" in request.POST and form.is_valid():
now = datetime.datetime.now()
(e1, e2) = (None, None)
t = form.cleaned_data['announcement_text']
if t != existing.text:
e1 = WriteupDocEvent(doc=charter, by=login)
e1.by = login
e1.type = "changed_review_announcement"
e1.desc = "%s review text was changed" % (group.type.name)
e1.text = t
e1.time = now
e1.save()
t = form.cleaned_data['new_work_text']
if t != new_work_text:
e2 = WriteupDocEvent(doc=charter, by=login)
e2.by = login
e2.type = "changed_new_work_text"
e2.desc = "%s new work message text was changed" % (group.type.name)
e2.text = t
e2.time = now
e2.save()
if e1 or e2:
charter.time = now
charter.save()
if request.GET.get("next", "") == "approve":
return redirect('charter_approve', name=charter.canonical_name())
return redirect('doc_writeup', name=charter.canonical_name())
if "regenerate_text" in request.POST:
(e1, e2) = default_review_text(group, charter, login)
form = ReviewAnnouncementTextForm(initial=dict(announcement_text=e1.text,new_work_text=e2.text))
if any([x in request.POST for x in ['send_annc_only','send_nw_only','send_both']]) and form.is_valid():
if any([x in request.POST for x in ['send_annc_only','send_both']]):
parsed_msg = send_mail_preformatted(request, form.cleaned_data['announcement_text'])
messages.success(request, "The email To: '%s' with Subject: '%s' has been sent." % (parsed_msg["To"],parsed_msg["Subject"],))
if any([x in request.POST for x in ['send_nw_only','send_both']]):
parsed_msg = send_mail_preformatted(request, form.cleaned_data['new_work_text'])
messages.success(request, "The email To: '%s' with Subject: '%s' has been sent." % (parsed_msg["To"],parsed_msg["Subject"],))
return redirect('doc_writeup', name=charter.name)
return render_to_response('doc/charter/review_announcement_text.html',
dict(charter=charter,
back_url=urlreverse("doc_writeup", kwargs=dict(name=charter.name)),
announcement_text_form=form,
),
context_instance=RequestContext(request))
@role_required('Area Director','Secretariat')
def action_announcement_text(request, name):
"""Editing of action announcement text"""
charter = get_object_or_404(Document, type="charter", name=name)
group = charter.group
login = request.user.person
existing = charter.latest_event(WriteupDocEvent, type="changed_action_announcement")
if not existing:
existing = default_action_text(group, charter, login)
if not existing:
raise Http404
form = ActionAnnouncementTextForm(initial=dict(announcement_text=existing.text))
if request.method == 'POST':
form = ActionAnnouncementTextForm(request.POST)
if "save_text" in request.POST and form.is_valid():
t = form.cleaned_data['announcement_text']
if t != existing.text:
e = WriteupDocEvent(doc=charter, by=login)
e.by = login
e.type = "changed_%s_announcement" % ann
e.desc = "%s %s text was changed" % (group.type.name, ann)
e.type = "changed_action_announcement"
e.desc = "%s action text was changed" % group.type.name
e.text = t
e.save()
@ -475,21 +557,16 @@ def announcement_text(request, name, ann):
return redirect('doc_writeup', name=charter.canonical_name())
if "regenerate_text" in request.POST:
if ann == "action":
e = default_action_text(group, charter, login)
elif ann == "review":
e = default_review_text(group, charter, login)
# make sure form has the updated text
form = AnnouncementTextForm(initial=dict(announcement_text=e.text))
e = default_action_text(group, charter, login)
form = ActionAnnouncementTextForm(initial=dict(announcement_text=e.text))
if "send_text" in request.POST and form.is_valid():
parsed_msg = send_mail_preformatted(request, form.cleaned_data['announcement_text'])
messages.success(request, "The email To: '%s' with Subject: '%s' has been sent." % (parsed_msg["To"],parsed_msg["Subject"],))
return redirect('doc_writeup', name=charter.name)
return render_to_response('doc/charter/announcement_text.html',
return render_to_response('doc/charter/action_announcement_text.html',
dict(charter=charter,
announcement=ann,
back_url=urlreverse("doc_writeup", kwargs=dict(name=charter.name)),
announcement_text_form=form,
),

View file

@ -687,14 +687,14 @@ def document_writeup(request, name):
"",
[("WG Review Announcement",
text_from_writeup("changed_review_announcement"),
urlreverse("ietf.doc.views_charter.announcement_text", kwargs=dict(name=doc.name, ann="review")))]
urlreverse("ietf.doc.views_charter.review_announcement_text", kwargs=dict(name=doc.name)))]
))
sections.append(("WG Action Announcement",
"",
[("WG Action Announcement",
text_from_writeup("changed_action_announcement"),
urlreverse("ietf.doc.views_charter.announcement_text", kwargs=dict(name=doc.name, ann="action")))]
urlreverse("ietf.doc.views_charter.action_announcement_text", kwargs=dict(name=doc.name)))]
))
if doc.latest_event(BallotDocEvent, type="created_ballot"):

View file

@ -243,6 +243,10 @@ def make_recipients(apps):
desc="The person providing a comment to nomcom",
template='{{commenter}}')
rc(slug='new_work',
desc="The IETF New Work list",
template='<new-work@ietf.org>')
def make_mailtokens(apps):
Recipient=apps.get_model('mailtoken','Recipient')
@ -409,12 +413,23 @@ def make_mailtokens(apps):
"requests publication",
to_slugs=['iana_approve',])
mt_factory(slug='charter_internal_review',
desc="Recipients for message noting that internal review has "
"started on a charter",
to_slugs=['iesg',
'iab',
])
mt_factory(slug='charter_external_review',
desc="Recipients for a charter external review",
to_slugs=['ietf_announce',],
cc_slugs=['group_mail_list',],
)
mt_factory(slug='charter_external_review_new_work',
desc="Recipients for a message to new-work about a charter review",
to_slugs=['new_work',])
mt_factory(slug='conflrev_requested',
desc="Recipients for a stream manager's request for an IETF conflict review",
to_slugs=['iesg_secretary'],
@ -807,13 +822,6 @@ def make_mailtokens(apps):
'doc_non_ietf_stream_manager',
])
mt_factory(slug='charter_internal_review',
desc="Recipients for message noting that internal review has "
"started on a charter",
to_slugs=['iesg',
'iab',
])
def forward(apps, schema_editor):
make_recipients(apps)

View file

@ -4687,6 +4687,14 @@
"model": "mailtoken.recipient",
"pk": "logged_in_person"
},
{
"fields": {
"template": "<new-work@ietf.org>",
"desc": "The IETF New Work list"
},
"model": "mailtoken.recipient",
"pk": "new_work"
},
{
"fields": {
"template": "{{nomcom.group.get_chair.email.address}}",
@ -4930,6 +4938,17 @@
"model": "mailtoken.mailtoken",
"pk": "charter_external_review"
},
{
"fields": {
"cc": [],
"to": [
"new_work"
],
"desc": "Recipients for a message to new-work about a charter review"
},
"model": "mailtoken.mailtoken",
"pk": "charter_external_review_new_work"
},
{
"fields": {
"cc": [],

View file

@ -5,11 +5,11 @@
{% load bootstrap3 %}
{% load ietf_filters %}
{% block title %}WG {{ announcement }} announcement writeup for {{ charter.chartered_group.acronym }}{% endblock %}
{% block title %}WG Action announcement }} announcement writeup for {{ charter.chartered_group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>WG {{ announcement }} announcement writeup<br><small>{{ charter.chartered_group.acronym }}</small></h1>
<h1>WG Action announcement writeup<br><small>{{ charter.chartered_group.acronym }}</small></h1>
{% bootstrap_messages %}
@ -22,11 +22,7 @@
<button type="submit" class="btn btn-warning" name="regenerate_text" value="Reenerate"">Regenerate</button>
{% if user|has_role:"Secretariat" %}
{% if announcement == "action" %}
<a type="submit" class="btn btn-default" href="{% url "charter_approve" name=charter.canonical_name %}">Charter approval page</a>
{% else %}
<input type="submit" type="submit" class="btn btn-default" name="send_text" value="Send WG {{ announcement }} announcement" />
{% endif %}
{% endif %}
<a class="btn btn-default pull-right" href="{{ back_url }}">Back</a>

View file

@ -16,7 +16,7 @@
{% buttons %}
<button type="submit" class="btn btn-primary">Send announcement, close ballot & update revision</button>
<a class="btn btn-default" href="{% url "charter_edit_announcement" name=charter.canonical_name ann="action" %}?next=approve">Edit/regenerate announcement</a>
<a class="btn btn-default" href="{% url "ietf.doc.views_charter.action_announcement_text" name=charter.canonical_name %}?next=approve">Edit/regenerate announcement</a>
<a class="btn btn-default pull-right" href="{% url "doc_view" name=charter.name %}">Back</a>
{% endbuttons %}
</form>

View file

@ -0,0 +1,34 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
{% load ietf_filters %}
{% block title %}WG Review announcement }} announcement writeup for {{ charter.chartered_group.acronym }}{% endblock %}
{% block content %}
{% origin %}
<h1>WG Review announcement writeup<br><small>{{ charter.chartered_group.acronym }}</small></h1>
{% bootstrap_messages %}
<form method="post">
{% csrf_token %}
{% bootstrap_form announcement_text_form %}
{% buttons %}
<button type="submit" class="btn btn-primary" name="save_text" value="Save">Submit</button>
<button type="submit" class="btn btn-warning" name="regenerate_text" value="Regenerate"">Regenerate</button>
{% if user|has_role:"Secretariat" %}
<input type="submit" type="submit" class="btn btn-default" name="send_annc_only" value="Send only to IETF-Announce" />
<input type="submit" type="submit" class="btn btn-default" name="send_nw_only" value="Send only to New-Work" />
<input type="submit" type="submit" class="btn btn-default" name="send_both" value="Send to both" />
{% endif %}
<a class="btn btn-default pull-right" href="{{ back_url }}">Back</a>
{% endbuttons %}
</form>
{% endblock%}