From 7e00fa14240b34c8a15b7f48d59c360616868769 Mon Sep 17 00:00:00 2001
From: Ole Laursen <olau@iola.dk>
Date: Mon, 13 Jan 2014 16:08:58 +0000
Subject: [PATCH] Move the Secretariat group email utility to wginfo and the
 rest of the wgcharter stuff to doc/(views|urls|utils)_charter.py alongside
 the other document types  - Legacy-Id: 7121

---
 .../tests.py => doc/tests_charter.py}         |   8 +-
 ietf/doc/urls.py                              |   2 +-
 ietf/doc/urls_charter.py                      |  17 +++
 .../mails.py => doc/utils_charter.py}         | 144 ++++++++++++++----
 .../views.py => doc/views_charter.py}         |  28 ++--
 ietf/doc/views_doc.py                         |   7 +-
 ietf/iesg/views.py                            |   2 +-
 ietf/settings.py                              |   1 -
 .../charter}/action_text.txt                  |   2 +-
 .../charter}/announcement_text.html           |   0
 .../{wgcharter => doc/charter}/approve.html   |   0
 .../charter}/ballot_issued.html               |   0
 .../charter}/ballot_writeup.txt               |   0
 .../charter}/ballot_writeupnotes.html         |   0
 .../{wgcharter => doc/charter}/change_ad.html |   0
 .../charter}/change_state.html                |   0
 .../charter}/charter_with_milestones.txt      |   0
 .../charter}/edit_notify.html                 |   0
 .../charter}/edit_telechat_date.html          |   0
 .../{wgcharter => doc/charter}/group_info.txt |   0
 .../charter}/issue_ballot_mail.txt            |   0
 .../charter}/review_text.txt                  |   2 +-
 .../{wgcharter => doc/charter}/submit.html    |   0
 .../email_secretariat.txt                     |   0
 ietf/wgcharter/__init__.py                    |   1 -
 ietf/wgcharter/migrate.py                     | 101 ------------
 ietf/wgcharter/models.py                      |   0
 ietf/wgcharter/urls.py                        |  17 ---
 ietf/wgcharter/utils.py                       | 110 -------------
 ietf/wginfo/edit.py                           |   4 +-
 ietf/wginfo/mails.py                          |  14 ++
 31 files changed, 169 insertions(+), 291 deletions(-)
 rename ietf/{wgcharter/tests.py => doc/tests_charter.py} (97%)
 create mode 100644 ietf/doc/urls_charter.py
 rename ietf/{wgcharter/mails.py => doc/utils_charter.py} (56%)
 rename ietf/{wgcharter/views.py => doc/views_charter.py} (96%)
 rename ietf/templates/{wgcharter => doc/charter}/action_text.txt (90%)
 rename ietf/templates/{wgcharter => doc/charter}/announcement_text.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/approve.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/ballot_issued.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/ballot_writeup.txt (100%)
 rename ietf/templates/{wgcharter => doc/charter}/ballot_writeupnotes.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/change_ad.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/change_state.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/charter_with_milestones.txt (100%)
 rename ietf/templates/{wgcharter => doc/charter}/edit_notify.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/edit_telechat_date.html (100%)
 rename ietf/templates/{wgcharter => doc/charter}/group_info.txt (100%)
 rename ietf/templates/{wgcharter => doc/charter}/issue_ballot_mail.txt (100%)
 rename ietf/templates/{wgcharter => doc/charter}/review_text.txt (91%)
 rename ietf/templates/{wgcharter => doc/charter}/submit.html (100%)
 rename ietf/templates/{wgcharter => wginfo}/email_secretariat.txt (100%)
 delete mode 100644 ietf/wgcharter/__init__.py
 delete mode 100644 ietf/wgcharter/migrate.py
 delete mode 100644 ietf/wgcharter/models.py
 delete mode 100644 ietf/wgcharter/urls.py
 delete mode 100644 ietf/wgcharter/utils.py

diff --git a/ietf/wgcharter/tests.py b/ietf/doc/tests_charter.py
similarity index 97%
rename from ietf/wgcharter/tests.py
rename to ietf/doc/tests_charter.py
index 477d2af99..44e1e9933 100644
--- a/ietf/wgcharter/tests.py
+++ b/ietf/doc/tests_charter.py
@@ -19,7 +19,7 @@ from ietf.group.utils import *
 from ietf.name.models import *
 from ietf.person.models import *
 from ietf.iesg.models import TelechatDate
-from ietf.wgcharter.utils import *
+from ietf.doc.utils_charter import *
 
 class EditCharterTests(TestCase):
     def setUp(self):
@@ -71,13 +71,13 @@ class EditCharterTests(TestCase):
             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_document")[0].desc)
+            self.assertTrue("state changed" in find_event("changed_document")[0].desc.lower())
 
             if slug in ("intrev", "iesgrev"):
                 self.assertTrue(find_event("created_ballot"))
 
             self.assertEqual(len(outbox), mailbox_before + 1)
-            self.assertTrue("State changed" in outbox[-1]['Subject'])
+            self.assertTrue("state changed" in outbox[-1]['Subject'].lower())
                     
     def test_edit_telechat_date(self):
         make_test_data()
