diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py
index 00e4b265a..cb42553a8 100644
--- a/ietf/doc/proxy.py
+++ b/ietf/doc/proxy.py
@@ -175,32 +175,6 @@ class InternetDraft(Document):
     def expired_tombstone(self):
         return False
 
-    def calc_process_start_end(self):
-        import datetime
-        start, end = datetime.datetime.min, datetime.datetime.max
-        e = self.latest_event(type="started_iesg_process")
-        if e:
-            start = e.time
-            if self.get_state_slug() == "rfc" and self.name.startswith("draft") and not hasattr(self, "viewing_as_rfc"):
-                previous_process = self.latest_event(type="started_iesg_process", time__lt=e.time)
-                if previous_process:
-                    start = previous_process.time
-                    end = e.time
-        self._process_start = start
-        self._process_end = end
-
-    @property
-    def process_start(self):
-        if not hasattr(self, "_process_start"):
-            self.calc_process_start_end()
-        return self._process_start
-
-    @property
-    def process_end(self):
-        if not hasattr(self, "_process_end"):
-            self.calc_process_start_end()
-        return self._process_end
-
     #shepherd = BrokenForeignKey('PersonOrOrgInfo', null=True, blank=True, null_values=(0, )) # same name
 
     #idinternal = FKAsOneToOne('idinternal', reverse=True, query=models.Q(rfc_flag = 0))
diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py
index 2e1dfe4ac..e1bfd882f 100644
--- a/ietf/doc/utils.py
+++ b/ietf/doc/utils.py
@@ -35,14 +35,10 @@ def active_ballot_positions(doc, ballot=None):
     active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active"))
     res = {}
 
-    # FIXME: do something with ballot
+    if not ballot:
+        ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
 
-    start = datetime.datetime.min
-    e = doc.latest_event(type="started_iesg_process")
-    if e:
-        start = e.time
-
-    positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ad__in=active_ads, time__gte=start).select_related('ad').order_by("-time", "-id")
+    positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ad__in=active_ads, ballot=ballot).select_related('ad', 'pos').order_by("-time", "-id")
 
     for pos in positions:
         if pos.ad not in res:
@@ -75,7 +71,7 @@ def needed_ballot_positions(doc, active_positions):
     if doc.type_id == "draft" and doc.intended_std_level_id in ("bcp", "ps", "ds", "std"):
         # For standards-track, need positions from 2/3 of the
         # non-recused current IESG.
-        needed = len(active_positions) - recuse * 2 / 3
+        needed = len(active_positions) - len(recuse) * 2 / 3
 
     have = len(yes) + len(noobj) + len(blocking)
     if have < needed:
@@ -92,21 +88,6 @@ def needed_ballot_positions(doc, active_positions):
 
     return " ".join(answer)
     
-
-def get_rfc_number(doc):
-    qs = doc.docalias_set.filter(name__startswith='rfc')
-    return qs[0].name[3:] if qs else None
-
-def get_chartering_type(doc):
-    chartering = ""
-    if doc.get_state_slug() not in ("notrev", "approved"):
-        if doc.group.state_id == "proposed":
-            chartering = "initial"
-        elif doc.group.state_id == "active":
-            chartering = "rechartering"
-
-    return chartering
-
 def ballot_open(doc, ballot_type_slug):
     e = doc.latest_event(BallotDocEvent, ballot_type__slug=ballot_type_slug)
     return e and not e.type == "closed_ballot"
@@ -126,6 +107,20 @@ def close_open_ballots(doc, by):
             e.desc = 'Closed "%s" ballot' % t.name
             e.save()
 
+def get_rfc_number(doc):
+    qs = doc.docalias_set.filter(name__startswith='rfc')
+    return qs[0].name[3:] if qs else None
+
+def get_chartering_type(doc):
+    chartering = ""
+    if doc.get_state_slug() not in ("notrev", "approved"):
+        if doc.group.state_id == "proposed":
+            chartering = "initial"
+        elif doc.group.state_id == "active":
+            chartering = "rechartering"
+
+    return chartering
+
 def augment_with_telechat_date(docs):
     """Add a telechat_date attribute to each document with the
     scheduled telechat or None if it's not scheduled."""
diff --git a/ietf/idrfc/idrfc_wrapper.py b/ietf/idrfc/idrfc_wrapper.py
index 53a18a571..290e54084 100644
--- a/ietf/idrfc/idrfc_wrapper.py
+++ b/ietf/idrfc/idrfc_wrapper.py
@@ -733,7 +733,9 @@ class BallotWrapper:
 
         new_revisions = list(NewRevisionDocEvent.objects.filter(doc=self.ballot, type="new_revision").order_by('-time', '-id'))
 
