diff --git a/ietf/doc/admin.py b/ietf/doc/admin.py
index 88c341a0e..31d6488a1 100644
--- a/ietf/doc/admin.py
+++ b/ietf/doc/admin.py
@@ -1,3 +1,4 @@
+from django.template.defaultfilters import slugify
from django.utils.safestring import mark_safe
from django.contrib import admin
from django import forms
@@ -43,7 +44,7 @@ class StatesWidget(forms.SelectMultiple):
html = []
first = True
for label, states in categorized_choices:
- htmlid = "id_%s_%s" % (name, label)
+ htmlid = "id_%s_%s" % (name, slugify(label))
html.append('
' % ("1em" if first else "0.5em"))
html.append(u'
%s: ' % (htmlid, label))
@@ -108,11 +109,14 @@ class DocHistoryAdmin(admin.ModelAdmin):
admin.site.register(DocHistory, DocHistoryAdmin)
class DocAliasAdmin(admin.ModelAdmin):
- list_display = [ 'name', 'document_link', ]
- search_fields = [ 'name', 'document__name', ]
+ list_display = ['name', 'document_link']
+ search_fields = ['name', 'document__name']
raw_id_fields = ['document']
admin.site.register(DocAlias, DocAliasAdmin)
+class BallotTypeAdmin(admin.ModelAdmin):
+ list_display = ["slug", "doc_type", "name", "question"]
+admin.site.register(BallotType, BallotTypeAdmin)
# events
@@ -126,12 +130,13 @@ class DocEventAdmin(admin.ModelAdmin):
admin.site.register(DocEvent, DocEventAdmin)
admin.site.register(NewRevisionDocEvent, DocEventAdmin)
+admin.site.register(BallotDocEvent, DocEventAdmin)
admin.site.register(WriteupDocEvent, DocEventAdmin)
admin.site.register(LastCallDocEvent, DocEventAdmin)
admin.site.register(TelechatDocEvent, DocEventAdmin)
class BallotPositionDocEventAdmin(DocEventAdmin):
- raw_id_fields = ["doc", "by", "ad"]
+ raw_id_fields = ["doc", "by", "ad", "ballot"]
admin.site.register(BallotPositionDocEvent, BallotPositionDocEventAdmin)
diff --git a/ietf/doc/migrations/0001_initial.py b/ietf/doc/migrations/0001_initial.py
new file mode 100644
index 000000000..19cfec54b
--- /dev/null
+++ b/ietf/doc/migrations/0001_initial.py
@@ -0,0 +1,648 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'StateType'
+ db.create_table('doc_statetype', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=30, primary_key=True)),
+ ('label', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('doc', ['StateType'])
+
+ # Adding model 'State'
+ db.create_table('doc_state', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.StateType'])),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('doc', ['State'])
+
+ # Adding M2M table for field next_states on 'State'
+ db.create_table('doc_state_next_states', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('from_state', models.ForeignKey(orm['doc.state'], null=False)),
+ ('to_state', models.ForeignKey(orm['doc.state'], null=False))
+ ))
+ db.create_unique('doc_state_next_states', ['from_state_id', 'to_state_id'])
+
+ # Adding model 'RelatedDocument'
+ db.create_table('doc_relateddocument', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('source', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.Document'])),
+ ('target', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.DocAlias'])),
+ ('relationship', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.DocRelationshipName'])),
+ ))
+ db.send_create_signal('doc', ['RelatedDocument'])
+
+ # Adding model 'DocumentAuthor'
+ db.create_table('doc_documentauthor', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('document', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.Document'])),
+ ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Email'])),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=1)),
+ ))
+ db.send_create_signal('doc', ['DocumentAuthor'])
+
+ # Adding model 'Document'
+ db.create_table('doc_document', (
+ ('time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.DocTypeName'], null=True, blank=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('stream', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.StreamName'], null=True, blank=True)),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['group.Group'], null=True, blank=True)),
+ ('abstract', self.gf('django.db.models.fields.TextField')()),
+ ('rev', self.gf('django.db.models.fields.CharField')(max_length=16, blank=True)),
+ ('pages', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=1)),
+ ('intended_std_level', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.IntendedStdLevelName'], null=True, blank=True)),
+ ('std_level', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.StdLevelName'], null=True, blank=True)),
+ ('ad', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='ad_document_set', null=True, to=orm['person.Person'])),
+ ('shepherd', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='shepherd_document_set', null=True, to=orm['person.Person'])),
+ ('expires', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('notify', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('external_url', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)),
+ ('note', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('internal_comments', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255, primary_key=True)),
+ ))
+ db.send_create_signal('doc', ['Document'])
+
+ # Adding M2M table for field states on 'Document'
+ db.create_table('doc_document_states', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('document', models.ForeignKey(orm['doc.document'], null=False)),
+ ('state', models.ForeignKey(orm['doc.state'], null=False))
+ ))
+ db.create_unique('doc_document_states', ['document_id', 'state_id'])
+
+ # Adding M2M table for field tags on 'Document'
+ db.create_table('doc_document_tags', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('document', models.ForeignKey(orm['doc.document'], null=False)),
+ ('doctagname', models.ForeignKey(orm['name.doctagname'], null=False))
+ ))
+ db.create_unique('doc_document_tags', ['document_id', 'doctagname_id'])
+
+ # Adding model 'RelatedDocHistory'
+ db.create_table('doc_relateddochistory', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('source', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.DocHistory'])),
+ ('target', self.gf('django.db.models.fields.related.ForeignKey')(related_name='reversely_related_document_history_set', to=orm['doc.DocAlias'])),
+ ('relationship', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.DocRelationshipName'])),
+ ))
+ db.send_create_signal('doc', ['RelatedDocHistory'])
+
+ # Adding model 'DocHistoryAuthor'
+ db.create_table('doc_dochistoryauthor', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('document', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.DocHistory'])),
+ ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Email'])),
+ ('order', self.gf('django.db.models.fields.IntegerField')()),
+ ))
+ db.send_create_signal('doc', ['DocHistoryAuthor'])
+
+ # Adding model 'DocHistory'
+ db.create_table('doc_dochistory', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.DocTypeName'], null=True, blank=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('stream', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.StreamName'], null=True, blank=True)),
+ ('group', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['group.Group'], null=True, blank=True)),
+ ('abstract', self.gf('django.db.models.fields.TextField')()),
+ ('rev', self.gf('django.db.models.fields.CharField')(max_length=16, blank=True)),
+ ('pages', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=1)),
+ ('intended_std_level', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.IntendedStdLevelName'], null=True, blank=True)),
+ ('std_level', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.StdLevelName'], null=True, blank=True)),
+ ('ad', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='ad_dochistory_set', null=True, to=orm['person.Person'])),
+ ('shepherd', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='shepherd_dochistory_set', null=True, to=orm['person.Person'])),
+ ('expires', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('notify', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('external_url', self.gf('django.db.models.fields.URLField')(max_length=200, blank=True)),
+ ('note', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('internal_comments', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('doc', self.gf('django.db.models.fields.related.ForeignKey')(related_name='history_set', to=orm['doc.Document'])),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('doc', ['DocHistory'])
+
+ # Adding M2M table for field states on 'DocHistory'
+ db.create_table('doc_dochistory_states', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('dochistory', models.ForeignKey(orm['doc.dochistory'], null=False)),
+ ('state', models.ForeignKey(orm['doc.state'], null=False))
+ ))
+ db.create_unique('doc_dochistory_states', ['dochistory_id', 'state_id'])
+
+ # Adding M2M table for field tags on 'DocHistory'
+ db.create_table('doc_dochistory_tags', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('dochistory', models.ForeignKey(orm['doc.dochistory'], null=False)),
+ ('doctagname', models.ForeignKey(orm['name.doctagname'], null=False))
+ ))
+ db.create_unique('doc_dochistory_tags', ['dochistory_id', 'doctagname_id'])
+
+ # Adding model 'DocAlias'
+ db.create_table('doc_docalias', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('document', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.Document'])),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
+ ))
+ db.send_create_signal('doc', ['DocAlias'])
+
+ # Adding model 'DocReminder'
+ db.create_table('doc_docreminder', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('event', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.DocEvent'])),
+ ('type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.DocReminderTypeName'])),
+ ('due', self.gf('django.db.models.fields.DateTimeField')()),
+ ('active', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ))
+ db.send_create_signal('doc', ['DocReminder'])
+
+ # Adding model 'DocEvent'
+ db.create_table('doc_docevent', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('type', self.gf('django.db.models.fields.CharField')(max_length=50)),
+ ('by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'])),
+ ('doc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.Document'])),
+ ('desc', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal('doc', ['DocEvent'])
+
+ # Adding model 'NewRevisionDocEvent'
+ db.create_table('doc_newrevisiondocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('rev', self.gf('django.db.models.fields.CharField')(max_length=16)),
+ ))
+ db.send_create_signal('doc', ['NewRevisionDocEvent'])
+
+ # Adding model 'BallotPositionDocEvent'
+ db.create_table('doc_ballotpositiondocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('ad', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'])),
+ ('pos', self.gf('django.db.models.fields.related.ForeignKey')(default='norecord', to=orm['name.BallotPositionName'])),
+ ('discuss', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('discuss_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('comment_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('doc', ['BallotPositionDocEvent'])
+
+ # Adding model 'WriteupDocEvent'
+ db.create_table('doc_writeupdocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('text', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ))
+ db.send_create_signal('doc', ['WriteupDocEvent'])
+
+ # Adding model 'LastCallDocEvent'
+ db.create_table('doc_lastcalldocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('expires', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('doc', ['LastCallDocEvent'])
+
+ # Adding model 'TelechatDocEvent'
+ db.create_table('doc_telechatdocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('telechat_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)),
+ ('returning_item', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ))
+ db.send_create_signal('doc', ['TelechatDocEvent'])
+
+ # Adding model 'GroupBallotPositionDocEvent'
+ db.create_table('doc_groupballotpositiondocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('ad', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'])),
+ ('pos', self.gf('django.db.models.fields.related.ForeignKey')(default='norecord', to=orm['name.GroupBallotPositionName'])),
+ ('block_comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('block_comment_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('comment_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('doc', ['GroupBallotPositionDocEvent'])
+
+ # Adding model 'InitialReviewDocEvent'
+ db.create_table('doc_initialreviewdocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('expires', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('doc', ['InitialReviewDocEvent'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'StateType'
+ db.delete_table('doc_statetype')
+
+ # Deleting model 'State'
+ db.delete_table('doc_state')
+
+ # Removing M2M table for field next_states on 'State'
+ db.delete_table('doc_state_next_states')
+
+ # Deleting model 'RelatedDocument'
+ db.delete_table('doc_relateddocument')
+
+ # Deleting model 'DocumentAuthor'
+ db.delete_table('doc_documentauthor')
+
+ # Deleting model 'Document'
+ db.delete_table('doc_document')
+
+ # Removing M2M table for field states on 'Document'
+ db.delete_table('doc_document_states')
+
+ # Removing M2M table for field tags on 'Document'
+ db.delete_table('doc_document_tags')
+
+ # Deleting model 'RelatedDocHistory'
+ db.delete_table('doc_relateddochistory')
+
+ # Deleting model 'DocHistoryAuthor'
+ db.delete_table('doc_dochistoryauthor')
+
+ # Deleting model 'DocHistory'
+ db.delete_table('doc_dochistory')
+
+ # Removing M2M table for field states on 'DocHistory'
+ db.delete_table('doc_dochistory_states')
+
+ # Removing M2M table for field tags on 'DocHistory'
+ db.delete_table('doc_dochistory_tags')
+
+ # Deleting model 'DocAlias'
+ db.delete_table('doc_docalias')
+
+ # Deleting model 'DocReminder'
+ db.delete_table('doc_docreminder')
+
+ # Deleting model 'DocEvent'
+ db.delete_table('doc_docevent')
+
+ # Deleting model 'NewRevisionDocEvent'
+ db.delete_table('doc_newrevisiondocevent')
+
+ # Deleting model 'BallotPositionDocEvent'
+ db.delete_table('doc_ballotpositiondocevent')
+
+ # Deleting model 'WriteupDocEvent'
+ db.delete_table('doc_writeupdocevent')
+
+ # Deleting model 'LastCallDocEvent'
+ db.delete_table('doc_lastcalldocevent')
+
+ # Deleting model 'TelechatDocEvent'
+ db.delete_table('doc_telechatdocevent')
+
+ # Deleting model 'GroupBallotPositionDocEvent'
+ db.delete_table('doc_groupballotpositiondocevent')
+
+ # Deleting model 'InitialReviewDocEvent'
+ db.delete_table('doc_initialreviewdocevent')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'doc.ballotpositiondocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
+ },
+ 'doc.docalias': {
+ 'Meta': {'object_name': 'DocAlias'},
+ 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'doc.docevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
+ 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
+ 'desc': ('django.db.models.fields.TextField', [], {}),
+ 'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'doc.dochistory': {
+ 'Meta': {'object_name': 'DocHistory'},
+ 'abstract': ('django.db.models.fields.TextField', [], {}),
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
+ 'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}),
+ 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
+ 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
+ },
+ 'doc.dochistoryauthor': {
+ 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
+ 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'doc.docreminder': {
+ 'Meta': {'object_name': 'DocReminder'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'due': ('django.db.models.fields.DateTimeField', [], {}),
+ 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
+ },
+ 'doc.document': {
+ 'Meta': {'object_name': 'Document'},
+ 'abstract': ('django.db.models.fields.TextField', [], {}),
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
+ 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+ 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'reversely_related_document_set'", 'blank': 'True', 'through': "orm['doc.RelatedDocument']", 'to': "orm['doc.DocAlias']"}),
+ 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
+ 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
+ },
+ 'doc.documentauthor': {
+ 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
+ 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
+ },
+ 'doc.groupballotpositiondocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'GroupBallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
+ 'block_comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'block_comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.GroupBallotPositionName']"})
+ },
+ 'doc.initialreviewdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'doc.lastcalldocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'doc.newrevisiondocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
+ },
+ 'doc.relateddochistory': {
+ 'Meta': {'object_name': 'RelatedDocHistory'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
+ },
+ 'doc.relateddocument': {
+ 'Meta': {'object_name': 'RelatedDocument'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
+ },
+ 'doc.state': {
+ 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'previous_states'", 'symmetrical': 'False', 'to': "orm['doc.State']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'doc.statetype': {
+ 'Meta': {'object_name': 'StateType'},
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
+ },
+ 'doc.telechatdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'doc.writeupdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ 'group.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
+ 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
+ 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'name.ballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docrelationshipname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docremindertypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctagname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupBallotPositionName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupstatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.grouptypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.intendedstdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.stdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.streamname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'person.email': {
+ 'Meta': {'object_name': 'Email'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ 'person.person': {
+ 'Meta': {'object_name': 'Person'},
+ 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['doc']
diff --git a/ietf/doc/migrations/0002_auto__del_groupballotpositiondocevent__add_ballottype__add_ballotdocev.py b/ietf/doc/migrations/0002_auto__del_groupballotpositiondocevent__add_ballottype__add_ballotdocev.py
new file mode 100644
index 000000000..dda72adfb
--- /dev/null
+++ b/ietf/doc/migrations/0002_auto__del_groupballotpositiondocevent__add_ballottype__add_ballotdocev.py
@@ -0,0 +1,402 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting model 'GroupBallotPositionDocEvent'
+ db.delete_table('doc_groupballotpositiondocevent')
+
+ # Adding model 'BallotType'
+ db.create_table('doc_ballottype', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('doc_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['name.DocTypeName'], null=True, blank=True)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('question', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('doc', ['BallotType'])
+
+ # Adding M2M table for field positions on 'BallotType'
+ db.create_table('doc_ballottype_positions', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('ballottype', models.ForeignKey(orm['doc.ballottype'], null=False)),
+ ('ballotpositionname', models.ForeignKey(orm['name.ballotpositionname'], null=False))
+ ))
+ db.create_unique('doc_ballottype_positions', ['ballottype_id', 'ballotpositionname_id'])
+
+ # Adding model 'BallotDocEvent'
+ db.create_table('doc_ballotdocevent', (
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ('ballot_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.BallotType'])),
+ ))
+ db.send_create_signal('doc', ['BallotDocEvent'])
+
+ # Adding field 'BallotPositionDocEvent.ballot'
+ db.add_column('doc_ballotpositiondocevent', 'ballot', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['doc.BallotDocEvent'], null=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Adding model 'GroupBallotPositionDocEvent'
+ db.create_table('doc_groupballotpositiondocevent', (
+ ('block_comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('ad', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'])),
+ ('comment_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('block_comment_time', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)),
+ ('pos', self.gf('django.db.models.fields.related.ForeignKey')(default='norecord', to=orm['name.GroupBallotPositionName'])),
+ ('docevent_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['doc.DocEvent'], unique=True, primary_key=True)),
+ ))
+ db.send_create_signal('doc', ['GroupBallotPositionDocEvent'])
+
+ # Deleting model 'BallotType'
+ db.delete_table('doc_ballottype')
+
+ # Removing M2M table for field positions on 'BallotType'
+ db.delete_table('doc_ballottype_positions')
+
+ # Deleting model 'BallotDocEvent'
+ db.delete_table('doc_ballotdocevent')
+
+ # Deleting field 'BallotPositionDocEvent.ballot'
+ db.delete_column('doc_ballotpositiondocevent', 'ballot_id')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'doc.ballotdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotType']"}),
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'doc.ballotpositiondocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
+ 'ballot': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.BallotDocEvent']", 'null': 'True'}),
+ 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'comment_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'discuss': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'discuss_time': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': "orm['name.BallotPositionName']"})
+ },
+ 'doc.ballottype': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotType'},
+ 'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'positions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'doc.docalias': {
+ 'Meta': {'object_name': 'DocAlias'},
+ 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+ },
+ 'doc.docevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'},
+ 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}),
+ 'desc': ('django.db.models.fields.TextField', [], {}),
+ 'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'doc.dochistory': {
+ 'Meta': {'object_name': 'DocHistory'},
+ 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocHistoryAuthor']", 'blank': 'True'}),
+ 'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': "orm['doc.Document']"}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
+ 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.DocAlias']", 'symmetrical': 'False', 'through': "orm['doc.RelatedDocHistory']", 'blank': 'True'}),
+ 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
+ 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_dochistory_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
+ },
+ 'doc.dochistoryauthor': {
+ 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
+ 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {})
+ },
+ 'doc.docreminder': {
+ 'Meta': {'object_name': 'DocReminder'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'due': ('django.db.models.fields.DateTimeField', [], {}),
+ 'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocEvent']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocReminderTypeName']"})
+ },
+ 'doc.document': {
+ 'Meta': {'object_name': 'Document'},
+ 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ad_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['person.Email']", 'symmetrical': 'False', 'through': "orm['doc.DocumentAuthor']", 'blank': 'True'}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'external_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
+ 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.IntendedStdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'internal_comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'primary_key': 'True'}),
+ 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'notify': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1', 'blank': 'True'}),
+ 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'related': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'reversely_related_document_set'", 'blank': 'True', 'through': "orm['doc.RelatedDocument']", 'to': "orm['doc.DocAlias']"}),
+ 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16', 'blank': 'True'}),
+ 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'shepherd_document_set'", 'null': 'True', 'to': "orm['person.Person']"}),
+ 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}),
+ 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.StreamName']", 'null': 'True', 'blank': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['name.DocTagName']", 'null': 'True', 'blank': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'})
+ },
+ 'doc.documentauthor': {
+ 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Email']"}),
+ 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'})
+ },
+ 'doc.initialreviewdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'doc.lastcalldocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'doc.newrevisiondocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'})
+ },
+ 'doc.relateddochistory': {
+ 'Meta': {'object_name': 'RelatedDocHistory'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocHistory']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': "orm['doc.DocAlias']"})
+ },
+ 'doc.relateddocument': {
+ 'Meta': {'object_name': 'RelatedDocument'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DocRelationshipName']"}),
+ 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.Document']"}),
+ 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.DocAlias']"})
+ },
+ 'doc.state': {
+ 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'previous_states'", 'symmetrical': 'False', 'to': "orm['doc.State']"}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'doc.statetype': {
+ 'Meta': {'object_name': 'StateType'},
+ 'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'})
+ },
+ 'doc.telechatdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'returning_item': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'telechat_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'doc.writeupdocevent': {
+ 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': ['doc.DocEvent']},
+ 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'})
+ },
+ 'group.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'acronym': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
+ 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}),
+ 'charter': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'chartered_group'", 'unique': 'True', 'null': 'True', 'to': "orm['doc.Document']"}),
+ 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'list_archive': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'list_email': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
+ 'list_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}),
+ 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupStateName']", 'null': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.GroupTypeName']", 'null': 'True'}),
+ 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['doc.State']", 'symmetrical': 'False'}),
+ 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['name.DocTagName']", 'symmetrical': 'False'})
+ },
+ 'name.ballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
+ 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docrelationshipname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docremindertypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctagname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupstatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.grouptypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.intendedstdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.stdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.streamname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'person.email': {
+ 'Meta': {'object_name': 'Email'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '64', 'primary_key': 'True'}),
+ 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
+ },
+ 'person.person': {
+ 'Meta': {'object_name': 'Person'},
+ 'address': ('django.db.models.fields.TextField', [], {'max_length': '255', 'blank': 'True'}),
+ 'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'ascii': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ascii_short': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'})
+ }
+ }
+
+ complete_apps = ['doc']
diff --git a/ietf/doc/migrations/__init__.py b/ietf/doc/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ietf/doc/models.py b/ietf/doc/models.py
index 774c20485..8b4e27f27 100644
--- a/ietf/doc/models.py
+++ b/ietf/doc/models.py
@@ -12,11 +12,11 @@ from ietf.utils.admin import admin_link
import datetime, os
class StateType(models.Model):
- slug = models.CharField(primary_key=True, max_length=30) # draft, draft_iesg, charter, ...
- label = models.CharField(max_length=255) # State, IESG state, WG state, ...
+ slug = models.CharField(primary_key=True, max_length=30) # draft, draft-iesg, charter, ...
+ label = models.CharField(max_length=255, help_text="Label that should be used (e.g. in admin) for state drop-down for this type of state") # State, IESG state, WG state, ...
def __unicode__(self):
- return self.label
+ return self.slug
class State(models.Model):
type = models.ForeignKey(StateType)
@@ -46,10 +46,10 @@ class DocumentInfo(models.Model):
stream = models.ForeignKey(StreamName, blank=True, null=True) # IETF, IAB, IRTF, Independent Submission
group = models.ForeignKey(Group, blank=True, null=True) # WG, RG, IAB, IESG, Edu, Tools
- abstract = models.TextField()
+ abstract = models.TextField(blank=True)
rev = models.CharField(verbose_name="revision", max_length=16, blank=True)
pages = models.IntegerField(blank=True, null=True)
- order = models.IntegerField(default=1)
+ order = models.IntegerField(default=1, blank=True)
intended_std_level = models.ForeignKey(IntendedStdLevelName, verbose_name="Intended standardization level", blank=True, null=True)
std_level = models.ForeignKey(StdLevelName, verbose_name="Standardization level", blank=True, null=True)
ad = models.ForeignKey(Person, verbose_name="area director", related_name='ad_%(class)s_set', blank=True, null=True)
@@ -149,15 +149,10 @@ class Document(DocumentInfo):
def get_absolute_url(self):
name = self.name
- if self.type_id == "charter":
- return urlreverse('wg_view', kwargs={ 'name': self.group.acronym }, urlconf="ietf.urls")
- elif self.type_id == "draft":
- if self.get_state_slug() == "rfc":
- aliases = self.docalias_set.filter(name__startswith="rfc")
- if aliases:
- name = aliases[0].name
- return urlreverse('doc_view', kwargs={ 'name': name }, urlconf="ietf.urls")
-
+ if self.type_id == "draft" and self.get_state_slug() == "rfc":
+ aliases = self.docalias_set.filter(name__startswith="rfc")
+ if aliases:
+ name = aliases[0].name
elif self.type_id in ('slides','agenda','minutes'):
session = self.session_set.all()[0]
meeting = session.meeting
@@ -172,6 +167,9 @@ class Document(DocumentInfo):
self.type_id,
filename)
return url
+ return urlreverse('doc_view', kwargs={ 'name': name }, urlconf="ietf.urls")
+
+
def file_tag(self):
return u"<%s>" % self.filename_with_rev()
@@ -194,6 +192,9 @@ class Document(DocumentInfo):
a = self.docalias_set.filter(name__startswith="rfc")
if a:
name = a[0].name
+ elif self.type_id == "charter":
+ return "charter-ietf-%s" % self.chartered_group.acronym
+
return name
class RelatedDocHistory(models.Model):
@@ -216,11 +217,19 @@ class DocHistoryAuthor(models.Model):
class DocHistory(DocumentInfo):
doc = models.ForeignKey(Document, related_name="history_set")
- name = models.CharField(max_length=255) # WG charter names can change if the group acronym changes
+ name = models.CharField(max_length=255) # WG charter canonical names can change if the group acronym changes
related = models.ManyToManyField('DocAlias', through=RelatedDocHistory, blank=True)
authors = models.ManyToManyField(Email, through=DocHistoryAuthor, blank=True)
def __unicode__(self):
return unicode(self.doc.name)
+
+ def canonical_name(self):
+ return self.name
+
+ def latest_event(self, *args, **kwargs):
+ kwargs["time__lte"] = self.time
+ return self.doc.latest_event(*args, **kwargs)
+
class Meta:
verbose_name = "document history"
verbose_name_plural = "document histories"
@@ -234,7 +243,7 @@ def save_document_in_history(doc):
# copy fields
fields = get_model_fields_as_dict(doc)
fields["doc"] = doc
- fields["name"] = doc.name
+ fields["name"] = doc.canonical_name()
dochist = DocHistory(**fields)
dochist.save()
@@ -311,6 +320,8 @@ EVENT_TYPES = [
# IESG events
("started_iesg_process", "Started IESG process on document"),
+ ("created_ballot", "Created ballot"),
+ ("closed_ballot", "Closed ballot"),
("sent_ballot_announcement", "Sent ballot announcement"),
("changed_ballot_position", "Changed ballot position"),
@@ -347,7 +358,26 @@ class NewRevisionDocEvent(DocEvent):
rev = models.CharField(max_length=16)
# IESG events
+class BallotType(models.Model):
+ doc_type = models.ForeignKey(DocTypeName, blank=True, null=True)
+ slug = models.SlugField()
+ name = models.CharField(max_length=255)
+ question = models.TextField(blank=True)
+ used = models.BooleanField(default=True)
+ order = models.IntegerField(default=0)
+ positions = models.ManyToManyField(BallotPositionName, blank=True)
+
+ def __unicode__(self):
+ return self.name
+
+ class Meta:
+ ordering = ['order']
+
+class BallotDocEvent(DocEvent):
+ ballot_type = models.ForeignKey(BallotType)
+
class BallotPositionDocEvent(DocEvent):
+ ballot = models.ForeignKey(BallotDocEvent, null=True, default=None) # default=None is a temporary migration period fix, should be removed when charter branch is live
ad = models.ForeignKey(Person)
pos = models.ForeignKey(BallotPositionName, verbose_name="position", default="norecord")
discuss = models.TextField(help_text="Discuss text if position is discuss", blank=True)
@@ -365,14 +395,6 @@ class TelechatDocEvent(DocEvent):
telechat_date = models.DateField(blank=True, null=True)
returning_item = models.BooleanField(default=False)
-# Charter ballot events
-class GroupBallotPositionDocEvent(DocEvent):
- ad = models.ForeignKey(Person)
- pos = models.ForeignKey(GroupBallotPositionName, verbose_name="position", default="norecord")
- block_comment = models.TextField(help_text="Blocking comment if position is comment", blank=True)
- block_comment_time = models.DateTimeField(help_text="Blocking comment was written", blank=True, null=True)
- comment = models.TextField(help_text="Non-blocking comment", blank=True)
- comment_time = models.DateTimeField(help_text="Time non-blocking comment was written", blank=True, null=True)
-
+# charter events
class InitialReviewDocEvent(DocEvent):
expires = models.DateTimeField(blank=True, null=True)
diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py
index f9f0e4a07..8f9865bef 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))
@@ -823,7 +797,7 @@ class DocumentComment(DocEvent):
class Position(BallotPositionDocEvent):
def from_object(self, base):
for f in base._meta.fields:
- if not f.name in ('discuss', "ad"): # don't overwrite properties
+ if not f.name in ('discuss', 'ad', 'ballot', ): # don't overwrite properties
setattr(self, f.name, getattr(base, f.name))
self.orig = base
diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py
index 442164e15..d8caa8446 100644
--- a/ietf/doc/utils.py
+++ b/ietf/doc/utils.py
@@ -30,17 +30,14 @@ def get_tags_for_stream_id(stream_id):
else:
return []
-def active_ballot_positions(doc):
+def active_ballot_positions(doc, ballot=None):
"""Return dict mapping each active AD to a current ballot position (or None if they haven't voted)."""
active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active"))
res = {}
- 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")
+ if not ballot:
+ ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
+ 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:
@@ -51,7 +48,64 @@ def active_ballot_positions(doc):
res[ad] = None
return res
+
+def needed_ballot_positions(doc, active_positions):
+ '''Returns text answering the question "what does this document
+ need to pass?". The return value is only useful if the document
+ is currently in IESG evaluation.'''
+ yes = [p for p in active_positions if p and p.pos_id == "yes"]
+ noobj = [p for p in active_positions if p and p.pos_id == "noobj"]
+ blocking = [p for p in active_positions if p and p.pos.blocking]
+ recuse = [p for p in active_positions if p and p.pos_id == "recuse"]
+
+ answer = []
+ if yes < 1:
+ answer.append("Needs a YES.")
+ if blocking:
+ if blocking:
+ answer.append("Has a %s." % blocking[0].pos.name.upper())
+ else:
+ answer.append("Has %d %s." % (len(blocking), blocking[0].name.upper()))
+ needed = 1
+ 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) - len(recuse)) * 2 // 3
+
+ have = len(yes) + len(noobj) + len(blocking)
+ if have < needed:
+ more = needed - have
+ if more == 1:
+ answer.append("Needs %d more position." % more)
+ else:
+ answer.append("Needs %d more positions." % more)
+ else:
+ if blocking:
+ answer.append("Has enough positions to pass once %s positions are resolved." % blocking[0].pos.name.upper())
+ else:
+ answer.append("Has enough positions to pass.")
+
+ return " ".join(answer)
+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"
+
+def create_ballot_if_not_open(doc, by, ballot_type_slug):
+ if not ballot_open(doc, ballot_type_slug):
+ e = BallotDocEvent(type="created_ballot", by=by, doc=doc)
+ e.ballot_type = BallotType.objects.get(doc_type=doc.type, slug=ballot_type_slug)
+ e.desc = u'Created "%s" ballot' % e.ballot_type.name
+ e.save()
+
+def close_open_ballots(doc, by):
+ for t in BallotType.objects.filter(doc_type=doc.type_id):
+ if ballot_open(doc, t.slug):
+ e = BallotDocEvent(type="closed_ballot", doc=doc, by=by)
+ e.ballot_type = t
+ 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
@@ -79,6 +133,16 @@ def augment_with_start_time(docs):
return docs
+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."""
@@ -99,6 +163,3 @@ def augment_with_telechat_date(docs):
seen.add(e.doc_id)
return docs
-
-
-
diff --git a/ietf/group/proxy.py b/ietf/group/proxy.py
index 90340d737..817badf33 100644
--- a/ietf/group/proxy.py
+++ b/ietf/group/proxy.py
@@ -1,8 +1,6 @@
from ietf.utils.proxy import TranslatingManager, proxy_role_email
from models import *
-from ietf.doc.models import Document # for charter text
-from ietf.wgcharter.utils import get_charter_for_revision, approved_revision
class Acronym(Group):
class LazyIndividualSubmitter(object):
diff --git a/ietf/group/utils.py b/ietf/group/utils.py
index b658e1c91..e0a10ef54 100644
--- a/ietf/group/utils.py
+++ b/ietf/group/utils.py
@@ -35,21 +35,23 @@ def get_charter_text(group):
# get file path from settings. Syntesize file name from path, acronym, and suffix
try:
# Try getting charter from new charter tool
- from ietf.wgcharter.utils import get_charter_for_revision, approved_revision
+ c = group.charter
- charter = group.charter
- ch = get_charter_for_revision(charter, charter.rev)
- name = ch.name
- rev = approved_revision(ch.rev)
- filename = os.path.join(charter.get_file_path(), "%s-%s.txt" % (name, rev))
- desc_file = open(filename)
- desc = desc_file.read()
- return desc
- except:
+ # find the latest, preferably approved, revision
+ for h in group.charter.history_set.exclude(rev="").order_by("time"):
+ h_appr = "-" not in h.rev
+ c_appr = "-" not in c.rev
+ if (h.rev > c.rev and not (c_appr and not h_appr)) or (h_appr and not c_appr):
+ c = h
+
+ filename = os.path.join(c.get_file_path(), "%s-%s.txt" % (c.canonical_name(), c.rev))
+ with open(filename) as f:
+ return f.read()
+ except IOError:
try:
filename = os.path.join(settings.IETFWG_DESCRIPTIONS_PATH, group.acronym) + ".desc.txt"
desc_file = open(filename)
desc = desc_file.read()
- except:
+ except BaseException:
desc = 'Error Loading Work Group Description'
return desc
diff --git a/ietf/idrfc/idrfc_wrapper.py b/ietf/idrfc/idrfc_wrapper.py
index cf730c8ce..56396ab10 100644
--- a/ietf/idrfc/idrfc_wrapper.py
+++ b/ietf/idrfc/idrfc_wrapper.py
@@ -742,7 +742,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 d3f38736e..be6a94f37 100644
--- a/ietf/idrfc/mails.py
+++ b/ietf/idrfc/mails.py
@@ -333,11 +333,10 @@ def email_ballot_deferred(request, doc, by, telechat_date):
def generate_issue_ballot_mail(request, doc):
pass
-def generate_issue_ballot_mailREDESIGN(request, doc):
+def generate_issue_ballot_mailREDESIGN(request, doc, ballot):
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/markup_txt.py b/ietf/idrfc/markup_txt.py
index 9ef26d471..b4bb5d6f0 100644
--- a/ietf/idrfc/markup_txt.py
+++ b/ietf/idrfc/markup_txt.py
@@ -34,7 +34,7 @@ from django.utils.html import escape
import string
import re
-def markup(content):
+def markup(content, split=True):
# normalize line endings to LF only
content = content.replace("\r\n", "\n")
content = content.replace("\r", "\n")
@@ -63,8 +63,10 @@ def markup(content):
content = re.sub("\n\n([0-9]+\\.|[A-Z]\\.[0-9]|Appendix|Status of|Abstract|Table of|Full Copyright|Copyright|Intellectual Property|Acknowled|Author|Index)(.*)(?=\n\n)", """\n\n
\g<1>\g<2> """, content)
- n = content.find("\n", 5000)
- content1 = "
"+content[:n+1]+" \n"
- content2 = "
"+content[n+1:]+" \n"
-
- return (content1, content2)
+ if split:
+ n = content.find("\n", 5000)
+ content1 = "
"+content[:n+1]+" \n"
+ content2 = "
"+content[n+1:]+" \n"
+ return (content1, content2)
+ else:
+ return "
" + content + " \n"
diff --git a/ietf/idrfc/templatetags/ballot_icon.py b/ietf/idrfc/templatetags/ballot_icon.py
index 98b176065..bdbf752dc 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,103 +55,71 @@ 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
- tracker_id = doc.draft_id
- 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)
- tracker_id = doc.rfc_number
- else:
- name = doc.draft_name
- tracker_id = doc.tracker_id
- 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, tracker_id, red,yellow,green,gray,blank, my, adId)+""
-def render_ballot_icon2(draft_name, tracker_id, red,yellow,green,gray,blank, my,adId):
- edit_position_url = urlreverse('doc_edit_position', kwargs=dict(name=draft_name))
- if adId:
- res_cm = ' oncontextmenu="editBallot(\''+str(edit_position_url)+'\');return false;"'
- else:
- res_cm = ''
- res = '
'
- for y in range(3):
- res = res + ""
- 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 + ' '
- my = None
- else:
- res = res + ' '
- res = res + ' '
- res = res + '
'
- return res
+def render_ballot_icon(user, doc):
+ if not doc:
+ return ""
+
+ if doc.type_id == "draft":
+ s = doc.get_state("draft-iesg")
+ if s and s.name not in BALLOT_ACTIVE_STATES:
+ return ""
+ elif doc.type_id == "charter":
+ if doc.get_state_slug() not in ("intrev", "iesgrev"):
+ 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 = ['
']
+
+ res.append("")
+
+ for i, (ad, pos) in enumerate(positions):
+ if i > 0 and i % 5 == 0:
+ res.append(" ")
+ res.append("")
+
+ c = "position-%s" % (pos.pos.slug if pos else "norecord")
+
+ if ad == user.get_profile():
+ c += " my"
+
+ res.append(' ' % c)
+
+ res.append(" ")
+ res.append("
")
+
+ 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)
+ if hasattr(doc, "_idinternal"):
+ # hack for old schema
+ doc = doc._idinternal
+ return render_ballot_icon(context.get("user"), doc)
def do_ballot_icon(parser, token):
try:
diff --git a/ietf/idrfc/testsREDESIGN.py b/ietf/idrfc/testsREDESIGN.py
index 35ba65532..b9d3e0402 100644
--- a/ietf/idrfc/testsREDESIGN.py
+++ b/ietf/idrfc/testsREDESIGN.py
@@ -418,7 +418,8 @@ class EditPositionTestCase(django.test.TestCase):
def test_edit_position(self):
draft = make_test_data()
- url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name))
+ url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name,
+ ballot_id=draft.latest_event(BallotDocEvent, type="created_ballot").pk))
login_testing_unauthorized(self, "ad", url)
ad = Person.objects.get(name="Aread Irector")
@@ -479,7 +480,8 @@ class EditPositionTestCase(django.test.TestCase):
def test_edit_position_as_secretary(self):
draft = make_test_data()
- url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name))
+ url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name,
+ ballot_id=draft.latest_event(BallotDocEvent, type="created_ballot").pk))
ad = Person.objects.get(name="Aread Irector")
url += "?ad=%s" % ad.pk
login_testing_unauthorized(self, "secretary", url)
@@ -523,16 +525,19 @@ class EditPositionTestCase(django.test.TestCase):
draft.save()
ad = Person.objects.get(name="Aread Irector")
-
+
+ ballot = draft.latest_event(BallotDocEvent, type="created_ballot")
+
BallotPositionDocEvent.objects.create(
doc=draft, type="changed_ballot_position",
- by=ad, ad=ad, pos=BallotPositionName.objects.get(slug="discuss"),
+ by=ad, ad=ad, ballot=ballot, pos=BallotPositionName.objects.get(slug="discuss"),
discuss="This draft seems to be lacking a clearer title?",
discuss_time=datetime.datetime.now(),
comment="Test!",
comment_time=datetime.datetime.now())
- url = urlreverse('doc_send_ballot_comment', kwargs=dict(name=draft.name))
+ url = urlreverse('doc_send_ballot_comment', kwargs=dict(name=draft.name,
+ ballot_id=ballot.pk))
login_testing_unauthorized(self, "ad", url)
# normal get
@@ -691,10 +696,13 @@ class BallotWriteupsTestCase(django.test.TestCase):
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "ad", url)
+ ballot = draft.latest_event(BallotDocEvent, type="created_ballot")
+
def create_pos(num, vote, comment="", discuss=""):
ad = Person.objects.get(name="Ad No%s" % num)
e = BallotPositionDocEvent()
e.doc = draft
+ e.ballot = ballot
e.by = ad
e.ad = ad
e.pos = BallotPositionName.objects.get(slug=vote)
diff --git a/ietf/idrfc/urls.py b/ietf/idrfc/urls.py
index 5de204915..640bbbdad 100644
--- a/ietf/idrfc/urls.py
+++ b/ietf/idrfc/urls.py
@@ -30,7 +30,7 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-from django.conf.urls.defaults import patterns, url
+from django.conf.urls.defaults import patterns, url, include
from ietf.idrfc import views_doc, views_search, views_edit, views_ballot, views
urlpatterns = patterns('',
@@ -40,10 +40,16 @@ urlpatterns = patterns('',
(r'^active/$', views_search.active),
(r'^in-last-call/$', views_search.in_last_call),
url(r'^ad/(?P
[A-Za-z0-9.-]+)/$', views_search.by_ad, name="doc_search_by_ad"),
-
- url(r'^(?P[A-Za-z0-9.-]+)/((?Pballot|writeup|history)/)?$', views_doc.document_main, name="doc_view"),
+
+ url(r'^(?P[A-Za-z0-9.-]+)/((?P[0-9-]+)/)?$', views_doc.document_main, name="doc_view"),
+ url(r'^(?P[A-Za-z0-9.-]+)/history/$', views_doc.document_history, name="doc_history"),
+ url(r'^(?P[A-Za-z0-9.-]+)/writeup/$', views_doc.document_writeup, name="doc_writeup"),
+ url(r'^(?P[A-Za-z0-9.-]+)/ballot/(?P[A-Za-z0-9.-]+)/position/$', views_ballot.edit_position, name='doc_edit_position'),
+ url(r'^(?P[A-Za-z0-9.-]+)/ballot/(?P[A-Za-z0-9.-]+)/emailposition/$', views_ballot.send_ballot_comment, name='doc_send_ballot_comment'),
+ url(r'^(?P[A-Za-z0-9.-]+)/ballot/(?P[A-Za-z0-9.-]+)/$', views_doc.document_ballot, name="doc_ballot"),
+ url(r'^(?P[A-Za-z0-9.-]+)/ballot/$', views_doc.document_ballot, name="doc_ballot"),
(r'^(?P[A-Za-z0-9.-]+)/doc.json$', views_doc.document_debug),
- (r'^(?P[A-Za-z0-9.-]+)/_ballot.data$', views_doc.document_ballot),
+ (r'^(?P[A-Za-z0-9.-]+)/_ballot.data$', views_doc.ballot_html), # why is this url so weird instead of just ballot.html?
(r'^(?P[A-Za-z0-9.-]+)/ballot.tsv$', views_doc.ballot_tsv),
(r'^(?P[A-Za-z0-9.-]+)/ballot.json$', views_doc.ballot_json),
@@ -52,14 +58,14 @@ urlpatterns = patterns('',
url(r'^(?P[A-Za-z0-9.-]+)/edit/requestresurrect/$', views_edit.request_resurrect, name='doc_request_resurrect'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/resurrect/$', views_edit.resurrect, name='doc_resurrect'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/addcomment/$', views_edit.add_comment, name='doc_add_comment'),
- url(r'^(?P[A-Za-z0-9.-]+)/edit/position/$', views_ballot.edit_position, name='doc_edit_position'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/clearballot/$', views_ballot.clear_ballot, name='doc_clear_ballot'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/deferballot/$', views_ballot.defer_ballot, name='doc_defer_ballot'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/undeferballot/$', views_ballot.undefer_ballot, name='doc_undefer_ballot'),
- url(r'^(?P[A-Za-z0-9.-]+)/edit/sendballotcomment/$', views_ballot.send_ballot_comment, name='doc_send_ballot_comment'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/lastcalltext/$', views_ballot.lastcalltext, name='doc_ballot_lastcall'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/ballotwriteupnotes/$', views_ballot.ballot_writeupnotes, name='doc_ballot_writeupnotes'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/approvaltext/$', views_ballot.ballot_approvaltext, name='doc_ballot_approvaltext'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'),
url(r'^(?P[A-Za-z0-9.-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),
+
+ (r'^(?P[A-Za-z0-9.-]+)/charter/', include('ietf.wgcharter.urls')),
)
diff --git a/ietf/idrfc/views_ballot.py b/ietf/idrfc/views_ballot.py
index 9f1fa9eb2..e31104fb0 100644
--- a/ietf/idrfc/views_ballot.py
+++ b/ietf/idrfc/views_ballot.py
@@ -10,10 +10,11 @@ from django.template.loader import render_to_string
from django.template import RequestContext
from django import forms
from django.utils.html import strip_tags
+from django.utils import simplejson
from django.conf import settings
from ietf.utils.mail import send_mail_text, send_mail_preformatted
-from ietf.ietfauth.decorators import group_required
+from ietf.ietfauth.decorators import group_required, role_required
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ietfauth.decorators import has_role, role_required
from ietf.idtracker.models import *
@@ -25,10 +26,12 @@ 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.message.utils import infer_message
+from ietf.message.utils import infer_message
+from ietf.person.models import Person
BALLOT_CHOICES = (("yes", "Yes"),
("noobj", "No Objection"),
@@ -216,6 +219,11 @@ class EditPositionFormREDESIGN(forms.Form):
comment = forms.CharField(required=False, widget=forms.Textarea)
return_to_url = forms.CharField(required=False, widget=forms.HiddenInput)
+ def __init__(self, *args, **kwargs):
+ ballot_type = kwargs.pop("ballot_type")
+ super(EditPositionForm, self).__init__(*args, **kwargs)
+ self.fields['position'].queryset = ballot_type.positions.order_by('order')
+
def clean_discuss(self):
entered_discuss = self.cleaned_data["discuss"]
entered_pos = self.cleaned_data["position"]
@@ -224,38 +232,34 @@ class EditPositionFormREDESIGN(forms.Form):
return entered_discuss
@role_required('Area Director','Secretariat')
-def edit_positionREDESIGN(request, name):
- """Vote and edit discuss and comment on Internet Draft as Area Director."""
+def edit_positionREDESIGN(request, name, ballot_id):
+ """Vote and edit discuss and comment on document as Area Director."""
doc = get_object_or_404(Document, docalias__name=name)
- started_process = doc.latest_event(type="started_iesg_process")
- if not doc.get_state("draft-iesg") or not started_process:
- raise Http404()
+ ballot = get_object_or_404(BallotDocEvent, type="created_ballot", pk=ballot_id, doc=doc)
ad = login = request.user.get_profile()
if 'HTTP_REFERER' in request.META:
return_to_url = request.META['HTTP_REFERER']
else:
- return_to_url = doc.get_absolute_url()
+ return_to_url = urlreverse("doc_ballot", kwargs=dict(name=doc.name, ballot_id=ballot_id))
# if we're in the Secretariat, we can select an AD to act as stand-in for
if has_role(request.user, "Secretariat"):
ad_id = request.GET.get('ad')
if not ad_id:
raise Http404()
- from ietf.person.models import Person
ad = get_object_or_404(Person, pk=ad_id)
- old_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
+ old_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, ballot=ballot)
if request.method == 'POST':
if not has_role(request.user, "Secretariat") and not ad.role_set.filter(name="ad", group__type="area", group__state="active"):
# prevent pre-ADs from voting
return HttpResponseForbidden("Must be a proper Area Director in an active area to cast ballot")
- form = EditPositionForm(request.POST)
+ form = EditPositionForm(request.POST, ballot_type=ballot.ballot_type)
if form.is_valid():
-
# save the vote
clean = form.cleaned_data
@@ -264,6 +268,7 @@ def edit_positionREDESIGN(request, name):
pos = BallotPositionDocEvent(doc=doc, by=login)
pos.type = "changed_ballot_position"
+ pos.ballot = ballot
pos.ad = ad
pos.pos = clean["position"]
pos.comment = clean["comment"].rstrip()
@@ -276,7 +281,7 @@ def edit_positionREDESIGN(request, name):
changes = []
added_events = []
# possibly add discuss/comment comments to history trail
- # so it's easy to see
+ # so it's easy to see what's happened
old_comment = old_pos.comment if old_pos else ""
if pos.comment != old_comment:
pos.comment_time = pos.time
@@ -298,7 +303,8 @@ def edit_positionREDESIGN(request, name):
e = DocEvent(doc=doc, by=login)
e.by = ad # otherwise we can't see who's saying it
e.type = "added_comment"
- e.desc = "[Ballot discuss]\n" + pos.discuss
+ e.desc = "[Ballot %s]\n" % pos.pos.name.lower()
+ e.desc += pos.discuss
added_events.append(e)
# figure out a description
@@ -318,13 +324,13 @@ def edit_positionREDESIGN(request, name):
pos.save()
for e in added_events:
- e.save() # save them after the position is saved to get later id
+ e.save() # save them after the position is saved to get later id for sorting order
if request.POST.get("send_mail"):
qstr = "?return_to_url=%s" % return_to_url
if request.GET.get('ad'):
qstr += "&ad=%s" % request.GET.get('ad')
- return HttpResponseRedirect(urlreverse("doc_send_ballot_comment", kwargs=dict(name=doc.name)) + qstr)
+ return HttpResponseRedirect(urlreverse("doc_send_ballot_comment", kwargs=dict(name=doc.name, ballot_id=ballot_id)) + qstr)
elif request.POST.get("Defer"):
return HttpResponseRedirect(urlreverse("doc_defer_ballot", kwargs=dict(name=doc)))
elif request.POST.get("Undefer"):
@@ -341,10 +347,13 @@ def edit_positionREDESIGN(request, name):
if return_to_url:
initial['return_to_url'] = return_to_url
- form = EditPositionForm(initial=initial)
+ 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("draft-iesg") == "defer":
+ if doc.get_state_slug("%s-iesg" % doc.type_id) == "defer":
ballot_deferred = doc.latest_event(type="changed_document", desc__startswith="State changed to IESG Evaluation - Defer ")
return render_to_response('idrfc/edit_positionREDESIGN.html',
@@ -354,6 +363,8 @@ def edit_positionREDESIGN(request, name):
return_to_url=return_to_url,
old_pos=old_pos,
ballot_deferred=ballot_deferred,
+ show_discuss_text=old_pos and old_pos.pos.blocking,
+ blocking_positions=simplejson.dumps(blocking_positions),
),
context_instance=RequestContext(request))
@@ -434,42 +445,39 @@ def send_ballot_comment(request, name):
),
context_instance=RequestContext(request))
-@group_required('Area_Director','Secretariat')
-def send_ballot_commentREDESIGN(request, name):
- """Email Internet Draft ballot discuss/comment for area director."""
+@role_required('Area Director','Secretariat')
+def send_ballot_commentREDESIGN(request, name, ballot_id):
+ """Email document ballot position discuss/comment for Area Director."""
doc = get_object_or_404(Document, docalias__name=name)
- started_process = doc.latest_event(type="started_iesg_process")
- if not started_process:
- raise Http404()
+ ballot = get_object_or_404(BallotDocEvent, type="created_ballot", pk=ballot_id, doc=doc)
ad = login = request.user.get_profile()
return_to_url = request.GET.get('return_to_url')
if not return_to_url:
- return_to_url = doc.get_absolute_url()
+ return_to_url = urlreverse("doc_ballot", kwargs=dict(name=doc.name, ballot_id=ballot_id))
if 'HTTP_REFERER' in request.META:
back_url = request.META['HTTP_REFERER']
else:
- back_url = doc.get_absolute_url()
+ back_url = urlreverse("doc_ballot", kwargs=dict(name=doc.name, ballot_id=ballot_id))
# if we're in the Secretariat, we can select an AD to act as stand-in for
if not has_role(request.user, "Area Director"):
ad_id = request.GET.get('ad')
if not ad_id:
raise Http404()
- from ietf.person.models import Person
ad = get_object_or_404(Person, pk=ad_id)
- pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time)
+ pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad=ad, ballot=ballot)
if not pos:
raise Http404()
subj = []
d = ""
- if pos.pos_id == "discuss" and pos.discuss:
+ if pos.pos.blocking and pos.discuss:
d = pos.discuss
- subj.append("DISCUSS")
+ subj.append(pos.pos.name.upper())
c = ""
if pos.comment:
c = pos.comment
@@ -950,9 +958,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()
@@ -975,9 +980,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"
@@ -988,7 +997,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)
@@ -1241,6 +1250,8 @@ def approve_ballotREDESIGN(request, name):
new_state = State.objects.get(type="draft-iesg", slug="ann")
# fixup document
+ close_open_ballots(doc, login)
+
save_document_in_history(doc)
prev = doc.get_state("draft-iesg")
diff --git a/ietf/idrfc/views_doc.py b/ietf/idrfc/views_doc.py
index e6c4aab47..d4f84c5fe 100644
--- a/ietf/idrfc/views_doc.py
+++ b/ietf/idrfc/views_doc.py
@@ -30,11 +30,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import re, os
-from datetime import datetime, time
+import re, os, datetime
from django.http import HttpResponse, Http404
-from django.shortcuts import render_to_response, get_object_or_404
+from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.template import RequestContext
from django.template.loader import render_to_string
from django.template.defaultfilters import truncatewords_html
@@ -50,6 +49,299 @@ from ietf.idrfc import markup_txt
from ietf.idrfc.models import RfcIndex, DraftVersions
from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper
from ietf.ietfworkflows.utils import get_full_info_for_draft
+from ietf.doc.models import *
+from ietf.doc.utils import get_chartering_type, needed_ballot_positions, active_ballot_positions
+from ietf.utils.history import find_history_active_at
+from ietf.ietfauth.decorators import has_role
+
+
+def render_document_top(request, doc, tab):
+ tabs = []
+ tabs.append(("Document", "document", urlreverse("ietf.idrfc.views_doc.document_main", kwargs=dict(name=doc.name)), True))
+
+ ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
+ if doc.type_id == "draft":
+ # if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
+ tabs.append(("IESG Evaluation Record", "ballot", urlreverse("ietf.idrfc.views_doc.document_ballot", kwargs=dict(name=doc.name)), ballot))
+ elif doc.type_id == "charter":
+ tabs.append(("IESG Review", "ballot", urlreverse("ietf.idrfc.views_doc.document_ballot", kwargs=dict(name=doc.name)), ballot))
+
+ # FIXME: if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot:
+ tabs.append(("IESG Writeups", "writeup", urlreverse("ietf.idrfc.views_doc.document_writeup", kwargs=dict(name=doc.name)), True))
+ tabs.append(("History", "history", urlreverse("ietf.idrfc.views_doc.document_history", kwargs=dict(name=doc.name)), True))
+
+ name = doc.canonical_name()
+ if name.startswith("rfc"):
+ name = "RFC %s" % name[3:]
+ else:
+ name += "-" + doc.rev
+
+ return render_to_string("idrfc/document_top.html",
+ dict(doc=doc,
+ tabs=tabs,
+ selected=tab,
+ name=name))
+
+
+def document_main(request, name, rev=None):
+ if name.lower().startswith("draft") or name.lower().startswith("rfc"):
+ if rev != None: # no support for old revisions at the moment
+ raise Http404()
+ return document_main_idrfc(request, name, tab="document")
+
+ print name
+ print Document.objects.filter(name=name)
+ doc = get_object_or_404(Document, docalias__name=name)
+ print doc
+ group = doc.group
+ print group
+
+ revisions = [ doc.rev ]
+ for h in doc.history_set.order_by("-time"):
+ if h.rev and not h.rev in revisions:
+ revisions.append(h.rev)
+
+ snapshot = False
+
+ if rev != None:
+ if rev == doc.rev:
+ return redirect('doc_view', name=name)
+
+ # find the entry in the history
+ for h in doc.history_set.order_by("-time"):
+ if rev == h.rev:
+ snapshot = True
+ doc = h
+ break
+
+ if not snapshot:
+ return redirect('doc_view', name=name)
+
+ # find old group, too
+ gh = find_history_active_at(doc.group, doc.time)
+ if gh:
+ group = gh
+
+ top = render_document_top(request, doc, "document")
+
+
+ telechat = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
+ if telechat and telechat.telechat_date < datetime.date.today():
+ telechat = None
+
+ if doc.type_id == "charter":
+ filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
+
+ content = _get_html(filename, os.path.join(settings.CHARTER_PATH, filename), split=False)
+
+ ballot_summary = None
+ if doc.get_state_slug() in ("intrev", "iesgrev"):
+ ballot_summary = needed_ballot_positions(doc, active_ballot_positions(doc).values())
+
+ return render_to_response("idrfc/document_charter.html",
+ dict(doc=doc,
+ top=top,
+ chartering=get_chartering_type(doc),
+ content=content,
+ txt_url=settings.CHARTER_TXT_URL + filename,
+ revisions=revisions,
+ snapshot=snapshot,
+ telechat=telechat,
+ ballot_summary=ballot_summary,
+ group=group,
+ ),
+ context_instance=RequestContext(request))
+
+ raise Http404()
+
+
+def document_history(request, name):
+ # todo: remove need for specific handling of drafts by porting the
+ # two event text hacks
+ if name.lower().startswith("draft") or name.lower().startswith("rfc"):
+ return document_main_idrfc(request, name, "history")
+
+
+ doc = get_object_or_404(Document, docalias__name=name)
+ top = render_document_top(request, doc, "history")
+
+ diff_documents = [ doc ]
+ diff_documents.extend(Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces"))
+
+ # pick up revisions from events
+ diff_revisions = []
+ seen = set()
+
+ diffable = name.startswith("draft") or name.startswith("charter")
+
+ if diffable:
+ for e in NewRevisionDocEvent.objects.filter(type="new_revision", doc__in=diff_documents).select_related('doc').order_by("-time", "-id"):
+ if not (e.doc.name, e.rev) in seen:
+ seen.add((e.doc.name, e.rev))
+
+ url = ""
+ if name.startswith("charter"):
+ h = find_history_active_at(e.doc, e.time)
+ url = settings.CHARTER_TXT_URL + ("%s-%s.txt" % ((h or doc).canonical_name(), e.rev))
+ elif name.startswith("draft"):
+ # rfcdiff tool has special support for IDs
+ url = e.doc.name + "-" + e.rev
+
+ diff_revisions.append((e.doc.name, e.rev, e.time, url))
+
+ # grab event history
+ events = doc.docevent_set.all().order_by("-time", "-id").select_related("by")
+
+ # fill in revision numbers
+ event_revisions = list(NewRevisionDocEvent.objects.filter(doc=doc).order_by('time', 'id').values('rev', 'time'))
+
+ cur_rev = doc.rev
+ if doc.get_state_slug() == "rfc":
+ cur_rev = "RFC"
+
+ for e in events:
+ while event_revisions and e.time < event_revisions[-1]["time"]:
+ event_revisions.pop()
+
+ if event_revisions:
+ cur_rev = event_revisions[-1]["rev"]
+ else:
+ cur_rev = "00"
+
+ e.rev = cur_rev
+
+ return render_to_response("idrfc/document_history.html",
+ dict(doc=doc,
+ top=top,
+ diff_revisions=diff_revisions,
+ events=events,
+ ),
+ context_instance=RequestContext(request))
+
+def document_writeup(request, name):
+ if name.lower().startswith("draft") or name.lower().startswith("rfc"):
+ # todo: migrate idrfc to pattern below
+ return document_main_idrfc(request, name, "writeup")
+
+ doc = get_object_or_404(Document, docalias__name=name)
+ top = render_document_top(request, doc, "writeup")
+
+ writeups = []
+ if doc.type_id == "charter":
+ e = doc.latest_event(WriteupDocEvent, type="changed_review_announcement")
+ writeups.append(("WG Review Announcement",
+ e.text if e else "",
+ urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="review"))))
+
+ e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
+ writeups.append(("WG Action Announcement",
+ e.text if e else "",
+ urlreverse("ietf.wgcharter.views.announcement_text", kwargs=dict(name=doc.name, ann="action"))))
+
+ if doc.latest_event(BallotDocEvent, type="created_ballot"):
+ e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
+ writeups.append(("Ballot Announcement",
+ e.text if e else "",
+ urlreverse("ietf.wgcharter.views.ballot_writeupnotes", kwargs=dict(name=doc.name))))
+
+ if not writeups:
+ raise Http404()
+
+ return render_to_response("idrfc/document_writeup.html",
+ dict(doc=doc,
+ top=top,
+ writeups=writeups,
+ can_edit=has_role(request.user, ("Area Director", "Secretariat")),
+ ),
+ context_instance=RequestContext(request))
+
+def document_ballot_content(request, doc, ballot_id, editable=True):
+ """Render HTML string with content of ballot page."""
+ if ballot_id != None:
+ ballot = doc.latest_event(BallotDocEvent, type="created_ballot", pk=ballot_id)
+ else:
+ ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
+
+ if not ballot:
+ raise Http404()
+
+ deferred = None
+ if doc.type_id == "draft" and doc.get_state_slug("draft-iesg") == "defer":
+ # FIXME: fragile
+ deferred = doc.latest_event(type="changed_document", desc__startswith="State changed to IESG Evaluation - Defer ")
+
+ # collect positions
+ active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active").distinct())
+
+ positions = []
+ seen = {}
+ for e in BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).select_related('ad', 'pos').order_by("-time", '-id'):
+ if e.ad not in seen:
+ e.old_ad = e.ad not in active_ads
+ e.old_positions = []
+ positions.append(e)
+ seen[e.ad] = e
+ else:
+ latest = seen[e.ad]
+ if latest.old_positions:
+ prev = latest.old_positions[-1]
+ else:
+ prev = latest.pos.name
+
+ 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")
+ for ad in active_ads:
+ if ad not in seen:
+ e = BallotPositionDocEvent(type="changed_ballot_position", doc=doc, ad=ad)
+ e.pos = norecord
+ e.old_ad = False
+ e.old_positions = []
+ positions.append(e)
+
+ # put into position groups
+ position_groups = []
+ for n in BallotPositionName.objects.filter(slug__in=[p.pos_id for p in positions]).order_by('order'):
+ g = (n, [p for p in positions if p.pos_id == n.slug])
+ g[1].sort(key=lambda p: (p.old_ad, p.ad.plain_name()))
+ if n.blocking:
+ position_groups.insert(0, g)
+ else:
+ position_groups.append(g)
+
+ summary = needed_ballot_positions(doc, [p for p in positions if not p.old_ad])
+
+ text_positions = [p for p in positions if p.discuss or p.comment]
+ text_positions.sort(key=lambda p: (p.old_ad, p.ad.plain_name()))
+
+ all_ballots = BallotDocEvent.objects.filter(doc=doc, type="created_ballot")
+
+ return render_to_string("idrfc/document_ballot_content.html",
+ dict(doc=doc,
+ ballot=ballot,
+ position_groups=position_groups,
+ text_positions=text_positions,
+ editable=editable,
+ deferred=deferred,
+ summary=summary,
+ all_ballots=all_ballots,
+ ),
+ context_instance=RequestContext(request))
+
+def document_ballot(request, name, ballot_id=None):
+ doc = get_object_or_404(Document, docalias__name=name)
+ top = render_document_top(request, doc, "ballot")
+
+ c = document_ballot_content(request, doc, ballot_id, editable=True)
+
+ return render_to_response("idrfc/document_ballot.html",
+ dict(doc=doc,
+ top=top,
+ ballot_content=c,
+ ),
+ context_instance=RequestContext(request))
def document_debug(request, name):
r = re.compile("^rfc([1-9][0-9]*)$")
@@ -63,18 +355,21 @@ def document_debug(request, name):
doc = IdWrapper(draft=id)
return HttpResponse(doc.to_json(), mimetype='text/plain')
-def _get_html(key, filename):
+def _get_html(key, filename, split=True):
f = None
try:
f = open(filename, 'rb')
raw_content = f.read()
except IOError:
- return ("Error; cannot read ("+key+")", "")
+ error = "Error; cannot read ("+key+")"
+ if split:
+ return (error, "")
+ else:
+ return error
finally:
if f:
f.close()
- (c1,c2) = markup_txt.markup(raw_content)
- return (c1,c2)
+ return markup_txt.markup(raw_content, split)
def include_text(request):
include_text = request.GET.get( 'include_text' )
@@ -114,9 +409,7 @@ def document_main_rfc(request, rfc_number, tab):
context_instance=RequestContext(request));
@decorator_from_middleware(GZipMiddleware)
-def document_main(request, name, tab):
- if tab is None:
- tab = "document"
+def document_main_idrfc(request, name, tab):
r = re.compile("^rfc([1-9][0-9]*)$")
m = r.match(name)
if m:
@@ -221,11 +514,11 @@ def _get_history(doc, versions):
# convert plain dates to datetimes (required for sorting)
for x in results:
- if not isinstance(x['date'], datetime):
+ if not isinstance(x['date'], datetime.datetime):
if x['date']:
- x['date'] = datetime.combine(x['date'], time(0,0,0))
+ x['date'] = datetime.datetime.combine(x['date'], datetime.time(0,0,0))
else:
- x['date'] = datetime(1970,1,1)
+ x['date'] = datetime.datetime(1970,1,1)
results.sort(key=lambda x: x['date'])
results.reverse()
@@ -295,7 +588,7 @@ def get_ballot(name):
ballot = BallotWrapper(id)
return ballot, doc
-def document_ballot(request, name):
+def ballot_html(request, name):
ballot, doc = get_ballot(name)
return render_to_response('idrfc/doc_ballot.html', {'ballot':ballot, 'doc':doc}, context_instance=RequestContext(request))
@@ -308,3 +601,4 @@ def ballot_json(request, name):
response = HttpResponse(mimetype='text/plain')
response.write(json.dumps(ballot.dict(), indent=2))
return response
+
diff --git a/ietf/idrfc/views_edit.py b/ietf/idrfc/views_edit.py
index d3495355a..8429b775b 100644
--- a/ietf/idrfc/views_edit.py
+++ b/ietf/idrfc/views_edit.py
@@ -791,10 +791,8 @@ def add_comment(request, name):
@group_required('Area_Director', 'Secretariat', 'IANA')
def add_commentREDESIGN(request, name):
- """Add comment to Internet Draft."""
+ """Add comment to history of document."""
doc = get_object_or_404(Document, docalias__name=name)
- if not doc.get_state("draft-iesg"):
- raise Http404()
login = request.user.get_profile()
@@ -808,16 +806,16 @@ def add_commentREDESIGN(request, name):
e.desc = c
e.save()
- email_owner(request, doc, doc.ad, login,
- "A new comment added by %s" % login.name)
- return HttpResponseRedirect(doc.get_absolute_url())
+ if doc.type_id == "draft":
+ email_owner(request, doc, doc.ad, login,
+ "A new comment added by %s" % login.name)
+ return HttpResponseRedirect(urlreverse("doc_history", kwargs=dict(name=doc.name)))
else:
form = AddCommentForm()
return render_to_response('idrfc/add_comment.html',
dict(doc=doc,
- form=form,
- back_url=doc.get_absolute_url()),
+ form=form),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
diff --git a/ietf/idtracker/templatetags/ietf_filters.py b/ietf/idtracker/templatetags/ietf_filters.py
index 4b67b0d6f..e42994981 100644
--- a/ietf/idtracker/templatetags/ietf_filters.py
+++ b/ietf/idtracker/templatetags/ietf_filters.py
@@ -445,9 +445,9 @@ def ad_area(user):
def format_history_text(text):
"""Run history text through some cleaning and add ellipsis if it's too long."""
full = mark_safe(sanitize_html(keep_spacing(linebreaksbr(urlize(mark_safe(text))))))
- snipped = truncatewords_html(format_textarea(fill(text, 80)), 25)
- if snipped[-3:] == "...":
- return mark_safe(u'%s
[show all]
%s
' % (snipped, full))
+ snippet = truncatewords_html(format_textarea(text), 25)
+ if snippet[-3:] == "...":
+ return mark_safe(u'
%s[show all]
%s
' % (snippet, full))
return full
@register.filter
diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
index 69e8463aa..8e6224283 100644
--- a/ietf/iesg/views.py
+++ b/ietf/iesg/views.py
@@ -33,6 +33,9 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import codecs, re, os, glob
+import datetime
+import tarfile
+
from ietf.idtracker.models import IDInternal, InternetDraft,AreaGroup, Position, IESGLogin, Acronym
from django.views.generic.list_detail import object_list
from django.views.generic.simple import direct_to_template
@@ -50,8 +53,8 @@ from ietf.idrfc.utils import update_telechat
from ietf.ietfauth.decorators import group_required
from ietf.idtracker.templatetags.ietf_filters import in_group
from ietf.ipr.models import IprRfc, IprDraft, IprDetail
-import datetime
-import tarfile
+from ietf.doc.models import Document, TelechatDocEvent
+from ietf.group.models import Group
def date_threshold():
"""Return the first day of the month that is 185 days ago."""
@@ -208,6 +211,23 @@ def get_doc_sectionREDESIGN(id):
s = s + "1"
return s
+def get_wg_section(wg):
+ s = ""
+ charter_slug = None
+ if wg.charter:
+ charter_slug = wg.charter.get_state_slug()
+ if wg.state_id == "proposed":
+ if charter_slug == "intrev":
+ s = '411'
+ elif charter_slug == "iesgrev":
+ s = '412'
+ elif wg.state_id == "active":
+ if charter_slug == "intrev":
+ s = '421'
+ elif charter_slug == "iesgrev":
+ s = '422'
+ return s
+
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
get_doc_section = get_doc_sectionREDESIGN
@@ -253,15 +273,16 @@ def agenda_docs(date, next_agenda):
return res
def agenda_wg_actions(date):
- mapping = {12:'411', 13:'412',22:'421',23:'422'}
- matches = WGAction.objects.filter(agenda=1,telechat_date=date,category__in=mapping.keys()).order_by('category')
- res = {}
- for o in matches:
- section_key = "s"+mapping[o.category]
+ res = dict(("s%s%s%s" % (i, j, k), []) for i in range(2, 5) for j in range (1, 4) for k in range(1, 4))
+ charters = Document.objects.filter(type="charter", docevent__telechatdocevent__telechat_date=date).distinct()
+ for c in charters:
+ if c.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
+ continue
+
+ section_key = "s" + get_wg_section(c.group)
if section_key not in res:
res[section_key] = []
- area = AreaGroup.objects.get(group=o.group_acronym)
- res[section_key].append({'obj':o, 'area':str(area.area)})
+ res[section_key].append({'obj': c.group})
return res
def agenda_management_issues(date):
diff --git a/ietf/ietfauth/decorators.py b/ietf/ietfauth/decorators.py
index bd4aa3035..933338a43 100644
--- a/ietf/ietfauth/decorators.py
+++ b/ietf/ietfauth/decorators.py
@@ -71,30 +71,38 @@ def has_role(user, role_names):
if not user or not user.is_authenticated():
return False
- from ietf.person.models import Person
-
- try:
- person = user.get_profile()
- except Person.DoesNotExist:
- return False
+ if not hasattr(user, "roles_check_cache"):
+ user.roles_check_cache = {}
- role_qs = {
- "Area Director": Q(person=person, name__in=("pre-ad", "ad"), group__type="area", group__state="active"),
- "Secretariat": Q(person=person, name="secr", group__acronym="secretariat"),
- "IANA": Q(person=person, name="auth", group__acronym="iana"),
- "IAD": Q(person=person, name="admdir", group__acronym="ietf"),
- "IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
- "IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
- "WG Chair": Q(person=person,name="chair", group__type="wg", group__state="active"),
- "WG Secretary": Q(person=person,name="secr", group__type="wg", group__state="active"),
- }
+ key = frozenset(role_names)
+ if key not in user.roles_check_cache:
- filter_expr = Q()
- for r in role_names:
- filter_expr |= role_qs[r]
+ from ietf.person.models import Person
+ from ietf.group.models import Role
- from ietf.group.models import Role
- return bool(Role.objects.filter(filter_expr)[:1])
+ try:
+ person = user.get_profile()
+ except Person.DoesNotExist:
+ return False
+
+ role_qs = {
+ "Area Director": Q(person=person, name__in=("pre-ad", "ad"), group__type="area", group__state="active"),
+ "Secretariat": Q(person=person, name="secr", group__acronym="secretariat"),
+ "IANA": Q(person=person, name="auth", group__acronym="iana"),
+ "IAD": Q(person=person, name="admdir", group__acronym="ietf"),
+ "IETF Chair": Q(person=person, name="chair", group__acronym="ietf"),
+ "IAB Chair": Q(person=person, name="chair", group__acronym="iab"),
+ "WG Chair": Q(person=person,name="chair", group__type="wg", group__state="active"),
+ "WG Secretary": Q(person=person,name="secr", group__type="wg", group__state="active"),
+ }
+
+ filter_expr = Q()
+ for r in role_names:
+ filter_expr |= role_qs[r]
+
+ user.roles_check_cache[key] = bool(Role.objects.filter(filter_expr)[:1])
+
+ return user.roles_check_cache[key]
def role_required(*role_names):
"""View decorator for checking that the user is logged in and
diff --git a/ietf/liaisons/forms.py b/ietf/liaisons/forms.py
index 0ed432eb6..0cfc391f7 100644
--- a/ietf/liaisons/forms.py
+++ b/ietf/liaisons/forms.py
@@ -361,7 +361,7 @@ class OutgoingLiaisonForm(LiaisonForm):
person = self.fake_person or self.person
for i in self.hm.get_entities_for_person(person):
all_entities += i[1]
- # If the from entity is one in wich the user has full privileges the to entity could be anyone
+ # If the from entity is one in which the user has full privileges the to entity could be anyone
if from_code in [i[0] for i in all_entities]:
return to_code
sdo_codes = ['sdo_%s' % i.sdo.pk for i in person.liaisonmanagers_set.all().distinct()]
diff --git a/ietf/name/fixtures/names.xml b/ietf/name/fixtures/names.xml
index 3978aa433..dad67e3a5 100644
--- a/ietf/name/fixtures/names.xml
+++ b/ietf/name/fixtures/names.xml
@@ -5,36 +5,49 @@
True
1
+
False
No Objection
True
2
+ False
Discuss
True
3
+ False
+
+
+ Block
+
+ True
+ 3
+ True
Abstain
True
4
+ False
Recuse
True
5
+ False
No Record
True
6
+ False
Conflicts with
@@ -54,8 +67,8 @@
True
0
-
- Replaces
+
+ Obsoletes
True
0
@@ -66,8 +79,8 @@
True
0
-
- Obsoletes
+
+ Replaces
True
0
@@ -78,6 +91,30 @@
True
0
+
+ IANA coordination
+ RFC-Editor/IANA Registration Coordination
+ True
+ 0
+
+
+ Holding for references
+ Holding for normative reference
+ True
+ 0
+
+
+ Missing references
+ Awaiting missing normative reference
+ True
+ 0
+
+
+ Has errata
+
+ True
+ 0
+
Review by RFC Editor
@@ -90,24 +127,12 @@
True
0
-
- Expired tombstone
-
- True
- 0
-
Approved in minute
True
0
-
- Has errata
-
- True
- 0
-
Shepherd Needed
@@ -246,6 +271,12 @@
True
11
+
+ Charter
+
+ True
+ 0
+
Agenda
@@ -276,12 +307,6 @@
True
0
-
- Charter
-
- True
- 0
-
No
@@ -396,6 +421,12 @@
True
0
+
+ IRTF
+
+ True
+ 0
+
Proposed Standard
@@ -405,7 +436,7 @@
Draft Standard
- True
+ False
2
@@ -480,8 +511,8 @@
True
0
-
- Ex Area Director
+
+ Incoming Area Director
True
0
@@ -582,6 +613,12 @@
True
0
+
+ Deleted
+
+ True
+ 0
+
Internet Standard
@@ -630,32 +667,38 @@
True
0
-
- Legacy
-
- True
- 0
-
IETF
-
+ IETF stream
True
- 0
+ 1
ISE
- Independent submission editor stream
+ Independent Submission Editor stream
True
- 0
-
-
- IAB
-
- True
- 0
+ 2
IRTF
+ Independent Submission Editor stream
+ True
+ 3
+
+
+ IAB
+ IAB stream
+ True
+ 4
+
+
+ Legacy
+ Legacy stream
+ True
+ 5
+
+
+ Other
True
0
@@ -697,10 +740,10 @@
RFC Editor state
- WG state
+ IETF state
- RG state
+ IRTF state
ISE state
@@ -919,7 +962,7 @@
True
The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as "discuss" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion.
20
-
+
draft-iesg
@@ -937,7 +980,7 @@
True
The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message.
27
-
+
draft-iesg
@@ -946,7 +989,7 @@
True
The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor.
30
-
+
draft-iesg
@@ -1581,4 +1624,31 @@
2
-
+
+ charter
+ r-extrev
+ Ready for external review
+ Is this charter ready for external review?
+ True
+ 0
+
+
+
+ charter
+ approve
+ Approve
+ Do we approve of this charter?
+ True
+ 0
+
+
+
+ draft
+ approve
+ Approve
+
+ True
+ 0
+
+
+
\ No newline at end of file
diff --git a/ietf/name/generate_fixtures.py b/ietf/name/generate_fixtures.py
index c3307b049..c4b8bed35 100755
--- a/ietf/name/generate_fixtures.py
+++ b/ietf/name/generate_fixtures.py
@@ -37,9 +37,10 @@ for n in dir(ietf.name.models):
objects.extend(model.objects.all())
-import ietf.doc.models # FIXME
+import ietf.doc.models # also pick some other name-like types while we're at it
objects += ietf.doc.models.StateType.objects.all()
objects += ietf.doc.models.State.objects.all()
+objects += ietf.doc.models.BallotType.objects.all()
output("names", objects)
diff --git a/ietf/name/migrations/0001_initial.py b/ietf/name/migrations/0001_initial.py
new file mode 100644
index 000000000..1e0a987ca
--- /dev/null
+++ b/ietf/name/migrations/0001_initial.py
@@ -0,0 +1,375 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'GroupStateName'
+ db.create_table('name_groupstatename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['GroupStateName'])
+
+ # Adding model 'GroupTypeName'
+ db.create_table('name_grouptypename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['GroupTypeName'])
+
+ # Adding model 'RoleName'
+ db.create_table('name_rolename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['RoleName'])
+
+ # Adding model 'StreamName'
+ db.create_table('name_streamname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['StreamName'])
+
+ # Adding model 'DocRelationshipName'
+ db.create_table('name_docrelationshipname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['DocRelationshipName'])
+
+ # Adding model 'DocTypeName'
+ db.create_table('name_doctypename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['DocTypeName'])
+
+ # Adding model 'DocTagName'
+ db.create_table('name_doctagname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['DocTagName'])
+
+ # Adding model 'StdLevelName'
+ db.create_table('name_stdlevelname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['StdLevelName'])
+
+ # Adding model 'IntendedStdLevelName'
+ db.create_table('name_intendedstdlevelname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['IntendedStdLevelName'])
+
+ # Adding model 'DocReminderTypeName'
+ db.create_table('name_docremindertypename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['DocReminderTypeName'])
+
+ # Adding model 'BallotPositionName'
+ db.create_table('name_ballotpositionname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['BallotPositionName'])
+
+ # Adding model 'GroupBallotPositionName'
+ db.create_table('name_groupballotpositionname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['GroupBallotPositionName'])
+
+ # Adding model 'MeetingTypeName'
+ db.create_table('name_meetingtypename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['MeetingTypeName'])
+
+ # Adding model 'SessionStatusName'
+ db.create_table('name_sessionstatusname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['SessionStatusName'])
+
+ # Adding model 'TimeSlotTypeName'
+ db.create_table('name_timeslottypename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['TimeSlotTypeName'])
+
+ # Adding model 'ConstraintName'
+ db.create_table('name_constraintname', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['ConstraintName'])
+
+ # Adding model 'LiaisonStatementPurposeName'
+ db.create_table('name_liaisonstatementpurposename', (
+ ('slug', self.gf('django.db.models.fields.CharField')(max_length=8, primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('desc', self.gf('django.db.models.fields.TextField')(blank=True)),
+ ('used', self.gf('django.db.models.fields.BooleanField')(default=True)),
+ ('order', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('name', ['LiaisonStatementPurposeName'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'GroupStateName'
+ db.delete_table('name_groupstatename')
+
+ # Deleting model 'GroupTypeName'
+ db.delete_table('name_grouptypename')
+
+ # Deleting model 'RoleName'
+ db.delete_table('name_rolename')
+
+ # Deleting model 'StreamName'
+ db.delete_table('name_streamname')
+
+ # Deleting model 'DocRelationshipName'
+ db.delete_table('name_docrelationshipname')
+
+ # Deleting model 'DocTypeName'
+ db.delete_table('name_doctypename')
+
+ # Deleting model 'DocTagName'
+ db.delete_table('name_doctagname')
+
+ # Deleting model 'StdLevelName'
+ db.delete_table('name_stdlevelname')
+
+ # Deleting model 'IntendedStdLevelName'
+ db.delete_table('name_intendedstdlevelname')
+
+ # Deleting model 'DocReminderTypeName'
+ db.delete_table('name_docremindertypename')
+
+ # Deleting model 'BallotPositionName'
+ db.delete_table('name_ballotpositionname')
+
+ # Deleting model 'GroupBallotPositionName'
+ db.delete_table('name_groupballotpositionname')
+
+ # Deleting model 'MeetingTypeName'
+ db.delete_table('name_meetingtypename')
+
+ # Deleting model 'SessionStatusName'
+ db.delete_table('name_sessionstatusname')
+
+ # Deleting model 'TimeSlotTypeName'
+ db.delete_table('name_timeslottypename')
+
+ # Deleting model 'ConstraintName'
+ db.delete_table('name_constraintname')
+
+ # Deleting model 'LiaisonStatementPurposeName'
+ db.delete_table('name_liaisonstatementpurposename')
+
+
+ models = {
+ 'name.ballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.constraintname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docrelationshipname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docremindertypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctagname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupBallotPositionName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupstatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.grouptypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.intendedstdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.liaisonstatementpurposename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.meetingtypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.rolename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.sessionstatusname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.stdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.streamname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.timeslottypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ }
+ }
+
+ complete_apps = ['name']
diff --git a/ietf/name/migrations/0002_auto__add_field_ballotpositionname_blocking.py b/ietf/name/migrations/0002_auto__add_field_ballotpositionname_blocking.py
new file mode 100644
index 000000000..5355e726d
--- /dev/null
+++ b/ietf/name/migrations/0002_auto__add_field_ballotpositionname_blocking.py
@@ -0,0 +1,161 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'BallotPositionName.blocking'
+ db.add_column('name_ballotpositionname', 'blocking', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'BallotPositionName.blocking'
+ db.delete_column('name_ballotpositionname', 'blocking')
+
+
+ models = {
+ 'name.ballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'},
+ 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.constraintname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docrelationshipname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.docremindertypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctagname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.doctypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupballotpositionname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupBallotPositionName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.groupstatename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.grouptypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.intendedstdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.liaisonstatementpurposename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.meetingtypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.rolename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.sessionstatusname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.stdlevelname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.streamname': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'name.timeslottypename': {
+ 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'},
+ 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}),
+ 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ }
+ }
+
+ complete_apps = ['name']
diff --git a/ietf/name/migrations/__init__.py b/ietf/name/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/ietf/name/models.py b/ietf/name/models.py
index 50f914702..672b57004 100644
--- a/ietf/name/models.py
+++ b/ietf/name/models.py
@@ -42,7 +42,8 @@ class IntendedStdLevelName(NameModel):
class DocReminderTypeName(NameModel):
"Stream state"
class BallotPositionName(NameModel):
- """ Yes, No Objection, Abstain, Discuss, Recuse """
+ """ Yes, No Objection, Abstain, Discuss, Block, Recuse """
+ blocking = models.BooleanField(default=False)
class GroupBallotPositionName(NameModel):
""" Yes, No, Block, Abstain """
class MeetingTypeName(NameModel):
diff --git a/ietf/person/forms.py b/ietf/person/forms.py
new file mode 100644
index 000000000..039d0cb1b
--- /dev/null
+++ b/ietf/person/forms.py
@@ -0,0 +1,31 @@
+from django.utils import simplejson
+from django.utils.html import escape
+from django.utils.functional import lazy
+from django import forms
+from django.core.urlresolvers import reverse as urlreverse
+
+from ietf.person.models import *
+
+def json_emails(emails):
+ return simplejson.dumps([{"id": e.address + "", "name": escape(u"%s <%s>" % (e.person.name, e.address))} for e in emails])
+
+class EmailsField(forms.CharField):
+ def __init__(self, *args, **kwargs):
+ kwargs["max_length"] = 1000
+ if not "help_text" in kwargs:
+ kwargs["help_text"] = "Type in name to search for person"
+ super(EmailsField, self).__init__(*args, **kwargs)
+ self.widget.attrs["class"] = "emails-field"
+ self.widget.attrs["data-ajax-url"] = lazy(urlreverse, str)("ajax_search_emails") # make this lazy to prevent initialization problem
+
+ def prepare_value(self, value):
+ if not value:
+ return ""
+ if isinstance(value, str):
+ return value
+ return json_emails(value)
+
+ def clean(self, value):
+ value = super(EmailsField, self).clean(value)
+ return Email.objects.filter(address__in=[x.strip() for x in value.split(",") if x.strip()]).select_related("person")
+
diff --git a/ietf/person/urls.py b/ietf/person/urls.py
new file mode 100644
index 000000000..03a0ecc2a
--- /dev/null
+++ b/ietf/person/urls.py
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('',
+ (r'^search/$', "ietf.person.views.ajax_search_emails", None, 'ajax_search_emails'),
+)
diff --git a/ietf/person/views.py b/ietf/person/views.py
new file mode 100644
index 000000000..be305361c
--- /dev/null
+++ b/ietf/person/views.py
@@ -0,0 +1,8 @@
+from django.http import Http404, HttpResponse
+
+from ietf.person.models import *
+from ietf.person.forms import json_emails
+
+def ajax_search_emails(request):
+ emails = Email.objects.filter(person__alias__name__istartswith=request.GET.get('q','')).order_by('person__name').distinct()
+ return HttpResponse(json_emails(emails), mimetype='application/json')
diff --git a/ietf/templates/base_leftmenu.html b/ietf/templates/base_leftmenu.html
index 55f1d89fc..544ba0c83 100644
--- a/ietf/templates/base_leftmenu.html
+++ b/ietf/templates/base_leftmenu.html
@@ -72,7 +72,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Active WGs
- {# FIXME: wgcharter Proposed WGs #}
+ Chartering WGs
Concluded WGs
Non-WG Lists
@@ -89,7 +89,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Upcoming
Other Documents
- {# FIXME: wgcharter WG Charters #}
IPR Disclosures
Liaison Statements
IESG Agenda
diff --git a/ietf/templates/feeds/group_description.html b/ietf/templates/feeds/group_description.html
new file mode 100644
index 000000000..8cb921700
--- /dev/null
+++ b/ietf/templates/feeds/group_description.html
@@ -0,0 +1,3 @@
+{# Copyright The IETF Trust 2011, All Rights Reserved #}
+{% load ietf_filters %}
+{{ obj.desc|safe|linebreaksbr }}
diff --git a/ietf/templates/feeds/wg_charter_description.html b/ietf/templates/feeds/wg_charter_description.html
deleted file mode 100644
index a171e5675..000000000
--- a/ietf/templates/feeds/wg_charter_description.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{# Copyright The IETF Trust 2011, All Rights Reserved #}
-{% load ietf_filters %}
-{{ obj.info.text|safe }}
-
-{% with obj.group as wg %}
-WG name: {{ wg.name }}
-WG acronym: {{ wg.acronym }}
-IETF area: {{ wg.parent|default:"-" }}
-
-WG chairs: {% for n in obj.chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}
-WG secretaries: {% for n in obj.secr %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}
-WG technical advisors: {% for n in obj.techadv %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}
-Assigned AD: {{ wg.ad }}
-
-Mailing list: {{ wg.list_email }}
-Mailing list subscribe {{ wg.list_subscribe }}
-Mailing list archive: {{ wg.list_archive }}
-Other web sites: {% for a in wg.groupurl_set.all %}{{ a.url }} {% if a.name %}({{ a.name }}){% endif %}{% if not forloop.last %}, {% endif %}{% endfor %}
-
-WG State: {{ wg.state|safe }}
-Charter State: {{ wg.charter.charter_state|safe }}
-
-{% if obj.rev %}
-{{ obj.charter|safe }}
-{% else %}
-The WG does not yet have a charter
-{% endif %}
-{% endwith %}
diff --git a/ietf/templates/feeds/wg_charter_title.html b/ietf/templates/feeds/wg_charter_title.html
deleted file mode 100644
index 29cd881f9..000000000
--- a/ietf/templates/feeds/wg_charter_title.html
+++ /dev/null
@@ -1 +0,0 @@
-{% load ietf_filters %}WG Charter for {{obj.group.name}} ({{ obj.group.acronym|safe }})
diff --git a/ietf/templates/idrfc/add_comment.html b/ietf/templates/idrfc/add_comment.html
index 54208d886..b67767d56 100644
--- a/ietf/templates/idrfc/add_comment.html
+++ b/ietf/templates/idrfc/add_comment.html
@@ -24,7 +24,7 @@ form.add-comment .actions {
- Back
+ Back
diff --git a/ietf/templates/idrfc/ballot_writeup.txt b/ietf/templates/idrfc/ballot_writeup.txt
index 2ffd650f2..df5814020 100644
--- a/ietf/templates/idrfc/ballot_writeup.txt
+++ b/ietf/templates/idrfc/ballot_writeup.txt
@@ -1,4 +1,4 @@
-
+{% autoescape off %}
Technical Summary
Relevant content can frequently be found in the abstract
@@ -47,4 +47,4 @@ IESG Note
IANA Note
(Insert IANA Note here or remove section)
-
+{% endautoescape%}
diff --git a/ietf/templates/idrfc/change_notice.txt b/ietf/templates/idrfc/change_notice.txt
index 9f2b9d28a..bc6e62b62 100644
--- a/ietf/templates/idrfc/change_notice.txt
+++ b/ietf/templates/idrfc/change_notice.txt
@@ -1,6 +1,8 @@
+{% autoescape off %}
Please DO NOT reply to this email.
I-D: {{ doc.file_tag|safe }}
ID Tracker URL: {{ url }}
{{ text }}
+{% endautoescape%}
diff --git a/ietf/templates/idrfc/doc_ballot.html b/ietf/templates/idrfc/doc_ballot.html
index af7f74e99..9a985088f 100644
--- a/ietf/templates/idrfc/doc_ballot.html
+++ b/ietf/templates/idrfc/doc_ballot.html
@@ -1,6 +1,6 @@
-{% comment %}
+{% comment %}{% endcomment %}
{% load ietf_filters %}
@@ -86,12 +86,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% ifequal pos.position "Discuss" %}
Discuss ({{pos.discuss_date}})
-{{pos.discuss_text|fill:"80"|escape }}
+{{pos.discuss_text|wrap_text:80|escape }}
{% endifequal %}
{% if pos.comment_text %}
Comment ({{pos.comment_date}})
-{{pos.comment_text|fill:"80"|escape }}
+{{pos.comment_text|wrap_text:80|escape }}
{% endif %}
{% endfor %}
diff --git a/ietf/templates/idrfc/doc_history.html b/ietf/templates/idrfc/doc_history.html
index 8ec6b72e1..220770ac0 100644
--- a/ietf/templates/idrfc/doc_history.html
+++ b/ietf/templates/idrfc/doc_history.html
@@ -62,13 +62,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{% else %}
{% if c.info.dontmolest %}
{{ c.info.text|safe }}
{% else %}
-{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
+{{ c.info.text|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }}
{% endif %}
{% endif %}
diff --git a/ietf/templates/idrfc/document_ballot.html b/ietf/templates/idrfc/document_ballot.html
new file mode 100644
index 000000000..44d1e7ad3
--- /dev/null
+++ b/ietf/templates/idrfc/document_ballot.html
@@ -0,0 +1,19 @@
+{% extends "base.html" %}
+
+{% load ietf_filters %}
+
+{% block title %}Ballot for {{ doc.name }}-{{ doc.rev }}{% endblock %}
+
+{% block pagehead %}
+
+{% endblock %}
+
+{% block content %}
+{{ top|safe }}
+
+{{ ballot_content|safe }}
+
+{% endblock content %}
+
+{% block content_end %}
+{% endblock content_end %}
diff --git a/ietf/templates/idrfc/document_ballot_content.html b/ietf/templates/idrfc/document_ballot_content.html
new file mode 100644
index 000000000..673cafa8c
--- /dev/null
+++ b/ietf/templates/idrfc/document_ballot_content.html
@@ -0,0 +1,67 @@
+{% load ietf_filters %}
+
+
+
+
+
+{% if all_ballots and all_ballots|length > 1 %}
+
+{% endif %}
+
+
{{ ballot.ballot_type.question }}
+
+
Summary: {{ summary }}
+
+{% for p in text_positions %}
+
+
+{% if p.discuss %}
+
{{ p.pos.name }} ({{ p.discuss_time|date:"Y-m-d" }})
+
{{ p.discuss|wrap_text:80|escape }}
+{% endif %}
+
+{% if p.comment %}
+
Comment ({{ p.comment_time|date:"Y-m-d" }})
+
{{ p.comment|wrap_text:80|escape }}
+{% endif %}
+{% endfor %}
+
+
diff --git a/ietf/templates/idrfc/document_charter.html b/ietf/templates/idrfc/document_charter.html
new file mode 100644
index 000000000..89478ba42
--- /dev/null
+++ b/ietf/templates/idrfc/document_charter.html
@@ -0,0 +1,116 @@
+{% extends "base.html" %}
+
+{% load ietf_filters %}
+
+{% block title %}{{ doc.canonical_name }}-{{ doc.rev }}{% endblock %}
+
+{% block pagehead %}
+
+{% endblock %}
+
+{% block content %}
+{{ top|safe }}
+
+
+ Snapshots:
+
+ {% for rev in revisions %}
+ {{ rev }}
+ {% endfor %}
+
+
+
+
+
+Other versions: plain text
+
+Charter {{ doc.canonical_name }}-{{ doc.rev }}
+
+{% if user|has_role:"Area Director,Secretariat" and chartering and group.state_id != "conclude" %}
+Change charter text
+{% endif %}
+
+
+{% if doc.rev %}
+
+{{ content|fill:"80"|safe|linebreaksbr|keep_spacing|sanitize_html|safe }}
+
+{% endif %}
+
+{% endblock %}
+
diff --git a/ietf/templates/idrfc/document_history.html b/ietf/templates/idrfc/document_history.html
new file mode 100644
index 000000000..8c2e789da
--- /dev/null
+++ b/ietf/templates/idrfc/document_history.html
@@ -0,0 +1,80 @@
+{% extends "base.html" %}
+
+{% load ietf_filters %}
+
+{% block title %}History for {{ doc.name }}-{{ doc.rev }}{% endblock %}
+
+{% block pagehead %}
+
+{% endblock %}
+
+{% block content %}
+{{ top|safe }}
+
+{% if diff_revisions %}
+
+{% endif %}
+
+Document history
+{% if user|has_role:"Area Director,Secretariat,IANA" %}
+
+{% endif %}
+
+
+
+ Date Version By Text
+
+ {% for e in events %}
+
+ {{ e.time|date:"Y-m-d" }}
+ {{ e.rev }}
+ {{ e.by|escape }}
+ {{ e.desc|format_history_text }}
+
+
+ {% endfor %}
+
+{% endblock content %}
+
+{% block content_end %}
+
+{% endblock content_end %}
diff --git a/ietf/templates/idrfc/document_top.html b/ietf/templates/idrfc/document_top.html
new file mode 100644
index 000000000..2b7383cf0
--- /dev/null
+++ b/ietf/templates/idrfc/document_top.html
@@ -0,0 +1,9 @@
+{{ doc.title }} {{ name }}
+
+
+
+ {% for name, t, url, active in tabs %}
+ {{ name }}
+ {% endfor %}
+
+
diff --git a/ietf/templates/idrfc/document_writeup.html b/ietf/templates/idrfc/document_writeup.html
new file mode 100644
index 000000000..b125ed80c
--- /dev/null
+++ b/ietf/templates/idrfc/document_writeup.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block title %}Writeups for {{ doc.name }}-{{ doc.rev }}{% endblock %}
+
+{% block pagehead %}
+
+{% endblock %}
+
+{% block content %}
+{{ top|safe }}
+
+{% for title, text, url in writeups %}
+
+
{{ title }}
+
+{% if can_edit %}
Edit {{ title }} {% endif %}
+
+
+{{ text }}
+
+
+{% endfor%}
+
+{% endblock content %}
diff --git a/ietf/templates/idrfc/edit_positionREDESIGN.html b/ietf/templates/idrfc/edit_positionREDESIGN.html
index daab1574b..232812562 100644
--- a/ietf/templates/idrfc/edit_positionREDESIGN.html
+++ b/ietf/templates/idrfc/edit_positionREDESIGN.html
@@ -45,13 +45,15 @@ form.position-form .comment-text {
+ {% if doc.type_id == "draft" %}
{% if ballot_deferred %} {% else %} {% endif %}
+ {% endif %}