@@ -272,7 +272,7 @@ class ApproveCharterTests(TestCase):
 
         self.assertEqual(len(outbox), mailbox_before + 2)
         self.assertTrue("WG Action" in outbox[-1]['Subject'])
-        self.assertTrue("Charter approved" in outbox[-2]['Subject'])
+        self.assertTrue("approved" in outbox[-2]['Subject'].lower())
 
         self.assertEqual(group.groupmilestone_set.filter(state="charter").count(), 0)
         self.assertEqual(group.groupmilestone_set.filter(state="active").count(), 2)
diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py
index 04184a1cd..5e77d528b 100644
--- a/ietf/doc/urls.py
+++ b/ietf/doc/urls.py
@@ -96,7 +96,7 @@ urlpatterns = patterns('',
 
     url(r'^help/state/(?P<type>[\w-]+)/$', 'ietf.doc.views_help.state_help', name="state_help"),
 
-    (r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.wgcharter.urls')),
+    (r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.doc.urls_charter')),
     (r'^(?P<name>[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')),
     (r'^(?P<name>[A-Za-z0-9._+-]+)/status-change/', include('ietf.doc.urls_status_change')),
 )
diff --git a/ietf/doc/urls_charter.py b/ietf/doc/urls_charter.py
new file mode 100644
index 000000000..763111182
--- /dev/null
+++ b/ietf/doc/urls_charter.py
@@ -0,0 +1,17 @@
+# Copyright The IETF Trust 2011, All Rights Reserved
+
+from django.conf.urls import patterns, url
+
+urlpatterns = patterns('',
+    url(r'^state/$', "ietf.doc.views_charter.change_state", name='charter_change_state'),
+    url(r'^(?P<option>initcharter|recharter|abandon)/$', "ietf.doc.views_charter.change_state", name='charter_startstop_process'),
+    url(r'^telechat/$', "ietf.doc.views_charter.telechat_date", name='charter_telechat_date'),
+    url(r'^notify/$', "ietf.doc.views_charter.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'^ballotwriteupnotes/$', "ietf.doc.views_charter.ballot_writeupnotes"),
+    url(r'^approve/$', "ietf.doc.views_charter.approve", name='charter_approve'),
+    url(r'^submit/$', "ietf.doc.views_charter.submit", name='charter_submit'),
+    url(r'^submit/(?P<option>initcharter|recharter)/$', "ietf.doc.views_charter.submit", name='charter_submit'), # shouldn't be here
+    url(r'^withmilestones-(?P<rev>[0-9-]+).txt$', "ietf.doc.views_charter.charter_with_milestones_txt", name='charter_with_milestones_txt'),
+)
diff --git a/ietf/wgcharter/mails.py b/ietf/doc/utils_charter.py
similarity index 56%
rename from ietf/wgcharter/mails.py
rename to ietf/doc/utils_charter.py
index f024193cc..ffc60bf38 100644
--- a/ietf/wgcharter/mails.py
+++ b/ietf/doc/utils_charter.py
@@ -1,42 +1,120 @@
-# generation of mails 
-
-import textwrap, datetime
+import re, datetime, os, textwrap
 
 from django.template.loader import render_to_string
 from django.utils.html import strip_tags
 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.ipr.search import iprs_from_docs
-from ietf.doc.models import WriteupDocEvent, DocAlias, BallotPositionDocEvent
+from ietf.utils.mail import send_mail_text
 from ietf.person.models import Person
-from ietf.wgcharter.utils import *
+from ietf.group.models import GroupEvent, ChangeStateGroupEvent
+from ietf.doc.models import Document, DocAlias, DocHistory, RelatedDocument, DocumentAuthor
+from ietf.doc.models import DocEvent, NewRevisionDocEvent, WriteupDocEvent, BallotPositionDocEvent
+from ietf.utils.history import find_history_active_at
 
-def email_secretariat(request, group, type, text):
-    to = ["iesg-secretary@ietf.org"]
 
-    types = {}
-    types['state'] = "State changed"
-    types['state-notrev'] = "State changed to Not currently under review"
-    types['state-infrev'] = "State changed to Informal review"
-    types['state-intrev'] = "State changed to Internal review"
-    types['state-extrev'] = "State changed to External review"
-    types['state-iesgrev'] = "State changed to IESG review"
-    types['state-approved'] = "Charter approved"
-    types['conclude'] = "Request closing of group"
+def log_state_changed(request, doc, by, prev_state):
+    e = DocEvent(doc=doc, by=by)
+    e.type = "changed_document"
+    e.desc = u"State changed to <b>%s</b> from %s" % (
+        doc.get_state().name,
+        prev_state.name if prev_state else "None")
+    e.save()
+    return e
 
-    subject = u"Regarding %s %s: %s" % (group.type.name, group.acronym, types[type])
+def next_revision(rev):
+    if rev == "":
+        return "00-00"
+    m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
+    if m.group('minor'):
+        return "%s-%#02d" % (m.group('major'), int(m.group('minor')) + 1)
+    else:
+        return "%s-00" % (m.group('major'))
+
+def approved_revision(rev):
+    if rev == "":
+        return ""
+    m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
+    return m.group('major')
+
+def next_approved_revision(rev):
+    if rev == "":
+        return "01"
+    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 historic_milestones_for_charter(charter, rev):
+    """Return GroupMilestone/GroupMilestoneHistory objects for charter
+    document at rev by looking through the history."""
+
+    chartering = "-" in rev
+    if chartering:
+        need_state = "charter"
+    else:
+        need_state = "active"
+
+    # slight complication - we can assign milestones to a revision up
+    # until the point where the next superseding revision is
+    # published, so that time shall be our limit
+    revision_event = charter.latest_event(NewRevisionDocEvent, type="new_revision", rev=rev)
+    if not revision_event:
+        return []
+
+    e = charter.docevent_set.filter(time__gt=revision_event.time, type="new_revision").order_by("time")
+    if not chartering:
+        e = e.exclude(newrevisiondocevent__rev__contains="-")
+
+    if e:
+        # subtract a margen of error to avoid collisions with
+        # milestones being published at the same time as the new
+        # revision (when approving a charter)
+        just_before_next_rev = e[0].time - datetime.timedelta(seconds=5)
+    else:
+        just_before_next_rev = datetime.datetime.now()
+
+    res = []
+    for m in charter.chartered_group.groupmilestone_set.all():
+        mh = find_history_active_at(m, just_before_next_rev)
+        if mh and mh.state_id == need_state:
+            res.append(mh)
+
+    return res
     
-    text = strip_tags(text)
-    send_mail(request, to, None, subject,
-              "wgcharter/email_secretariat.txt",
-              dict(text=text,
-                   group=group,
-                   group_url=settings.IDTRACKER_BASE_URL + urlreverse('group_charter', kwargs=dict(acronym=group.acronym)),
-                   charter_url=settings.IDTRACKER_BASE_URL + urlreverse('doc_view', kwargs=dict(name=group.charter.name)),
-                   )
-              )
+
+def update_telechat(request, doc, by, new_telechat_date):
+    # FIXME: reuse function in doc/utils.py instead of this one
+    # (need to fix auto-setting returning item problem first though)
+    from ietf.doc.models import TelechatDocEvent
+    
+    on_agenda = bool(new_telechat_date)
+
+    prev = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
+    prev_telechat = prev.telechat_date if prev else None
+    prev_agenda = bool(prev_telechat)
+    
+    e = TelechatDocEvent()
+    e.type = "scheduled_for_telechat"
+    e.by = by
+    e.doc = doc
+    e.telechat_date = new_telechat_date
+    
+    if on_agenda != prev_agenda:
+        if on_agenda:
+            e.desc = "Placed on agenda for telechat - %s" % new_telechat_date
+        else:
+            e.desc = "Removed from agenda for telechat"
+        e.save()
+    elif on_agenda and new_telechat_date != prev_telechat:
+        e.desc = "Telechat date has been changed to <b>%s</b> from <b>%s</b>" % (new_telechat_date, prev_telechat)
+        e.save()
 
 def email_state_changed(request, doc, text):
     to = [e.strip() for e in doc.notify.replace(';', ',').split(',')]
@@ -51,14 +129,13 @@ def email_state_changed(request, doc, text):
                    "State changed: %s-%s" % (doc.canonical_name(), doc.rev),
                    text)
 
-    
 def generate_ballot_writeup(request, doc):
     e = WriteupDocEvent()
     e.type = "changed_ballot_writeup_text"
     e.by = request.user.person
     e.doc = doc
     e.desc = u"Ballot writeup was generated"
-    e.text = unicode(render_to_string("wgcharter/ballot_writeup.txt"))
+    e.text = unicode(render_to_string("doc/charter/ballot_writeup.txt"))
     e.save()
     
     return e
@@ -73,7 +150,7 @@ def default_action_text(group, charter, user):
     e.by = user
     e.type = "changed_action_announcement"
     e.desc = "%s action text was changed" % group.type.name
-    e.text = render_to_string("wgcharter/action_text.txt",
+    e.text = render_to_string("doc/charter/action_text.txt",
                               dict(group=group,
                                    charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(),
                                    charter_text=read_charter_text(charter),
@@ -93,7 +170,7 @@ def default_review_text(group, charter, user):
     e.by = user
     e.type = "changed_review_announcement"
     e.desc = "%s review text was changed" % group.type.name
-    e.text = render_to_string("wgcharter/review_text.txt",
+    e.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),
@@ -163,7 +240,7 @@ def generate_issue_ballot_mail(request, doc, ballot):
     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",
+    return render_to_string("doc/charter/issue_ballot_mail.txt",
                             dict(doc=doc,
                                  doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
                                  active_ad_positions=active_ad_positions,
@@ -174,3 +251,4 @@ def generate_issue_ballot_mail(request, doc, ballot):
                                  )
                             )
 
+        
diff --git a/ietf/wgcharter/views.py b/ietf/doc/views_charter.py
similarity index 96%
rename from ietf/wgcharter/views.py
rename to ietf/doc/views_charter.py
index 71aa7aa83..37d0e3c3e 100644
--- a/ietf/wgcharter/views.py
+++ b/ietf/doc/views_charter.py
@@ -23,8 +23,8 @@ from ietf.name.models import *
 from ietf.person.models import *
 from ietf.group.models import *
 from ietf.group.utils import save_group_in_history, save_milestone_in_history
-from ietf.wgcharter.mails import *
-from ietf.wgcharter.utils import *
+from ietf.wginfo.mails import email_secretariat
+from ietf.doc.utils_charter import *
 
 import debug
 
@@ -124,7 +124,7 @@ def change_state(request, name, option=None):
                 charter.save()
 
                 if message:
-                    email_secretariat(request, group, "state-%s" % charter_state.slug, message)
+                    email_secretariat(request, group, "Charter state changed to %s" % charter_state.name, message)
 
                 email_state_changed(request, charter, "State changed to %s." % charter_state)
 
@@ -185,7 +185,7 @@ def change_state(request, name, option=None):
 
     states_for_ballot_wo_extern = State.objects.filter(used=True, type="charter", slug="intrev").values_list("pk", flat=True)
 
-    return render_to_response('wgcharter/change_state.html',
+    return render_to_response('doc/charter/change_state.html',
                               dict(form=form,
                                    doc=group.charter,
                                    login=login,
@@ -230,7 +230,7 @@ def telechat_date(request, name):
     else:
         form = TelechatForm(initial=initial)
 
-    return render_to_response('wgcharter/edit_telechat_date.html',
+    return render_to_response('doc/charter/edit_telechat_date.html',
                               dict(doc=doc,
                                    form=form,
                                    user=request.user,
@@ -274,7 +274,7 @@ def edit_notify(request, name):
     else:
         form = NotifyForm(initial=init)
 
-    return render_to_response('wgcharter/edit_notify.html',
+    return render_to_response('doc/charter/edit_notify.html',
                               dict(doc=doc,
                                    form=form,
                                    user=request.user,
@@ -322,7 +322,7 @@ def edit_ad(request, name):
         init = { "ad" : charter.ad_id }
         form = AdForm(initial=init)
 
-    return render_to_response('wgcharter/change_ad.html',
+    return render_to_response('doc/charter/change_ad.html',
                               {'form':   form,
                                'charter': charter,
                               },
@@ -415,7 +415,7 @@ def submit(request, name=None, acronym=None, option=None):
         except IOError:
             pass
         form = UploadForm(initial=init)
-    return render_to_response('wgcharter/submit.html',
+    return render_to_response('doc/charter/submit.html',
                               {'form': form,
                                'next_rev': next_rev,
                                'group': group },
@@ -481,7 +481,7 @@ def announcement_text(request, name, ann):
             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('wgcharter/announcement_text.html',
+    return render_to_response('doc/charter/announcement_text.html',
                               dict(charter=charter,
                                    announcement=ann,
                                    back_url=urlreverse("doc_writeup", kwargs=dict(name=charter.name)),
@@ -547,13 +547,13 @@ def ballot_writeupnotes(request, name):
                 e.desc = "Ballot has been sent"
                 e.save()
 
-                return render_to_response('wgcharter/ballot_issued.html',
+                return render_to_response('doc/charter/ballot_issued.html',
                                           dict(doc=charter,
                                                ),
                                           context_instance=RequestContext(request))
                         
 
-    return render_to_response('wgcharter/ballot_writeupnotes.html',
+    return render_to_response('doc/charter/ballot_writeupnotes.html',
                               dict(charter=charter,
                                    ballot_issued=bool(charter.latest_event(type="sent_ballot_announcement")),
                                    ballot_writeup_form=form,
@@ -632,7 +632,7 @@ def approve(request, name):
         charter.time = e.time
         charter.save()
 
-        email_secretariat(request, group, "state-%s" % new_charter_state.slug, change_description)
+        email_secretariat(request, group, "Charter state changed to %s" % new_charter_state.name, change_description)
 
         # move milestones over
         milestones_to_delete = list(group.groupmilestone_set.filter(state__in=("active", "review")))
@@ -689,7 +689,7 @@ def approve(request, name):
 
         return HttpResponseRedirect(charter.get_absolute_url())
     
-    return render_to_response('wgcharter/approve.html',
+    return render_to_response('doc/charter/approve.html',
                               dict(charter=charter,
                                    announcement=announcement),
                               context_instance=RequestContext(request))
@@ -720,7 +720,7 @@ def charter_with_milestones_txt(request, name, rev):
     for m in milestones:
         m.desc_filled = wrapper.fill(m.desc)
 
-    return render_to_response('wgcharter/charter_with_milestones.txt',
+    return render_to_response('doc/charter/charter_with_milestones.txt',
                               dict(charter_text=charter_text,
                                    milestones=milestones),
                               context_instance=RequestContext(request),
diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py
index 677a58e75..db75beb6f 100644
--- a/ietf/doc/views_doc.py
+++ b/ietf/doc/views_doc.py
@@ -50,7 +50,6 @@ from ietf.doc.utils import *
 from ietf.utils.history import find_history_active_at
 from ietf.ietfauth.utils import *
 from ietf.doc.views_status_change import RELATION_SLUGS as status_change_relationships
-from ietf.wgcharter.utils import historic_milestones_for_charter
 from ietf.ipr.models import IprDocAlias
 from ietf.doc.mails import email_ad
 
@@ -563,14 +562,14 @@ def document_writeup(request, name):
                          "",
                          [("WG Review Announcement",
                            text_from_writeup("changed_review_announcement"),
-                           urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="review")))]
+                           urlreverse("ietf.doc.views_charter.announcement_text", kwargs=dict(name=doc.name, ann="review")))]
                          ))
 
         sections.append(("WG Action Announcement",
                          "",
                          [("WG Action Announcement",
                            text_from_writeup("changed_action_announcement"),
-                           urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="action")))]
+                           urlreverse("ietf.doc.views_charter.announcement_text", kwargs=dict(name=doc.name, ann="action")))]
                          ))
 
         if doc.latest_event(BallotDocEvent, type="created_ballot"):
@@ -578,7 +577,7 @@ def document_writeup(request, name):
                              "",
                              [("Ballot Announcement",
                                text_from_writeup("changed_ballot_writeup_text"),
-                               urlreverse("ietf.wgcharter.views.ballot_writeupnotes", kwargs=dict(name=doc.name)))]
+                               urlreverse("ietf.doc.views_charter.ballot_writeupnotes", kwargs=dict(name=doc.name)))]
                              ))
 
     if not sections:
diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
index 3242e27ad..1d7d71bad 100644
--- a/ietf/iesg/views.py
+++ b/ietf/iesg/views.py
@@ -354,7 +354,7 @@ def agenda_documents(request):
     telechats = []
     for date in dates:
         sections = agenda_sections()
-        fill_in_agenda_docs(date, sections, docs_by_date[d])
+        fill_in_agenda_docs(date, sections, docs_by_date[date])
 
         telechats.append({
                 "date":date,
diff --git a/ietf/settings.py b/ietf/settings.py
index 4700c34b0..0c4e450ac 100644
--- a/ietf/settings.py
+++ b/ietf/settings.py
@@ -179,7 +179,6 @@ INSTALLED_APPS = (
     'ietf.redirects',
     'ietf.wginfo',
     'ietf.submit',
-    'ietf.wgcharter',
     'ietf.sync',
     'ietf.community',
     'ietf.release',
diff --git a/ietf/templates/wgcharter/action_text.txt b/ietf/templates/doc/charter/action_text.txt
similarity index 90%
rename from ietf/templates/wgcharter/action_text.txt
rename to ietf/templates/doc/charter/action_text.txt
index 599b8622a..af508f788 100644
--- a/ietf/templates/wgcharter/action_text.txt
+++ b/ietf/templates/doc/charter/action_text.txt
@@ -5,4 +5,4 @@ Subject: WG Action: {{ action_type }} {{ group.name }} ({{ group.acronym }})
 
 {% filter wordwrap:73 %}{% if action_type == "Formed" %}A new IETF working group has been formed in the {{ group.parent.name }}.{% endif %}{% if action_type == "Rechartered" %}The {{ group.name }} ({{ group.acronym }}) working group in the {{ group.parent.name }} of the IETF has been rechartered.{% endif %} For additional information please contact the Area Directors or the {{ group.type.name }} Chair{{ chairs|pluralize}}.
 
-{% include "wgcharter/group_info.txt" %}{% endfilter %}{% endautoescape %}
+{% include "doc/charter/group_info.txt" %}{% endfilter %}{% endautoescape %}
diff --git a/ietf/templates/wgcharter/announcement_text.html b/ietf/templates/doc/charter/announcement_text.html
similarity index 100%
rename from ietf/templates/wgcharter/announcement_text.html
rename to ietf/templates/doc/charter/announcement_text.html
diff --git a/ietf/templates/wgcharter/approve.html b/ietf/templates/doc/charter/approve.html
similarity index 100%
rename from ietf/templates/wgcharter/approve.html
rename to ietf/templates/doc/charter/approve.html
diff --git a/ietf/templates/wgcharter/ballot_issued.html b/ietf/templates/doc/charter/ballot_issued.html
similarity index 100%
rename from ietf/templates/wgcharter/ballot_issued.html
rename to ietf/templates/doc/charter/ballot_issued.html
diff --git a/ietf/templates/wgcharter/ballot_writeup.txt b/ietf/templates/doc/charter/ballot_writeup.txt
similarity index 100%
rename from ietf/templates/wgcharter/ballot_writeup.txt
rename to ietf/templates/doc/charter/ballot_writeup.txt
diff --git a/ietf/templates/wgcharter/ballot_writeupnotes.html b/ietf/templates/doc/charter/ballot_writeupnotes.html
similarity index 100%
rename from ietf/templates/wgcharter/ballot_writeupnotes.html
rename to ietf/templates/doc/charter/ballot_writeupnotes.html
diff --git a/ietf/templates/wgcharter/change_ad.html b/ietf/templates/doc/charter/change_ad.html
similarity index 100%
rename from ietf/templates/wgcharter/change_ad.html
rename to ietf/templates/doc/charter/change_ad.html
diff --git a/ietf/templates/wgcharter/change_state.html b/ietf/templates/doc/charter/change_state.html
similarity index 100%
rename from ietf/templates/wgcharter/change_state.html
rename to ietf/templates/doc/charter/change_state.html
diff --git a/ietf/templates/wgcharter/charter_with_milestones.txt b/ietf/templates/doc/charter/charter_with_milestones.txt
similarity index 100%
rename from ietf/templates/wgcharter/charter_with_milestones.txt
rename to ietf/templates/doc/charter/charter_with_milestones.txt
diff --git a/ietf/templates/wgcharter/edit_notify.html b/ietf/templates/doc/charter/edit_notify.html
similarity index 100%
rename from ietf/templates/wgcharter/edit_notify.html
rename to ietf/templates/doc/charter/edit_notify.html
diff --git a/ietf/templates/wgcharter/edit_telechat_date.html b/ietf/templates/doc/charter/edit_telechat_date.html
similarity index 100%
rename from ietf/templates/wgcharter/edit_telechat_date.html
rename to ietf/templates/doc/charter/edit_telechat_date.html
diff --git a/ietf/templates/wgcharter/group_info.txt b/ietf/templates/doc/charter/group_info.txt
similarity index 100%
rename from ietf/templates/wgcharter/group_info.txt
rename to ietf/templates/doc/charter/group_info.txt
diff --git a/ietf/templates/wgcharter/issue_ballot_mail.txt b/ietf/templates/doc/charter/issue_ballot_mail.txt
similarity index 100%
rename from ietf/templates/wgcharter/issue_ballot_mail.txt
rename to ietf/templates/doc/charter/issue_ballot_mail.txt
diff --git a/ietf/templates/wgcharter/review_text.txt b/ietf/templates/doc/charter/review_text.txt
similarity index 91%
rename from ietf/templates/wgcharter/review_text.txt
rename to ietf/templates/doc/charter/review_text.txt
index 6b4abde36..c982eb5e1 100644
--- a/ietf/templates/wgcharter/review_text.txt
+++ b/ietf/templates/doc/charter/review_text.txt
@@ -5,4 +5,4 @@ Subject: WG Review: {{ group.name }} ({{ group.acronym }})
 
 {% filter wordwrap:73 %}{% if review_type == "new" %}A new IETF working group has been proposed in the {{ group.parent.name }}.{% endif %}{% if review_type == "recharter" %}The {{ group.name }} ({{group.acronym}}) working group in the {{ group.parent.name }} of the IETF is undergoing rechartering.{% endif %} 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/group_info.txt" %}{% endfilter %}{% endautoescape %}
+{% include "doc/charter/group_info.txt" %}{% endfilter %}{% endautoescape %}
diff --git a/ietf/templates/wgcharter/submit.html b/ietf/templates/doc/charter/submit.html
similarity index 100%
rename from ietf/templates/wgcharter/submit.html
rename to ietf/templates/doc/charter/submit.html
diff --git a/ietf/templates/wgcharter/email_secretariat.txt b/ietf/templates/wginfo/email_secretariat.txt
similarity index 100%
rename from ietf/templates/wgcharter/email_secretariat.txt
rename to ietf/templates/wginfo/email_secretariat.txt
diff --git a/ietf/wgcharter/__init__.py b/ietf/wgcharter/__init__.py
deleted file mode 100644
index 792d60054..000000000
--- a/ietf/wgcharter/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/ietf/wgcharter/migrate.py b/ietf/wgcharter/migrate.py
deleted file mode 100644
index 5faffdb39..000000000
--- a/ietf/wgcharter/migrate.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import sys, os, re, datetime
-
-basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
-sys.path = [ basedir ] + sys.path
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ietf.settings")
-
-from ietf.doc.models import *
-
-# make sure ballot positions and types are right
-BallotPositionName.objects.get_or_create(slug="block",
-                                         order=3,
-                                         name="Block",
-                                         blocking=True,
-                                         )
-
-BallotPositionName.objects.filter(slug="discuss").update(blocking=True)
-
-charter_positions = BallotPositionName.objects.filter(slug__in=["yes", "noobj", "block", "abstain", "norecord" ])
-
-o,_ = BallotType.objects.get_or_create(doc_type_id="charter",
-                                 slug="r-extrev",
-                                 name="Ready for external review",
-                                 question="Is this charter ready for external review?",
-                                 order=1,
-                                 )
-
-o.positions = charter_positions
-
-o,_ = BallotType.objects.get_or_create(doc_type_id="charter",
-                                       slug="r-wo-ext",
-                                       name="Ready w/o external review",
-                                       question="Is this charter ready for external review? Is this charter ready for approval without external review?",
-                                       order=2,
-                                       )
-o.positions = charter_positions
-
-o,_ = BallotType.objects.get_or_create(doc_type_id="charter",
-                                       slug="approve",
-                                       name="Approve",
-                                       question="Do we approve of this charter?",
-                                       order=3,
-                                       )
-o.positions = charter_positions
-
-draft_ballot,_ = BallotType.objects.get_or_create(doc_type_id="draft",
-                                     slug="approve",
-                                     name="Approve",
-                                     question="",
-                                     order=1,
-                                     )
-draft_ballot.positions = BallotPositionName.objects.filter(slug__in=["yes", "noobj", "discuss", "abstain", "recuse", "norecord"])
-
-
-# add events for drafts
-
-# prevent memory from leaking when settings.DEBUG=True
-from django.db import connection
-class DontSaveQueries(object):
-    def append(self, x):
-        pass
-connection.queries = DontSaveQueries()
-
-relevant_docs = Document.objects.filter(type="draft", docevent__type__in=("changed_ballot_position", "sent_ballot_announcement")).distinct()
-for d in relevant_docs.iterator():
-    ballot = None
-    for e in d.docevent_set.order_by("time", "id").select_related("ballotpositiondocevent"):
-        if e.type == "created_ballot":
-            ballot = e
-
-        if e.type == "closed_ballot":
-            ballot = None
-
-        if not ballot and e.type in ("sent_ballot_announcement", "changed_ballot_position"):
-            ballot = BallotDocEvent(doc=e.doc, by=e.by)
-            ballot.type = "created_ballot"
-            ballot.ballot_type = draft_ballot
-            # place new event just before
-            ballot.time = e.time - datetime.timedelta(seconds=1)
-            ballot.desc = u'Created "%s" ballot' % draft_ballot
-            ballot.save()
-
-            if e.type == "sent_ballot_announcement":
-                print "added ballot for", d.name
-            else:
-                print "MISSING ballot issue event, added ballot for", d.name
-
-        if e.type == "changed_ballot_position" and not e.ballotpositiondocevent.ballot:
-            e.ballotpositiondocevent.ballot_id = ballot.id
-            e.ballotpositiondocevent.save()
-
-        if e.type in ("iesg_approved", "iesg_disapproved") and ballot:
-            c = BallotDocEvent(doc=e.doc, by=e.by)
-            c.type = "closed_ballot"
-            c.ballot_type = draft_ballot
-            # place new event just before
-            c.time = e.time - datetime.timedelta(seconds=1)
-            c.desc = u'Closed "%s" ballot' % draft_ballot.name
-            c.save()
-            ballot = None
-
-            print "closed ballot for", d.name
diff --git a/ietf/wgcharter/models.py b/ietf/wgcharter/models.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/ietf/wgcharter/urls.py b/ietf/wgcharter/urls.py
deleted file mode 100644
index 223a73730..000000000
--- a/ietf/wgcharter/urls.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright The IETF Trust 2011, All Rights Reserved
-
-from django.conf.urls import patterns, url
-
-urlpatterns = patterns('',
-    url(r'^state/$', "ietf.wgcharter.views.change_state", name='charter_change_state'),
-    url(r'^(?P<option>initcharter|recharter|abandon)/$', "ietf.wgcharter.views.change_state", name='charter_startstop_process'),
-    url(r'^telechat/$', "ietf.wgcharter.views.telechat_date", name='charter_telechat_date'),
-    url(r'^notify/$', "ietf.wgcharter.views.edit_notify", name='charter_edit_notify'),
-    url(r'^ad/$', "ietf.wgcharter.views.edit_ad", name='charter_edit_ad'),
-    url(r'^(?P<ann>action|review)/$', "ietf.wgcharter.views.announcement_text", name="charter_edit_announcement"),
-    url(r'^ballotwriteupnotes/$', "ietf.wgcharter.views.ballot_writeupnotes"),
-    url(r'^approve/$', "ietf.wgcharter.views.approve", name='charter_approve'),
-    url(r'^submit/$', "ietf.wgcharter.views.submit", name='charter_submit'),
-    url(r'^submit/(?P<option>initcharter|recharter)/$', "ietf.wgcharter.views.submit", name='charter_submit'), # shouldn't be here
-    url(r'^withmilestones-(?P<rev>[0-9-]+).txt$', "ietf.wgcharter.views.charter_with_milestones_txt", name='charter_with_milestones_txt'),
-)
diff --git a/ietf/wgcharter/utils.py b/ietf/wgcharter/utils.py
deleted file mode 100644
index 4a8af2a64..000000000
--- a/ietf/wgcharter/utils.py
+++ /dev/null
@@ -1,110 +0,0 @@
-import re, datetime, os
-
-from django.conf import settings
-
-from ietf.group.models import GroupEvent, ChangeStateGroupEvent
-from ietf.doc.models import Document, DocAlias, DocHistory, RelatedDocument, DocumentAuthor, DocEvent, NewRevisionDocEvent
-from ietf.utils.history import find_history_active_at
-
-def log_state_changed(request, doc, by, prev_state):
-    e = DocEvent(doc=doc, by=by)
-    e.type = "changed_document"
-    e.desc = u"State changed to <b>%s</b> from %s" % (
-        doc.get_state().name,
-        prev_state.name if prev_state else "None")
-    e.save()
-    return e
-
-def next_revision(rev):
-    if rev == "":
-        return "00-00"
-    m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
-    if m.group('minor'):
-        return "%s-%#02d" % (m.group('major'), int(m.group('minor')) + 1)
-    else:
-        return "%s-00" % (m.group('major'))
-
-def approved_revision(rev):
-    if rev == "":
-        return ""
-    m = re.match(r"(?P<major>[0-9][0-9])(-(?P<minor>[0-9][0-9]))?", rev)
-    return m.group('major')
-
-def next_approved_revision(rev):
-    if rev == "":
-        return "01"
-    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 historic_milestones_for_charter(charter, rev):
-    """Return GroupMilestone/GroupMilestoneHistory objects for charter
-    document at rev by looking through the history."""
-
-    chartering = "-" in rev
-    if chartering:
-        need_state = "charter"
-    else:
-        need_state = "active"
-
-    # slight complication - we can assign milestones to a revision up
-    # until the point where the next superseding revision is
-    # published, so that time shall be our limit
-    revision_event = charter.latest_event(NewRevisionDocEvent, type="new_revision", rev=rev)
-    if not revision_event:
-        return []
-
-    e = charter.docevent_set.filter(time__gt=revision_event.time, type="new_revision").order_by("time")
-    if not chartering:
-        e = e.exclude(newrevisiondocevent__rev__contains="-")
-
-    if e:
-        # subtract a margen of error to avoid collisions with
-        # milestones being published at the same time as the new
-        # revision (when approving a charter)
-        just_before_next_rev = e[0].time - datetime.timedelta(seconds=5)
-    else:
-        just_before_next_rev = datetime.datetime.now()
-
-    res = []
-    for m in charter.chartered_group.groupmilestone_set.all():
-        mh = find_history_active_at(m, just_before_next_rev)
-        if mh and mh.state_id == need_state:
-            res.append(mh)
-
-    return res
-    
-
-def update_telechat(request, doc, by, new_telechat_date):
-    # FIXME: reuse function in doc/utils.py instead of this one
-    # (need to fix auto-setting returning item problem first though)
-    from ietf.doc.models import TelechatDocEvent
-    
-    on_agenda = bool(new_telechat_date)
-
-    prev = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
-    prev_telechat = prev.telechat_date if prev else None
-    prev_agenda = bool(prev_telechat)
-    
-    e = TelechatDocEvent()
-    e.type = "scheduled_for_telechat"
-    e.by = by
-    e.doc = doc
-    e.telechat_date = new_telechat_date
-    
-    if on_agenda != prev_agenda:
-        if on_agenda:
-            e.desc = "Placed on agenda for telechat - %s" % new_telechat_date
-        else:
-            e.desc = "Removed from agenda for telechat"
-        e.save()
-    elif on_agenda and new_telechat_date != prev_telechat:
-        e.desc = "Telechat date has been changed to <b>%s</b> from <b>%s</b>" % (new_telechat_date, prev_telechat)
-        e.save()
diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py
index 8bdddd812..0cde47c4a 100644
--- a/ietf/wginfo/edit.py
+++ b/ietf/wginfo/edit.py
@@ -17,7 +17,7 @@ from ietf.name.models import *
 from ietf.person.models import *
 from ietf.group.models import *
 from ietf.group.utils import save_group_in_history
-from ietf.wgcharter.mails import email_secretariat
+from ietf.wginfo.mails import email_secretariat
 from ietf.person.forms import EmailsField
 from ietf.doc.utils import get_tags_for_stream_id
 
@@ -319,7 +319,7 @@ def conclude(request, acronym):
         if form.is_valid():
             instructions = form.cleaned_data['instructions']
 
-            email_secretariat(request, wg, "conclude", instructions)
+            email_secretariat(request, wg, "Request closing of group", instructions)
 
             e = GroupEvent(group=wg, by=login)
             e.type = "requested_close"
diff --git a/ietf/wginfo/mails.py b/ietf/wginfo/mails.py
index 02606d30e..e6df3f103 100644
--- a/ietf/wginfo/mails.py
+++ b/ietf/wginfo/mails.py
@@ -12,6 +12,20 @@ from ietf.utils.mail import send_mail, send_mail_text
 
 from ietf.group.models import *
 
+def email_secretariat(request, group, subject, text):
+    to = ["iesg-secretary@ietf.org"]
+    full_subject = u"Regarding %s %s: %s" % (group.type.name, group.acronym, subject)
+    text = strip_tags(text)
+
+    send_mail(request, to, None, subject,
+              "wginfo/email_secretariat.txt",
+              dict(text=text,
+                   group=group,
+                   group_url=settings.IDTRACKER_BASE_URL + urlreverse('group_charter', kwargs=dict(acronym=group.acronym)),
+                   charter_url=settings.IDTRACKER_BASE_URL + urlreverse('doc_view', kwargs=dict(name=group.charter.name)),
+                   )
+              )
+
 def email_milestones_changed(request, group, changes):
     def wrap_up_email(to, text):
         text = wrap(strip_tags(text), 70)