-	for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", time__gte=self.ballot.process_start, time__lte=self.ballot.process_end).select_related('ad').order_by("-time", '-id'):
+        ballot = self.ballot.latest_event(BallotDocEvent, type="created_ballot")
+
+	for pos in BallotPositionDocEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", ballot=ballot).select_related('ad').order_by("-time", '-id'):
             if pos.ad not in seen:
                 p = dict(ad_name=pos.ad.plain_name(),
                          ad_username=pos.ad.pk, # ought to rename this in doc_ballot_list
diff --git a/ietf/idrfc/mails.py b/ietf/idrfc/mails.py
index 3bac6bafc..8ae3ec17a 100644
--- a/ietf/idrfc/mails.py
+++ b/ietf/idrfc/mails.py
@@ -532,14 +532,13 @@ def generate_issue_ballot_mail(request, doc):
                                  )
                             )
 
-def generate_issue_ballot_mailREDESIGN(request, doc):
+def generate_issue_ballot_mailREDESIGN(request, doc, ballot):
     full_status = full_intended_status(doc.intended_std_level.name)
     status = full_status.replace("a ", "").replace("an ", "")
 
     active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct()
     
-    e = doc.latest_event(type="started_iesg_process")
-    positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad')
+    positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad')
 
     # format positions and setup discusses and comments
     ad_feedback = []
diff --git a/ietf/idrfc/templatetags/ballot_icon.py b/ietf/idrfc/templatetags/ballot_icon.py
index 12f6891a6..6182d5529 100644
--- a/ietf/idrfc/templatetags/ballot_icon.py
+++ b/ietf/idrfc/templatetags/ballot_icon.py
@@ -36,17 +36,12 @@ from django.conf import settings
 from ietf.idtracker.models import IDInternal, BallotInfo
 from ietf.idrfc.idrfc_wrapper import position_to_string, BALLOT_ACTIVE_STATES
 from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days
+from ietf.ietfauth.decorators import has_role
+from ietf.doc.utils import active_ballot_positions
+from ietf.doc.models import BallotDocEvent
 
 register = template.Library()
 
-def get_user_adid(context):
-    if 'user' in context and in_group(context['user'], "Area_Director"):
-        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
-            return context['user'].get_profile().id
-        return context['user'].get_profile().iesg_login_id()
-    else:
-        return None
-
 def get_user_name(context):
     if 'user' in context and context['user'].is_authenticated():
         if settings.USE_DB_REDESIGN_PROXY_CLASSES:
@@ -60,105 +55,64 @@ def get_user_name(context):
         if person:
             return str(person)
     return None
-    
-def render_ballot_icon(context, doc):
-    if isinstance(doc,IDInternal):
-        try:
-            ballot = doc.ballot
-            if not ballot.ballot_issued:
-                return ""
-        except BallotInfo.DoesNotExist:
-            return ""
-        if str(doc.cur_state) not in BALLOT_ACTIVE_STATES:
-            return ""
-        if doc.rfc_flag and not settings.USE_DB_REDESIGN_PROXY_CLASSES:
-            name = doc.document().filename()
-        else:
-            name = doc.document().filename
-    else:
-        if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot():
-            ballot = doc._idinternal.ballot
-        else:
-            return ""
-        if doc.is_rfc_wrapper:
-            name = "rfc"+str(doc.rfc_number)
-        else:
-            name = doc.draft_name
-    adId = get_user_adid(context)
-    red = 0
-    green = 0
-    yellow = 0
-    gray = 0
-    blank = 0
-    my = None
-    for p in ballot.active_positions():
-        if not p['pos']:
-            blank = blank + 1
-        elif (p['pos'].yes > 0) or (p['pos'].noobj > 0):
-            green = green + 1
-        elif (p['pos'].discuss > 0):
-            red = red + 1
-        elif (p['pos'].abstain > 0):
-            yellow = yellow + 1
-        elif (p['pos'].recuse > 0):
-            gray = gray + 1
-        else:
-            blank = blank + 1
-        if adId and (p['ad'].id == adId):
-            my = position_to_string(p['pos'])
-    return render_ballot_icon2(name, red,yellow,green,gray,blank, my, adId)+"<!-- adId="+str(adId)+" my="+str(my)+"-->"
 
