def approve(request, name): """Approve this conflict review, setting the appropriate state and send the announcement to the right parties.""" review = get_object_or_404(Document, type="conflrev", name=name) if review.get_state('conflrev').slug not in ('appr-reqnopub-pend','appr-noprob-pend'): raise Http404 login = request.user.get_profile() if request.method == 'POST': form = AnnouncementForm(request.POST) if form.is_valid(): new_state_slug = 'appr-reqnopub-sent' if review.get_state('conflrev').slug=='appr-reqnopub-pend' else 'appr-noprob-sent' new_review_state = State.objects.get(used=True, type="conflrev", slug=new_state_slug) save_document_in_history(review) old_description = review.friendly_state() review.set_state(new_review_state) new_description = review.friendly_state() log_state_changed(request, review, login, new_description, old_description) close_open_ballots(review, login) e = DocEvent(doc=review, by=login) e.type = "iesg_approved" e.desc = "IESG has approved the conflict review response" e.save() review.time = e.time review.save() # send announcement send_mail_preformatted(request, form.cleaned_data['announcement_text']) c = DocEvent(type="added_comment", doc=review, by=login) c.desc = "The following approval message was sent\n"+form.cleaned_data['announcement_text'] c.save() return HttpResponseRedirect(review.get_absolute_url()) else: init = { "announcement_text" : default_approval_text(review) } form = AnnouncementForm(initial=init) return render_to_response('doc/conflict_review/approve.html', dict( review = review, conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document, form = form, ), context_instance=RequestContext(request))
def approve(request, name): """Approve this conflict review, setting the appropriate state and send the announcement to the right parties.""" review = get_object_or_404(Document, type="conflrev", name=name) if review.get_state('conflrev').slug not in ('appr-reqnopub-pend','appr-noprob-pend'): raise Http404 login = request.user.person if request.method == 'POST': form = AnnouncementForm(request.POST) if form.is_valid(): prev_state = review.get_state() events = [] new_state_slug = 'appr-reqnopub-sent' if prev_state.slug == 'appr-reqnopub-pend' else 'appr-noprob-sent' new_state = State.objects.get(used=True, type="conflrev", slug=new_state_slug) review.set_state(new_state) e = add_state_change_event(review, login, prev_state, new_state) events.append(e) close_open_ballots(review, login) e = DocEvent(doc=review, rev=review.rev, by=login) e.type = "iesg_approved" e.desc = "IESG has approved the conflict review response" e.save() events.append(e) review.save_with_history(events) # send announcement send_mail_preformatted(request, form.cleaned_data['announcement_text']) c = DocEvent(type="added_comment", doc=review, rev=review.rev, by=login) c.desc = "The following approval message was sent\n"+form.cleaned_data['announcement_text'] c.save() return HttpResponseRedirect(review.get_absolute_url()) else: init = { "announcement_text" : default_approval_text(review) } form = AnnouncementForm(initial=init) return render(request, 'doc/conflict_review/approve.html', dict( review = review, conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document, form = form, ))
def approve_ballot(request, name): """Approve ballot, sending out announcement, changing state.""" doc = get_object_or_404(Document, docalias__name=name) if not doc.get_state("draft-iesg"): raise Http404 login = request.user.person approval_mail_event = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text") if not approval_mail_event: approval_mail_event = generate_approval_mail(request, doc) approval_text = approval_mail_event.text ballot_writeup_event = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text") if not ballot_writeup_event: ballot_writeup_event = generate_ballot_writeup(request, doc) ballot_writeup = ballot_writeup_event.text error_duplicate_rfc_editor_note = False e = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text") if e and (e.text != ""): if "RFC Editor Note" in ballot_writeup: error_duplicate_rfc_editor_note = True ballot_writeup += "\n\n" + e.text if error_duplicate_rfc_editor_note: return render(request, 'doc/draft/rfceditor_note_duplicate_error.html', {'doc': doc}) if "NOT be published" in approval_text: action = "do_not_publish" elif "To: RFC Editor" in approval_text: action = "to_rfc_editor" else: action = "to_announcement_list" # NOTE: according to Michelle Cotton <*****@*****.**> # (as per 2011-10-24) IANA is scraping these messages for # information so would like to know beforehand if the format # changes announcement = approval_text + "\n\n" + ballot_writeup if request.method == 'POST': if action == "do_not_publish": new_state = State.objects.get(used=True, type="draft-iesg", slug="dead") else: new_state = State.objects.get(used=True, type="draft-iesg", slug="ann") prev_state = doc.get_state("draft-iesg") prev_tags = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS) events = [] if approval_mail_event.pk == None: approval_mail_event.save() if ballot_writeup_event.pk == None: ballot_writeup_event.save() if new_state.slug == "ann" and new_state.slug != prev_state.slug and not request.POST.get( "skiprfceditorpost"): # start by notifying the RFC Editor import ietf.sync.rfceditor response, error = ietf.sync.rfceditor.post_approved_draft( settings.RFC_EDITOR_SYNC_NOTIFICATION_URL, doc.name) if error: return render( request, 'doc/draft/rfceditor_post_approved_draft_failed.html', dict(name=doc.name, response=response, error=error)) doc.set_state(new_state) doc.tags.remove(*prev_tags) # fixup document close_open_ballots(doc, login) e = DocEvent(doc=doc, rev=doc.rev, by=login) if action == "do_not_publish": e.type = "iesg_disapproved" e.desc = "Do Not Publish note has been sent to the RFC Editor" else: e.type = "iesg_approved" e.desc = "IESG has approved the document" e.save() events.append(e) e = add_state_change_event(doc, login, prev_state, new_state, prev_tags=prev_tags, new_tags=[]) if e: events.append(e) doc.save_with_history(events) # send announcement send_mail_preformatted(request, announcement) if action == "to_announcement_list": addrs = gather_address_lists( 'ballot_approved_ietf_stream_iana').as_strings(compact=False) send_mail_preformatted(request, announcement, extra=extra_automation_headers(doc), override={ "To": addrs.to, "CC": addrs.cc, "Bcc": None, "Reply-To": None }) msg = infer_message(announcement) msg.by = login msg.save() msg.related_docs.add(doc) return HttpResponseRedirect(doc.get_absolute_url()) return render(request, 'doc/ballot/approve_ballot.html', dict(doc=doc, action=action, announcement=announcement))
def approve(request, name): """Approve this status change, setting the appropriate state and send the announcements to the right parties.""" status_change = get_object_or_404(Document, type="statchg", name=name) if status_change.get_state('statchg').slug not in ('appr-pend'): raise Http404 login = request.user.get_profile() AnnouncementFormSet = formset_factory(AnnouncementForm,extra=0) if request.method == 'POST': formset = AnnouncementFormSet(request.POST) if formset.is_valid(): save_document_in_history(status_change) old_description = status_change.friendly_state() status_change.set_state(State.objects.get(type='statchg', slug='appr-sent')) new_description = status_change.friendly_state() log_state_changed(request, status_change, login, new_description, old_description) close_open_ballots(status_change, login) e = DocEvent(doc=status_change, by=login) e.type = "iesg_approved" e.desc = "IESG has approved the status change" e.save() status_change.time = e.time status_change.save() for form in formset.forms: send_mail_preformatted(request,form.cleaned_data['announcement_text']) c = DocEvent(type="added_comment", doc=status_change, by=login) c.desc = "The following approval message was sent\n"+form.cleaned_data['announcement_text'] c.save() for rel in status_change.relateddocument_set.filter(relationship__slug__in=RELATION_SLUGS): # Add a document event to each target c = DocEvent(type="added_comment", doc=rel.target.document, by=login) c.desc = "New status of %s approved by the IESG\n%s%s" % (newstatus(rel), settings.IDTRACKER_BASE_URL,reverse('doc_view', kwargs={'name': status_change.name})) c.save() return HttpResponseRedirect(status_change.get_absolute_url()) else: init = [] for rel in status_change.relateddocument_set.filter(relationship__slug__in=RELATION_SLUGS): init.append({"announcement_text" : default_approval_text(status_change,rel), "label": "Announcement text for %s to %s"%(rel.target.document.canonical_name(),newstatus(rel)), }) formset = AnnouncementFormSet(initial=init) for form in formset.forms: form.fields['announcement_text'].label=form.label return render_to_response('doc/status_change/approve.html', dict( doc = status_change, formset = formset, ), context_instance=RequestContext(request))
def approve_ballot(request, name): """Approve ballot, sending out announcement, changing state.""" doc = get_object_or_404(Document, docalias__name=name) if not doc.get_state("draft-iesg"): raise Http404() login = request.user.person e = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text") if not e: e = generate_approval_mail(request, doc) approval_text = e.text e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text") if not e: e = generate_ballot_writeup(request, doc) ballot_writeup = e.text if "NOT be published" in approval_text: action = "do_not_publish" elif "To: RFC Editor" in approval_text: action = "to_rfc_editor" else: action = "to_announcement_list" # NOTE: according to Michelle Cotton <*****@*****.**> # (as per 2011-10-24) IANA is scraping these messages for # information so would like to know beforehand if the format # changes announcement = approval_text + "\n\n" + ballot_writeup if request.method == 'POST': if action == "do_not_publish": new_state = State.objects.get(used=True, type="draft-iesg", slug="dead") else: new_state = State.objects.get(used=True, type="draft-iesg", slug="ann") prev_state = doc.get_state("draft-iesg") prev_tags = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS) if new_state.slug == "ann" and new_state.slug != prev_state.slug and not request.REQUEST.get("skiprfceditorpost"): # start by notifying the RFC Editor import ietf.sync.rfceditor response, error = ietf.sync.rfceditor.post_approved_draft(settings.RFC_EDITOR_SYNC_NOTIFICATION_URL, doc.name) if error: return render_to_response('doc/draft/rfceditor_post_approved_draft_failed.html', dict(name=doc.name, response=response, error=error), context_instance=RequestContext(request)) save_document_in_history(doc) doc.set_state(new_state) doc.tags.remove(*prev_tags) # fixup document close_open_ballots(doc, login) e = DocEvent(doc=doc, by=login) if action == "do_not_publish": e.type = "iesg_disapproved" e.desc = "Do Not Publish note has been sent to the RFC Editor" else: e.type = "iesg_approved" e.desc = "IESG has approved the document" e.save() change_description = e.desc + " and state has been changed to %s" % doc.get_state("draft-iesg").name e = add_state_change_event(doc, login, prev_state, new_state, prev_tags=prev_tags, new_tags=[]) doc.time = (e and e.time) or datetime.datetime.now() doc.save() email_state_changed(request, doc, change_description) email_ad(request, doc, doc.ad, login, change_description) # send announcement send_mail_preformatted(request, announcement) if action == "to_announcement_list": send_mail_preformatted(request, announcement, extra=extra_automation_headers(doc), override={ "To": "IANA <%s>"%settings.IANA_APPROVE_EMAIL, "CC": None, "Bcc": None, "Reply-To": None}) msg = infer_message(announcement) msg.by = login msg.save() msg.related_docs.add(doc) return HttpResponseRedirect(doc.get_absolute_url()) return render_to_response('doc/ballot/approve_ballot.html', dict(doc=doc, action=action, announcement=announcement), context_instance=RequestContext(request))
def approve(request, name): """Approve this status change, setting the appropriate state and send the announcements to the right parties.""" status_change = get_object_or_404(Document, type="statchg", name=name) if status_change.get_state('statchg').slug not in ('appr-pend'): raise Http404 login = request.user.person AnnouncementFormSet = formset_factory(AnnouncementForm, extra=0) if request.method == 'POST': formset = AnnouncementFormSet(request.POST) if formset.is_valid(): prev_state = status_change.get_state() new_state = State.objects.get(type='statchg', slug='appr-sent') status_change.set_state(new_state) events = [] events.append( add_state_change_event(status_change, login, prev_state, new_state)) close_open_ballots(status_change, login) e = DocEvent(doc=status_change, rev=status_change.rev, by=login) e.type = "iesg_approved" e.desc = "IESG has approved the status change" e.save() events.append(e) status_change.save_with_history(events) for form in formset.forms: send_mail_preformatted(request, form.cleaned_data['announcement_text']) c = DocEvent(type="added_comment", doc=status_change, rev=status_change.rev, by=login) c.desc = "The following approval message was sent\n" + form.cleaned_data[ 'announcement_text'] c.save() for rel in status_change.relateddocument_set.filter( relationship__slug__in=STATUSCHANGE_RELATIONS): # Add a document event to each target c = DocEvent(type="added_comment", doc=rel.target.document, rev=rel.target.document.rev, by=login) c.desc = "New status of %s approved by the IESG\n%s%s" % ( newstatus(rel), settings.IDTRACKER_BASE_URL, reverse('ietf.doc.views_doc.document_main', kwargs={'name': status_change.name})) c.save() return HttpResponseRedirect(status_change.get_absolute_url()) else: init = [] for rel in status_change.relateddocument_set.filter( relationship__slug__in=STATUSCHANGE_RELATIONS): init.append({ "announcement_text": default_approval_text(status_change, rel), "label": "Announcement text for %s to %s" % (rel.target.document.canonical_name(), newstatus(rel)), }) formset = AnnouncementFormSet(initial=init) for form in formset.forms: form.fields['announcement_text'].label = form.label return render(request, 'doc/status_change/approve.html', dict( doc=status_change, formset=formset, ))
def test_change_state(self): make_test_data() group = Group.objects.get(acronym="ames") charter = group.charter url = urlreverse('ietf.doc.views_charter.change_state', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) first_state = charter.get_state() # normal get r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) self.assertEqual(len(q('form select[name=charter_state]')), 1) # faulty post r = self.client.post(url, dict(charter_state="-12345")) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) self.assertTrue(len(q('form .has-error')) > 0) self.assertEqual(charter.get_state(), first_state) # change state for slug in ("intrev", "extrev", "iesgrev"): s = State.objects.get(used=True, type="charter", slug=slug) events_before = charter.docevent_set.count() empty_outbox() r = self.client.post(url, dict(charter_state=str(s.pk), message="test message")) self.assertEqual(r.status_code, 302) charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) self.assertEqual(charter.get_state_slug(), slug) events_now = charter.docevent_set.count() self.assertTrue(events_now > events_before) def find_event(t): return [e for e in charter.docevent_set.all()[:events_now - events_before] if e.type == t] self.assertTrue("state changed" in find_event("changed_state")[0].desc.lower()) if slug in ("intrev", "extrev"): self.assertTrue(find_event("created_ballot")) self.assertEqual(len(outbox), 3 if slug=="intrev" else 2 ) if slug=="intrev": self.assertIn("Internal WG Review", outbox[-3]['Subject']) self.assertIn("iab@", outbox[-3]['To']) self.assertIn("iesg@", outbox[-3]['To']) self.assertIn("A new IETF WG", outbox[-3].get_payload()) body = outbox[-3].get_payload() for word in ["Chairs", "Ames Man <*****@*****.**>", "Secretaries", "Secretary <*****@*****.**>", "Assigned Area Director", "Areað Irector <*****@*****.**>", "Mailing list", "*****@*****.**", "Charter", "Milestones"]: self.assertIn(word, body) self.assertIn("state changed", outbox[-2]['Subject'].lower()) self.assertIn("iesg-secretary@", outbox[-2]['To']) body = outbox[-2].get_payload() for word in ["WG", "Charter", ]: self.assertIn(word, body) self.assertIn("State Update Notice", outbox[-1]['Subject']) self.assertIn("ames-chairs@", outbox[-1]['To']) body = outbox[-1].get_payload() for word in ["State changed", "Datatracker URL", ]: self.assertIn(word, body) by = Person.objects.get(user__username="******") for slug in ('extrev','iesgrev'): close_open_ballots(charter,by) r = self.client.post(url, dict(charter_state=str(State.objects.get(used=True,type='charter',slug=slug).pk) )) self.assertTrue(r.status_code,302) charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) self.assertTrue(charter.ballot_open('approve')) # Exercise internal review of a recharter group = Group.objects.get(acronym="mars") charter = group.charter url = urlreverse('ietf.doc.views_charter.change_state', kwargs=dict(name=charter.name)) empty_outbox() r = self.client.post(url, dict(charter_state=str(State.objects.get(used=True,type="charter",slug="intrev").pk), message="test")) self.assertEqual(r.status_code, 302) self.assertTrue("A new charter" in outbox[-3].get_payload())
def approve(request, name): """Approve charter, changing state, fixing revision, copying file to final location.""" charter = get_object_or_404(Document, type="charter", name=name) group = charter.group login = request.user.person e = charter.latest_event(WriteupDocEvent, type="changed_action_announcement") if not e: announcement = default_action_text(group, charter, login).text else: announcement = e.text if request.method == 'POST': new_charter_state = State.objects.get(used=True, type="charter", slug="approved") prev_charter_state = charter.get_state() save_document_in_history(charter) charter.set_state(new_charter_state) close_open_ballots(charter, login) # approve e = DocEvent(doc=charter, by=login) e.type = "iesg_approved" e.desc = "IESG has approved the charter" e.save() change_description = e.desc group_state_change_event = change_group_state_after_charter_approval(group, login) if group_state_change_event: change_description += " and group state has been changed to %s" % group.state.name add_state_change_event(charter, login, prev_charter_state, new_charter_state) fix_charter_revision_after_approval(charter, login) email_admin_re_charter(request, group, "Charter state changed to %s" % new_charter_state.name, change_description,'charter_state_edit_admin_needed') # move milestones over milestones_to_delete = list(group.groupmilestone_set.filter(state__in=("active", "review"))) for m in group.groupmilestone_set.filter(state="charter"): # see if we got this milestone already (i.e. it was copied # verbatim to the charter) found = False for i, o in enumerate(milestones_to_delete): if o.desc == m.desc and o.due == m.due and set(o.docs.all()) == set(m.docs.all()): found = True break if found: # keep existing, whack charter milestone if not o.state_id == "active": save_milestone_in_history(o) o.state_id = "active" o.save() MilestoneGroupEvent.objects.create( group=group, type="changed_milestone", by=login, desc="Changed milestone \"%s\", set state to active from review" % o.desc, milestone=o) del milestones_to_delete[i] # don't generate a DocEvent for this, it's implicit in the approval event save_milestone_in_history(m) m.state_id = "deleted" m.save() else: # move charter milestone save_milestone_in_history(m) m.state_id = "active" m.save() MilestoneGroupEvent.objects.create( group=group, type="changed_milestone", by=login, desc="Added milestone \"%s\", due %s, from approved charter" % (m.desc, m.due), milestone=m) for m in milestones_to_delete: save_milestone_in_history(m) m.state_id = "deleted" m.save() MilestoneGroupEvent.objects.create( group=group, type="changed_milestone", by=login, desc="Deleted milestone \"%s\", not present in approved charter" % m.desc, milestone=m) # send announcement send_mail_preformatted(request, announcement) return HttpResponseRedirect(charter.get_absolute_url()) return render_to_response('doc/charter/approve.html', dict(charter=charter, announcement=announcement), context_instance=RequestContext(request))
def change_state(request, name, option=None): """Change state of charter, notifying parties as necessary and logging the change as a comment.""" charter = get_object_or_404(Document, type="charter", name=name) group = charter.group if not can_manage_group_type(request.user, group.type_id): return HttpResponseForbidden("You don't have permission to access this view") chartering_type = get_chartering_type(charter) initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review") if charter.get_state_slug() != "infrev" or (initial_review and initial_review.expires < datetime.datetime.now()) or chartering_type == "rechartering": initial_review = None login = request.user.person if request.method == 'POST': form = ChangeStateForm(request.POST, group=group) if form.is_valid(): clean = form.cleaned_data charter_rev = charter.rev if option in ("initcharter", "recharter"): if group.type_id == "wg": charter_state = State.objects.get(used=True, type="charter", slug="infrev") else: charter_state = clean['charter_state'] # make sure we have the latest revision set, if we # abandoned a charter before, we could have reset the # revision to latest approved prev_revs = charter.history_set.order_by('-rev')[:1] if prev_revs and prev_revs[0].rev > charter_rev: charter_rev = prev_revs[0].rev if "-" not in charter_rev: charter_rev = charter_rev + "-00" elif option == "abandon": oldstate = group.state if oldstate.slug in ("proposed", "bof", "unknown"): charter_state = State.objects.get(used=True, type="charter", slug="notrev") #TODO : set an abandoned state and leave some comments here group.state = GroupStateName.objects.get(slug='abandon') group.save() e = ChangeStateGroupEvent(group=group, type="changed_state") e.time = group.time e.by = login e.state_id = group.state.slug e.desc = "Group state changed to %s from %s" % (group.state, oldstate) e.save() else: charter_state = State.objects.get(used=True, type="charter", slug="approved") charter_rev = approved_revision(charter.rev) else: charter_state = clean['charter_state'] comment = clean['comment'].rstrip() message = clean['message'] if charter_state != charter.get_state(): # Charter state changed save_document_in_history(charter) prev_state = charter.get_state() new_state = charter_state charter.set_state(new_state) charter.rev = charter_rev if option != "abandon": add_state_change_event(charter, login, prev_state, new_state) else: # kill hanging ballots close_open_ballots(charter, login) # Special log for abandoned efforts e = DocEvent(type="changed_document", doc=charter, by=login) e.desc = "IESG has abandoned the chartering effort" e.save() if comment: c = DocEvent(type="added_comment", doc=charter, by=login) c.desc = comment c.save() charter.time = datetime.datetime.now() charter.save() if charter_state.slug == 'intrev': email_charter_internal_review(request,charter) if message or charter_state.slug == "intrev" or charter_state.slug == "extrev": email_admin_re_charter(request, group, "Charter state changed to %s" % charter_state.name, message,'charter_state_edit_admin_needed') # TODO - do we need a seperate set of recipients for state changes to charters vrs other kind of documents email_state_changed(request, charter, "State changed to %s." % charter_state, 'doc_state_edited') if charter_state.slug == "intrev" and group.type_id == "wg": if request.POST.get("ballot_wo_extern"): create_ballot_if_not_open(charter, login, "r-wo-ext") else: create_ballot_if_not_open(charter, login, "r-extrev") default_review_text(group, charter, login) default_action_text(group, charter, login) elif charter_state.slug == "iesgrev": create_ballot_if_not_open(charter, login, "approve") elif charter_state.slug == "approved": change_group_state_after_charter_approval(group, login) fix_charter_revision_after_approval(charter, login) if charter_state.slug == "infrev" and clean["initial_time"] and clean["initial_time"] != 0: e = InitialReviewDocEvent(type="initial_review", by=login, doc=charter) e.expires = datetime.datetime.now() + datetime.timedelta(weeks=clean["initial_time"]) e.desc = "Initial review time expires %s" % e.expires.strftime("%Y-%m-%d") e.save() return redirect('doc_view', name=charter.name) else: hide = ['initial_time'] s = charter.get_state() init = dict(charter_state=s.pk if s and option != "recharter" else None) if option == "abandon": hide = ['initial_time', 'charter_state'] if group.type_id == "wg": if option == "recharter": hide = ['initial_time', 'charter_state', 'message'] init = dict() elif option == "initcharter": hide = ['charter_state'] init = dict(initial_time=1, message='%s has initiated chartering of the proposed %s:\n "%s" (%s).' % (login.plain_name(), group.type.name, group.name, group.acronym)) elif option == "abandon": hide = ['initial_time', 'charter_state'] init = dict(message='%s has abandoned the chartering effort on the %s:\n "%s" (%s).' % (login.plain_name(), group.type.name, group.name, group.acronym)) form = ChangeStateForm(hide=hide, initial=init, group=group) prev_charter_state = None charter_hists = DocHistory.objects.filter(doc=charter).exclude(states__type="charter", states__slug=charter.get_state_slug()).order_by("-time")[:1] if charter_hists: prev_charter_state = charter_hists[0].get_state() title = { "initcharter": "Initiate chartering of %s %s" % (group.acronym, group.type.name), "recharter": "Recharter %s %s" % (group.acronym, group.type.name), "abandon": "Abandon effort on %s %s" % (group.acronym, group.type.name), }.get(option) if not title: title = "Change chartering state of %s %s" % (group.acronym, group.type.name) def state_pk(slug): return State.objects.get(used=True, type="charter", slug=slug).pk info_msg = {} if group.type_id == "wg": info_msg[state_pk("infrev")] = 'The proposed charter for %s "%s" (%s) has been set to Informal IESG review by %s.' % (group.type.name, group.name, group.acronym, login.plain_name()) info_msg[state_pk("intrev")] = 'The proposed charter for %s "%s" (%s) has been set to Internal review by %s.\nPlease place it on the next IESG telechat if it has not already been placed.' % (group.type.name, group.name, group.acronym, login.plain_name()) info_msg[state_pk("extrev")] = 'The proposed charter for %s "%s" (%s) has been set to External review by %s.\nPlease send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: ' % (group.type.name, group.name, group.acronym, login.plain_name()) states_for_ballot_wo_extern = State.objects.none() if group.type_id == "wg": states_for_ballot_wo_extern = State.objects.filter(used=True, type="charter", slug="intrev").values_list("pk", flat=True) return render_to_response('doc/charter/change_state.html', dict(form=form, doc=group.charter, login=login, option=option, prev_charter_state=prev_charter_state, title=title, initial_review=initial_review, chartering_type=chartering_type, info_msg=json.dumps(info_msg), states_for_ballot_wo_extern=json.dumps(list(states_for_ballot_wo_extern)), ), context_instance=RequestContext(request))
def approve(request, name): """Approve charter, changing state, fixing revision, copying file to final location.""" charter = get_object_or_404(Document, type="charter", name=name) group = charter.group by = request.user.person e = charter.latest_event(WriteupDocEvent, type="changed_action_announcement") if not e: announcement = default_action_text(group, charter, by).text else: announcement = e.text if request.method == 'POST': new_charter_state = State.objects.get(used=True, type="charter", slug="approved") prev_charter_state = charter.get_state() charter.set_state(new_charter_state) close_open_ballots(charter, by) events = [] # approve e = DocEvent(doc=charter, rev=charter.rev, by=by) e.type = "iesg_approved" e.desc = "IESG has approved the charter" e.save() events.append(e) change_description = e.desc group_state_change_event = change_group_state_after_charter_approval( group, by) if group_state_change_event: change_description += " and group state has been changed to %s" % group.state.name e = add_state_change_event(charter, by, prev_charter_state, new_charter_state) if e: events.append(e) fix_charter_revision_after_approval(charter, by) charter.save_with_history(events) email_admin_re_charter( request, group, "Charter state changed to \"%s\"" % new_charter_state.name, change_description, 'charter_state_edit_admin_needed') # move milestones over milestones_to_delete = list( group.groupmilestone_set.filter(state__in=("active", "review"))) for m in group.groupmilestone_set.filter(state="charter"): # see if we got this milestone already (i.e. it was copied # verbatim to the charter) found = False for i, o in enumerate(milestones_to_delete): if o.desc == m.desc and o.due == m.due and set( o.docs.all()) == set(m.docs.all()): found = True break if found: # keep existing, whack charter milestone if not o.state_id == "active": save_milestone_in_history(o) o.state_id = "active" o.save() MilestoneGroupEvent.objects.create( group=group, type="changed_milestone", by=by, desc= "Changed milestone \"%s\", set state to active from review" % o.desc, milestone=o) del milestones_to_delete[i] # don't generate a DocEvent for this, it's implicit in the approval event save_milestone_in_history(m) m.state_id = "deleted" m.save() else: # move charter milestone save_milestone_in_history(m) m.state_id = "active" m.save() MilestoneGroupEvent.objects.create( group=group, type="changed_milestone", by=by, desc="Added milestone \"%s\", due %s, from approved charter" % (m.desc, m.due), milestone=m) for m in milestones_to_delete: save_milestone_in_history(m) m.state_id = "deleted" m.save() MilestoneGroupEvent.objects.create( group=group, type="changed_milestone", by=by, desc="Deleted milestone \"%s\", not present in approved charter" % m.desc, milestone=m) # send announcement send_mail_preformatted(request, announcement) return HttpResponseRedirect(charter.get_absolute_url()) return render(request, 'doc/charter/approve.html', dict(charter=charter, announcement=announcement))
def change_state(request, name, option=None): """Change state of charter, notifying parties as necessary and logging the change as a comment.""" charter = get_object_or_404(Document, type="charter", name=name) group = charter.group if not can_manage_group_type(request.user, group): return HttpResponseForbidden( "You don't have permission to access this view") chartering_type = get_chartering_type(charter) initial_review = charter.latest_event(InitialReviewDocEvent, type="initial_review") if charter.get_state_slug() != "infrev" or ( initial_review and initial_review.expires < datetime.datetime.now()) or chartering_type == "rechartering": initial_review = None by = request.user.person if request.method == 'POST': form = ChangeStateForm(request.POST, group=group) if form.is_valid(): clean = form.cleaned_data charter_rev = charter.rev if option in ("initcharter", "recharter"): if group.type_id == "wg": charter_state = State.objects.get(used=True, type="charter", slug="infrev") else: charter_state = clean['charter_state'] # make sure we have the latest revision set, if we # abandoned a charter before, we could have reset the # revision to latest approved prev_revs = charter.history_set.order_by('-rev')[:1] if prev_revs and prev_revs[0].rev > charter_rev: charter_rev = prev_revs[0].rev if "-" not in charter_rev: charter_rev = charter_rev + "-00" elif option == "abandon": oldstate = group.state if oldstate.slug in ("proposed", "bof", "unknown"): charter_state = State.objects.get(used=True, type="charter", slug="notrev") #TODO : set an abandoned state and leave some comments here group.state = GroupStateName.objects.get(slug='abandon') group.save() e = ChangeStateGroupEvent(group=group, type="changed_state") e.time = group.time e.by = by e.state_id = group.state.slug e.desc = "Group state changed to \"%s\" from \"%s\"" % ( group.state, oldstate) e.save() else: charter_state = State.objects.get(used=True, type="charter", slug="approved") charter_rev = approved_revision(charter.rev) else: charter_state = clean['charter_state'] comment = clean['comment'].rstrip() message = clean['message'] if charter_state != charter.get_state(): events = [] prev_state = charter.get_state() new_state = charter_state charter.set_state(new_state) charter.rev = charter_rev if option != "abandon": e = add_state_change_event(charter, by, prev_state, new_state) if e: events.append(e) else: # kill hanging ballots close_open_ballots(charter, by) # Special log for abandoned efforts e = DocEvent(type="changed_document", doc=charter, rev=charter.rev, by=by) e.desc = "Chartering effort abandoned" e.save() events.append(e) if comment: events.append( DocEvent.objects.create(type="added_comment", doc=charter, rev=charter.rev, by=by, desc=comment)) charter.save_with_history(events) if charter_state.slug == 'intrev': email_charter_internal_review(request, charter) if message or charter_state.slug == "intrev" or charter_state.slug == "extrev": email_admin_re_charter( request, group, "Charter state changed to \"%s\"" % charter_state.name, message, 'charter_state_edit_admin_needed') # TODO - do we need a seperate set of recipients for state changes to charters vrs other kind of documents email_state_changed(request, charter, "State changed to %s." % charter_state, 'doc_state_edited') if charter_state.slug == "intrev" and group.type_id == "wg": if request.POST.get("ballot_wo_extern"): create_ballot_if_not_open(charter, by, "r-wo-ext") else: create_ballot_if_not_open(charter, by, "r-extrev") (e1, e2) = default_review_text(group, charter, by) e1.save() e2.save() e = default_action_text(group, charter, by) e.save() elif charter_state.slug in ["extrev", "iesgrev"]: create_ballot_if_not_open(charter, by, "approve") elif charter_state.slug == "approved": change_group_state_after_charter_approval(group, by) fix_charter_revision_after_approval(charter, by) if charter_state.slug == "infrev" and clean[ "initial_time"] and clean["initial_time"] != 0: e = InitialReviewDocEvent(type="initial_review", by=by, doc=charter, rev=charter.rev) e.expires = datetime.datetime.now() + datetime.timedelta( weeks=clean["initial_time"]) e.desc = "Initial review time expires %s" % e.expires.strftime( "%Y-%m-%d") e.save() return redirect('ietf.doc.views_doc.document_main', name=charter.name) else: hide = ['initial_time'] s = charter.get_state() init = dict( charter_state=s.pk if s and option != "recharter" else None) if option == "abandon": hide = ['initial_time', 'charter_state'] if group.type_id == "wg": if option == "recharter": hide = ['initial_time', 'charter_state', 'message'] init = dict() elif option == "initcharter": hide = ['charter_state'] init = dict( initial_time=1, message= '%s has initiated chartering of the proposed %s:\n "%s" (%s).' % (by.plain_name(), group.type.name, group.name, group.acronym)) elif option == "abandon": hide = ['initial_time', 'charter_state'] init = dict( message= '%s has abandoned the chartering effort on the %s:\n "%s" (%s).' % (by.plain_name(), group.type.name, group.name, group.acronym)) form = ChangeStateForm(hide=hide, initial=init, group=group) prev_charter_state = None charter_hists = DocHistory.objects.filter(doc=charter).exclude( states__type="charter", states__slug=charter.get_state_slug()).order_by("-time")[:1] if charter_hists: prev_charter_state = charter_hists[0].get_state() title = { "initcharter": "Initiate chartering of %s %s" % (group.acronym, group.type.name), "recharter": "Recharter %s %s" % (group.acronym, group.type.name), "abandon": "Abandon effort on %s %s" % (group.acronym, group.type.name), }.get(option) if not title: title = "Change chartering state of %s %s" % (group.acronym, group.type.name) def state_pk(slug): return State.objects.get(used=True, type="charter", slug=slug).pk info_msg = {} if group.type_id == "wg": info_msg[state_pk( "infrev" )] = 'The proposed charter for %s "%s" (%s) has been set to Informal IESG review by %s.' % ( group.type.name, group.name, group.acronym, by.plain_name()) info_msg[state_pk( "intrev" )] = 'The proposed charter for %s "%s" (%s) has been set to Internal review by %s.\nPlease place it on the next IESG telechat if it has not already been placed.' % ( group.type.name, group.name, group.acronym, by.plain_name()) info_msg[state_pk( "extrev" )] = 'The proposed charter for %s "%s" (%s) has been set to External review by %s.\nPlease send out the external review announcement to the appropriate lists.\n\nSend the announcement to other SDOs: Yes\nAdditional recipients of the announcement: ' % ( group.type.name, group.name, group.acronym, by.plain_name()) states_for_ballot_wo_extern = State.objects.none() if group.type_id == "wg": states_for_ballot_wo_extern = State.objects.filter( used=True, type="charter", slug="intrev").values_list("pk", flat=True) return render( request, 'doc/charter/change_state.html', dict( form=form, doc=group.charter, option=option, prev_charter_state=prev_charter_state, title=title, initial_review=initial_review, chartering_type=chartering_type, info_msg=json.dumps(info_msg), states_for_ballot_wo_extern=json.dumps( list(states_for_ballot_wo_extern)), ))