-def render_ballot_icon2(draft_name, red,yellow,green,gray,blank, my,adId):
-    from ietf.doc.models import BallotDocEvent
-    ballots = BallotDocEvent.objects.filter(doc__docalias__name=draft_name).order_by("-time", "-id")
-    if ballots:
-        edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=draft_name, ballot_id=ballots[0].pk))
-    else:
-        edit_position_url = ""
-    if adId:
-        res_cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
-    else:
-        res_cm = ''
-    res = '<table class="ballot_icon" title="IESG Evaluation Record (click to show more, right-click to edit position)" onclick="showBallot(\'' + draft_name + '\',\'' + str(edit_position_url) + '\')"'+res_cm+'>'
-    for y in range(3):
-        res = res + "<tr>"
-        for x in range(5):
-            myMark = False
-            if red > 0:
-                c = "ballot_icon_red"
-                red = red - 1
-                myMark = (my == "Discuss")
-            elif yellow > 0:
-                c = "ballot_icon_yellow"
-                yellow = yellow - 1
-                myMark = (my == "Abstain")
-            elif green > 0:
-                c = "ballot_icon_green"
-                green = green - 1
-                myMark = (my == "Yes") or (my == "No Objection")
-            elif gray > 0:
-                c = "ballot_icon_gray"
-                gray = gray - 1
-                myMark = (my == "Recuse")
-            else:
-                c = ""
-                myMark = (y == 2) and (x == 4) and (my == "No Record")
-            if myMark:
-                res = res + '<td class="'+c+' ballot_icon_my" />' 
-                my = None
-            else:
-                res = res + '<td class="'+c+'" />'
-        res = res + '</tr>'
-    res = res + '</table>'
-    return res
+def render_ballot_icon(user, doc):
+    if not doc:
+        return ""
+
+    s = doc.get_state("draft-iesg")
+    if s and s.name not in BALLOT_ACTIVE_STATES:
+        return ""
+
+    ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
+    if not ballot:
+        return ""
+
+    edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=doc.name, ballot_id=ballot.pk))
+
+    def sort_key(t):
+        _, pos = t
+        if not pos:
+            return (2, 0)
+        elif pos.pos.blocking:
+            return (0, pos.pos.order)
+        else:
+            return (1, pos.pos.order)
+
+    positions = list(active_ballot_positions(doc, ballot).items())
+    positions.sort(key=sort_key)
+
+    cm = ""
+    if has_role(user, "Area Director"):
+        cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
+
+    res = ['<table class="ballot_icon" title="IESG Evaluation Record (click to show more, right-click to edit position)" onclick="showBallot(\'' + doc.name + '\',\'' + str(edit_position_url) + '\')"' + cm + '>']
+
+    res.append("<tr>")
+
+    for i, (ad, pos) in enumerate(positions):
+        if i > 0 and i % 5 == 0:
+            res.append("</tr>")
+            res.append("<tr>")
+
+        c = "position-%s" % (pos.pos.slug if pos else "norecord")
+
+        if ad == user.get_profile():
+            c += " my"
+
+        res.append('<td class="%s" />' % c)
+
+    res.append("</tr>")
+    res.append("</table>")
+
+    return "".join(res)
 
-       
 class BallotIconNode(template.Node):
     def __init__(self, doc_var):
         self.doc_var = doc_var
     def render(self, context):
         doc = template.resolve_variable(self.doc_var, context)
-        return render_ballot_icon(context, doc)
+        return render_ballot_icon(context.get("user"), doc._idinternal)
 
 def do_ballot_icon(parser, token):
     try:
diff --git a/ietf/idrfc/views_ballot.py b/ietf/idrfc/views_ballot.py
index 5f3f1067e..b7b2906e1 100644
--- a/ietf/idrfc/views_ballot.py
+++ b/ietf/idrfc/views_ballot.py
@@ -26,6 +26,7 @@ from ietf.idrfc.utils import *
 from ietf.idrfc.lastcall import request_last_call
 from ietf.idrfc.idrfc_wrapper import BallotWrapper
 
+from ietf.doc.utils import *
 from ietf.doc.models import *
 from ietf.name.models import BallotPositionName
 from ietf.person.models import Person
@@ -343,6 +344,7 @@ def edit_positionREDESIGN(request, name, ballot_id):
         form = EditPositionForm(initial=initial, ballot_type=ballot.ballot_type)
 
     blocking_positions = dict((p.pk, p.name) for p in form.fields["position"].queryset.all() if p.blocking)
+    print blocking_positions, form.fields["position"].queryset.all()
 
     ballot_deferred = None
     if doc.get_state_slug("%s-iesg" % doc.type_id) == "defer":
@@ -933,9 +935,6 @@ class BallotWriteupFormREDESIGN(forms.Form):
 def ballot_writeupnotesREDESIGN(request, name):
     """Editing of ballot write-up and notes"""
     doc = get_object_or_404(Document, docalias__name=name)
-    started_process = doc.latest_event(type="started_iesg_process")
-    if not started_process:
-        raise Http404()
 
     login = request.user.get_profile()
 
@@ -958,9 +957,13 @@ def ballot_writeupnotesREDESIGN(request, name):
                 e.save()
 
             if "issue_ballot" in request.POST:
-                if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, time__gte=started_process.time):
+                create_ballot_if_not_open(doc, login, "approve")
+                ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
+
+                if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, ad=login, ballot=ballot):
                     # sending the ballot counts as a yes
                     pos = BallotPositionDocEvent(doc=doc, by=login)
+                    pos.ballot = ballot
                     pos.type = "changed_ballot_position"
                     pos.ad = login
                     pos.pos_id = "yes"
@@ -971,7 +974,7 @@ def ballot_writeupnotesREDESIGN(request, name):
                 if not approval:
                     approval = generate_approval_mail(request, doc)
 
-                msg = generate_issue_ballot_mail(request, doc)
+                msg = generate_issue_ballot_mail(request, doc, ballot)
                 send_mail_preformatted(request, msg)
 
                 email_iana(request, doc, 'drafts-eval@icann.org', msg)
diff --git a/ietf/idrfc/views_doc.py b/ietf/idrfc/views_doc.py
index 68caeaf10..762ec3c62 100644
--- a/ietf/idrfc/views_doc.py
+++ b/ietf/idrfc/views_doc.py
@@ -271,10 +271,10 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
             if latest.old_positions:
                 prev = latest.old_positions[-1]
             else:
-                prev = latest
+                prev = latest.pos.name
 
-            if e.pos != prev.pos:
-                latest.old_positions.append(e)
+            if e.pos.name != prev:
+                latest.old_positions.append(e.pos.name)
 
     # add any missing ADs through fake No Record events
     norecord = BallotPositionName.objects.get(slug="norecord")
@@ -316,9 +316,6 @@ def document_ballot_content(request, doc, ballot_id, editable=True):
                               context_instance=RequestContext(request))
 
 def document_ballot(request, name, ballot_id=None):
-    if name.lower().startswith("draft") or name.lower().startswith("rfc"):
-        return document_main_idrfc(request, name, "ballot")
-
     doc = get_object_or_404(Document, docalias__name=name)
     top = render_document_top(request, doc, "ballot")
 
diff --git a/ietf/wgcharter/migrate.py b/ietf/wgcharter/migrate.py
new file mode 100644
index 000000000..fca8910fe
--- /dev/null
+++ b/ietf/wgcharter/migrate.py
@@ -0,0 +1,102 @@
+import sys, os, re, datetime
+
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
+sys.path = [ basedir ] + sys.path
+
+from ietf import settings
+from django.core import management
+management.setup_environ(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[:1].iterator():
+    # print ""
+    # print d.name
+    # for e in d.docevent_set.order_by("time", "id").select_related("ballotpositiondocevent"):
+    #     print e.time, e.type, "BINGO" if e.type == "sent_ballot_announcement" else ""
+
+
+    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' % ballot.ballot_type.name
+            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 = ballot
+            e.ballotpositiondocevent.save()
+
+
+        # FIXME: close ballot
diff --git a/static/css/base2.css b/static/css/base2.css
index 473069dc0..669c5955d 100644
--- a/static/css/base2.css
+++ b/static/css/base2.css
@@ -122,11 +122,7 @@ table.ietf-table { border-collapse:collapse; border:1px solid #7f7f7f; }
 
 table.ballot_icon { empty-cells: show; padding: 0; border-spacing: 0; border: 1px solid black; border-collapse: collapse; table-layout:fixed; min-width:35px; background:white; }
 table.ballot_icon td { border: 1px solid black; height: 7px; width: 6px; padding: 0;}
-td.ballot_icon_green { background:#80ff80; }
-td.ballot_icon_red { background: #c00000; color: yellow; }
-td.ballot_icon_gray { background: #c0c0c0; }
-td.ballot_icon_yellow { background: #ffff00; }
-table.ballot_icon td.ballot_icon_my { border: 3px outset black;}
+table.ballot_icon td.my { border: 3px outset black;}
 
 .ietf-small { font-size:85%; }
 .ietf-highlight-y { padding:0 2px;background:yellow;}