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'' % (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 + '' - res = res + '
' - my = None - else: - 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('") + res.append("
' % c) + + 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]
' % (snipped, full)) + snippet = truncatewords_html(format_textarea(text), 25) + if snippet[-3:] == "...": + return mark_safe(u'
%s[show all]
' % (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.
    {{ c.info.textSnippet|safe }}
    [show all] {% 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 editable and user|has_role:"Area Director,Secretariat" %} +
    +{% if user|has_role:"Area Director" %} + +{% endif %} + +{% if doc.type_id == "draft" %} +
    +{% if deferred %} + Undefer ballot +
    Ballot deferred by {{ deferred.by }} on {{ deferred.time|date:"Y-m-d" }}.
    +{% else %} + Defer ballot +{% endif %} +
    +{% endif %} + +
    +{% endif %} + +{% for n, positions in position_groups %} +
    +
    {{ n.name }}
    + {% for p in positions %} +
    {% if p.old_ad %}[{% endif %}{{ p.ad.plain_name }}{% if p.old_ad %}]{% endif %}{% if p.comment_text or p.discuss_text %} *{% endif %}
    + {% if p.old_positions %}
    (was {{ p.old_positions|join:", " }})
    {% endif %} + {% empty %} + none + {% endfor %} +
    +{% endfor %} + +
    + +
    + +{% if all_ballots and all_ballots|length > 1 %} +
    + Other ballots: +{% for b in all_ballots %} +{{ b.ballot_type.name }} ({{ b.time|date:"Y-m-d" }}) +{% endfor %} +
    +{% endif %} + +

    {{ ballot.ballot_type.question }}

    + +

    Summary: {{ summary }}

    + +{% for p in text_positions %} +

    {% if p.old_ad %}[{% endif %}{{ p.ad.plain_name }}{% if p.old_ad %}]{% endif %}

    + +{% 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 %} + +
    + +
    +
    + {% if snapshot %}Snapshot of{% endif %} + {% if doc.get_state_slug != "approved" %}Proposed{% endif %} + Charter for "{{ group.name }}" + ({{ group.acronym }}) {{ group.type.name }} +
    + + + + + + + + + + + + {% if chartering and group.comments %} + + {% if chartering == "initial" %}{% endif %} + {% if chartering == "rechartering" %}{% endif %} + + + {% endif %} + + + + + + + +
    WG State:{{ group.state.name }}
    Charter State: +
    + {{ doc.get_state.name }} + {% if chartering == "initial" %}(Initial Chartering){% endif %} + {% if chartering == "rechartering" %}(Rechartering){% endif %} + + {% if not snapshot and user|has_role:"Area Director,Secretariat" %} + + + {% if chartering %} + - Abandon effort + + {% if request.user|has_role:"Secretariat" %} + - Approve charter + {% endif %} + + {% else %} + - Recharter + {% endif %} + + {% endif %} +
    + + {% if not snapshot and chartering %} +
    + {% if not telechat %}Not on agenda of IESG telechat{% else %}On agenda of {{ telechat.telechat_date|date:"Y-m-d" }} IESG telechat{% endif %} + {% if user|has_role:"Area Director,Secretariat" %} + - Change + {% endif %} +
    + + {% if ballot_summary %} +
    + ({{ ballot_summary }}) +
    + {% endif %} + + {% endif %} +
    Reason for chartering:Reason for rechartering:{{ group.comments }}

    Last updated: {{ doc.time|date:"Y-m-d" }}

    + +
    + Atom feed +
    +
    + +

    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 %} +
    +

    Diffs

    + +
    + + + + + + + + +
    + + + + + + +
    + + +
    +
    +
    +{% endif %} + +

    Document history

    +{% if user|has_role:"Area Director,Secretariat,IANA" %} + +{% endif %} + + + + + + {% for e in events %} + + + + + + + {% endfor %} +
    DateVersionByText
    {{ e.time|date:"Y-m-d" }}{{ e.rev }}{{ e.by|escape }}{{ e.desc|format_history_text }} +
    +{% 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 %}
    -
    + diff --git a/ietf/templates/wgcharter/date_column.html b/ietf/templates/wgcharter/date_column.html deleted file mode 100644 index 515e8d80a..000000000 --- a/ietf/templates/wgcharter/date_column.html +++ /dev/null @@ -1,5 +0,0 @@ -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} -{% load ietf_filters %}{{ wg.time|date:"Y-m-d" }} - diff --git a/ietf/templates/wgcharter/edit_position.html b/ietf/templates/wgcharter/edit_position.html deleted file mode 100644 index daa98284c..000000000 --- a/ietf/templates/wgcharter/edit_position.html +++ /dev/null @@ -1,71 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Change position for {{ ad.plain_name }} on {{ wg.acronym }}{% endblock %} - -{% block morecss %} -form.position-form .position ul { - padding: 0; - margin: 0; -} -form.position-form .position li { - list-style-type: none; - float: left; - padding-right: 10px; -} -form.position-form .last-edited { - font-style: italic; -} -form.position-form .block_comment { - padding-top: 20px -} -form.position-form #id_block_comment, -form.position-form #id_comment { - width: 700px; - height: 250px; -} -form.position-form .comment { - margin-top: 20px; -} -{% endblock %} - -{% block content %} -

    Change position for {{ ad.plain_name }} on {{ wg.acronym }}

    - -
    -
    - {{ form.position }} - - - - -
    - -
    - -
    -
    - {{ form.block_comment.label_tag }}: - {% if old_pos and old_pos.block_comment_time %}(last edited {{ old_pos.block_comment_time }}){% endif %} -
    - {{ form.block_comment.errors }} - {{ form.block_comment }} -
    - -
    - {{ form.comment.label_tag }}: - {% if old_pos and old_pos.comment_time %}(last edited {{ old_pos.comment_time }}){% endif %} -
    - {{ form.comment }} - -
    - Back -
    - - {{ form.return_to_url }} - -
    -{% endblock %} - -{% block content_end %} - -{% endblock %} diff --git a/ietf/templates/wgcharter/edit_telechat_date.html b/ietf/templates/wgcharter/edit_telechat_date.html new file mode 100644 index 000000000..390a197b2 --- /dev/null +++ b/ietf/templates/wgcharter/edit_telechat_date.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} + +{% block title %} +Set Telechat Date for {{ doc.name }} +{% endblock %} + +{% block morecss %} +form.telechat-date td.actions { + padding-top: 1em; +} +{% endblock %} + +{% block content %} +{% load ietf_filters %} +

    Set Telechat Date for {{ doc.name }}

    + +
    + + {{ form.as_table }} + + + + +
    + Back + +
    +
    +{% endblock %} diff --git a/ietf/templates/wgcharter/email_secretariat.txt b/ietf/templates/wgcharter/email_secretariat.txt index d489456fa..140714df6 100644 --- a/ietf/templates/wgcharter/email_secretariat.txt +++ b/ietf/templates/wgcharter/email_secretariat.txt @@ -1,5 +1,6 @@ -{% autoescape off %} -{{ text }} +{% load ietf_filters %}{% autoescape off %} +{{ text|fill:70 }} -WG Record URL: {{ url }} +WG: {{ wg_url }} +Charter: {{ charter_url }} {% endautoescape %} diff --git a/ietf/templates/wgcharter/in_process.html b/ietf/templates/wgcharter/in_process.html deleted file mode 100644 index e0b628bbc..000000000 --- a/ietf/templates/wgcharter/in_process.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends "base.html" %} -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% block title %}Working Groups in IESG process{% endblock %} - -{% block content %} -

    Working Groups in IESG process

    - -

    -Start new WG charter effort -

    - -{% if not recs %} -

    No WGs match your query.

    -{% else %} - - - {% for hdr in meta.hdrs %} - {% include "wgcharter/table_header.html" %} - {% endfor %} - -{% for wg in recs %} -{% include "wgcharter/search_result_row.html" %} -{% endfor %} -
    -{% endif %} -{% endblock content %} diff --git a/ietf/templates/wgcharter/issue_ballot_mail.txt b/ietf/templates/wgcharter/issue_ballot_mail.txt index 3141ed79c..b901def38 100644 --- a/ietf/templates/wgcharter/issue_ballot_mail.txt +++ b/ietf/templates/wgcharter/issue_ballot_mail.txt @@ -1,9 +1,9 @@ {% autoescape off %}To: Internet Engineering Steering Group From: IESG Secretary Reply-To: IESG Secretary -Subject: Evaluation: {{ charter.chartered_group }} ({{ charter.chartered_group.acronym }}) +Subject: Evaluation: {{ doc.name }} -{% filter wordwrap:73 %}Evaluation for {{ charter.chartered_group }} ({{ charter.chartered_group.acronym }}) can be found at {{ charter_url }} +{% filter wordwrap:73 %}Evaluation for {{ doc.title }} can be found at {{ doc_url }} {% endfilter %} Please return the full line with your position. @@ -20,8 +20,8 @@ BLOCKING AND NON-BLOCKING COMMENTS ================================== {% filter wordwrap:79 %}{% for p in ad_feedback %}{{ p.ad }}: -{% if p.block_comment %}Blocking comment [{{ p.time }}]: -{{ p.block_comment }} +{% if p.discuss %}Blocking comment [{{ p.time }}]: +{{ p.discuss }} {% endif %}{% if p.comment %}Comment [{{ p.time }}]: {{ p.comment }} diff --git a/ietf/templates/wgcharter/review_text.txt b/ietf/templates/wgcharter/review_text.txt index 4611f2a5a..f56bbc4b2 100644 --- a/ietf/templates/wgcharter/review_text.txt +++ b/ietf/templates/wgcharter/review_text.txt @@ -1,8 +1,8 @@ {% load ietf_filters %}{% autoescape off %}From: The IESG To: IETF-Announce -Subject: WG Review: {{ wg.name }} ({{wg.acronym}}) +Subject: WG Review: {{ wg.name }} ({{ wg.acronym }}) -{% filter wordwrap:73 %}{% ifequal review_type "new" %}A new IETF working group has been proposed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal review_type "recharter" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF is undergoing rechartering.{% endifequal %} The IESG has not made any determination yet. The following draft charter was submitted, and is provided for informational purposes only. Please send your comments to the IESG mailing list (iesg at ietf.org) by {{ info.bydate }}. +{% filter wordwrap:73 %}{% ifequal review_type "new" %}A new IETF working group has been proposed in the {{ wg.parent.name }}.{% endifequal %}{% ifequal review_type "recharter" %}The {{ wg.name }} ({{wg.acronym}}) working group in the {{ wg.parent.name }} of the IETF is undergoing rechartering.{% endifequal %} The IESG has not made any determination yet. The following draft charter was submitted, and is provided for informational purposes only. Please send your comments to the IESG mailing list (iesg at ietf.org) by {{ review_date }}. {% include "wgcharter/wg_info.txt" %} diff --git a/ietf/templates/wgcharter/search_form.html b/ietf/templates/wgcharter/search_form.html deleted file mode 100644 index d64ea1293..000000000 --- a/ietf/templates/wgcharter/search_form.html +++ /dev/null @@ -1,144 +0,0 @@ -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -
    - -
    - {{ form.nameacronym }} -
    -
    - - - - - -
    {{ form.inprocess }} WGs (in chartering process)
    {{ form.active }} WGs (approved charter)
    {{ form.concluded }} WGs (concluded or not under review)
    -
    - - Advanced - -
    - Additional search criteria: - -
    - {{ form.state }} :: {{ form.charter_state }} -
    -
    - {{ form.ad }} -
    -
    - {{ form.area }} -
    -
    - {{ form.anyfield }} -
    -
    - {{ form.eacronym }} -
    -
    - -
    - - - -
    - - - -
    - - diff --git a/ietf/templates/wgcharter/search_main.html b/ietf/templates/wgcharter/search_main.html deleted file mode 100644 index 5c3eaf2db..000000000 --- a/ietf/templates/wgcharter/search_main.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base.html" %} -{% comment %} -Copyright 2011 The IETF Trust. All rights reserved. -{% endcomment %} - -{% block title %}Working Group Charters{% endblock %} - -{% block content %} -

    Working Group Charters

    - -
    -{% include "wgcharter/search_form.html" %} -
    - -
    -{% if meta.searching %} -{% include "wgcharter/search_results.html" %} -{% endif %} -
    -{% endblock content %} -{% block scripts %} -YAHOO.util.Event.onContentReady("search_submit_button", function () { - var oButton = new YAHOO.widget.Button("search_submit_button", {}); -}); -{% endblock scripts %} diff --git a/ietf/templates/wgcharter/search_results.html b/ietf/templates/wgcharter/search_results.html deleted file mode 100644 index 83e863e74..000000000 --- a/ietf/templates/wgcharter/search_results.html +++ /dev/null @@ -1,22 +0,0 @@ -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% if meta.max %} -

    Too many WGs match the query! Returning partial result only.

    -{% endif %} -{% if not recs %} -

    No WGs match your query.

    -{% else %} - - - {% for hdr in meta.hdrs %} - {% include "wgcharter/table_header.html" %} - {% endfor %} - -{% for wg in recs %} -{% include "wgcharter/search_result_row.html" %} -{% endfor %} - -
    -{% endif %} diff --git a/ietf/templates/wgcharter/send_ballot_comment.html b/ietf/templates/wgcharter/send_ballot_comment.html deleted file mode 100644 index 9539494f5..000000000 --- a/ietf/templates/wgcharter/send_ballot_comment.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends "base.html" %} -{% load ietf_filters %} -{% block title %}Send ballot position email for {{ ad }}{% endblock %} - -{% block morecss %} -form.send-ballot pre { - margin: 0; - padding: 4px; - border-top: 4px solid #eee; - border-bottom: 4px solid #eee; -} -{% endblock %} - -{% block content %} -

    Send ballot position email for {{ ad }}

    - -
    - - - - - - - - - - - - - - - - -
    From: {{ frm }}
    To: {{ to }}
    Cc:
    - separated
    by comma
    Subject: {{ subject }}
    Body:
    {{ body|wrap_text }}
    - Back - -
    -
    -{% endblock %} diff --git a/ietf/templates/wgcharter/status_columns.html b/ietf/templates/wgcharter/status_columns.html deleted file mode 100644 index 462b34f4c..000000000 --- a/ietf/templates/wgcharter/status_columns.html +++ /dev/null @@ -1,17 +0,0 @@ -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} -{% load ietf_filters ietf_streams %}{% load wg_ballot_icon %} - -{% if wg.charter %} -{{ wg.charter.get_state|safe }} {% ifequal wg.state_id "proposed" %}{% ifnotequal wg.charter.get_state_slug "notrev" %}(Initial Chartering){% endifnotequal %}{% else %}{% ifequal wg.state_id "active" %}{% ifnotequal wg.charter.get_state_slug "approved" %}(Rechartering){% endifnotequal %}{% endifequal %}{% ifequal wg.state_id "conclude" %}(Concluded){% endifequal %}{% endifequal %} -{% else %} -(data missing) -{% endif %} -{% if wg.charter.telechat_date %}
    IESG Telechat: {{ wg.charter.telechat_date }}{% endif %} - -{% block extra_status %}{% endblock %} - - -{% if wg.charter.get_state_slug == "iesgrev" %}{% wg_ballot_icon wg.acronym %}{% endif %} - diff --git a/ietf/templates/wgcharter/submit.html b/ietf/templates/wgcharter/submit.html index cac82e4cf..9646d452a 100644 --- a/ietf/templates/wgcharter/submit.html +++ b/ietf/templates/wgcharter/submit.html @@ -30,7 +30,7 @@ Charter submission for {{ wg.acronym }} - Back + Back diff --git a/ietf/templates/wgcharter/table_header.html b/ietf/templates/wgcharter/table_header.html deleted file mode 100644 index 604a92536..000000000 --- a/ietf/templates/wgcharter/table_header.html +++ /dev/null @@ -1,15 +0,0 @@ -{# Copyright The IETF Trust 2011, All Rights Reserved #} - - - - - {% if hdr.selected %} - - {% else %} - - {% endif %} - - diff --git a/ietf/templates/wgcharter/wg_ballot.html b/ietf/templates/wgcharter/wg_ballot.html deleted file mode 100644 index 6fa3e56c7..000000000 --- a/ietf/templates/wgcharter/wg_ballot.html +++ /dev/null @@ -1,57 +0,0 @@ -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} -{% load ietf_filters %} - -
    - -{% if user|in_group:"Area_Director,Secretariat" %} -{% if user|in_group:"Area_Director" %} - -{% endif %} -{% if user|in_group:"Secretariat" %} -{% if not info.pos_block %} -{% endif %} -{% endif %} -{% endif %} - -

    Blocking
    -{% with info.pos_block as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    - -

    Yes
    -{% with info.pos_yes as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    - -

    No
    -{% with info.pos_no as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    - -

    Abstain
    -{% with info.pos_abstain as positions %}{% include "wgcharter/wg_ballot_list.html" %}{% endwith %}

    - -

    No Record
    -{% for p in info.pos_no_record %} -{{p.name}}{% if user|in_group:"Secretariat" %}{% endif %}
    -{% empty %} -none -{% endfor %} -

    - -
    - -

    Comments

    - -{% for pos in info.positions %} -{% if pos.comment or pos.block_comment %} -

    {{pos.ad|escape}}

    - -{% if pos.block_comment %} -

    Blocking ({{pos.block_comment_time}})

    -
    {{pos.block_comment|fill:"80"|escape }}
    -{% endif %} - -{% if pos.comment %} -

    Comment ({{pos.comment_time}})

    -
    {{pos.comment|fill:"80"|escape }}
    -{% endif %} -{% endif %} -{% endfor %} -
    diff --git a/ietf/templates/wgcharter/wg_ballot_list.html b/ietf/templates/wgcharter/wg_ballot_list.html deleted file mode 100644 index b998547f3..000000000 --- a/ietf/templates/wgcharter/wg_ballot_list.html +++ /dev/null @@ -1,6 +0,0 @@ -{% load ietf_filters %} -{% for p in positions %} -{% for oad in info.old_ads %}{% ifequal oad p.ad %}[{%endifequal%}{% endfor %}{{p.ad}}{% for oad in info.old_ads %}{% ifequal oad p.ad %}]{%endifequal%}{% endfor %}{% if p.comment or p.block_comment %} *{% endif %}
    -{% empty %} -none -{% endfor %} diff --git a/ietf/templates/wgcharter/wg_description.html b/ietf/templates/wgcharter/wg_description.html deleted file mode 100644 index 3084834d0..000000000 --- a/ietf/templates/wgcharter/wg_description.html +++ /dev/null @@ -1 +0,0 @@ -IETF Working Group {{ wg.name }} ({{ wg.acronym }}){% if wg.parent %} under the {{ wg.parent }}{% endif %} diff --git a/ietf/templates/wgcharter/wg_history.html b/ietf/templates/wgcharter/wg_history.html deleted file mode 100644 index d4af98e93..000000000 --- a/ietf/templates/wgcharter/wg_history.html +++ /dev/null @@ -1,40 +0,0 @@ -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} - - - -{% for c in history %} - - - -{% if c.is_rev %} - - - -{% endif %} - -{% if c.is_com %} - - - -{% endif %} - - -{% endfor %} - -
    DateVersionByText
    {{ c.date|date:"Y-m-d" }}{{ c.info.version }}(System)New version available: charter-ietf-{{ c.group.acronym }}-{{ c.charter.rev }} {% if c.prev_charter %}(diff from -{{ c.prev_charter.rev }}){% endif %}{{ c.info.version }}{{ c.info.by|escape }}{% if c.comment.ballot %} -[Ballot {{ c.comment.get_ballot_display }}]
    -{% endif %} -{% if c.info.snipped %} -
    {{ c.info.textSnippet|safe }}
    -[show all] - -{% else %} -{{ c.info.text|fill:"80"|safe|urlize|linebreaksbr|keep_spacing|sanitize_html|safe }} -{% endif %} -
    diff --git a/ietf/templates/wgcharter/wg_info.txt b/ietf/templates/wgcharter/wg_info.txt index 11592bead..b25f6fdf6 100644 --- a/ietf/templates/wgcharter/wg_info.txt +++ b/ietf/templates/wgcharter/wg_info.txt @@ -3,22 +3,22 @@ Current Status: {{ wg.state.name }} Working Group Chairs: -{% for p in info.chairs %} {{ p.plain_name }} <{{p.email}}> +{% for r in chairs %} {{ r.person.plain_name }} <{{r.email.address}}> {% endfor %} Secretaries: -{% for p in info.secr %} {{ p.plain_name }} <{{p.email}}> +{% for r in secr %} {{ r.person.plain_name }} <{{r.email.address}}> {% endfor %} Technical advisors: -{% for p in info.techadv %} {{ p.plain_name }} <{{p.email}}> +{% for r in techadv %} {{ r.person.plain_name }} <{{r.email.address}}> {% endfor %} Assigned Area Director: - {{ info.ad.0.plain_name }} <{{ info.ad.0.email }}> +{% if wg.ad %} {{ wg.ad.plain_name }} <{{ ad_email }}>{% endif %} Mailing list: - Address: {{ info.list.0 }} - To Subscribe: {{ info.list_subscribe.0 }} - Archive: {{ info.list_archive.0 }} + Address: {{ wg.list_email }} + To Subscribe: {{ wg.list_subscribe }} + Archive: {{ wg.list_archive }} Charter: -{{ info.charter_txt }} +{{ charter_text }} diff --git a/ietf/templates/wgcharter/wg_main.html b/ietf/templates/wgcharter/wg_main.html deleted file mode 100644 index 793aa9b20..000000000 --- a/ietf/templates/wgcharter/wg_main.html +++ /dev/null @@ -1,72 +0,0 @@ -{% extends "base.html" %} - -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} -{% block morecss %} -.metabox { width: 99%; margin-top:8px; padding:4px; margin-bottom:1em; } -#metatable { border: 0; border-spacing: 0; } -#metatable tr { vertical-align:top ;} -.comment_toggle { text-decoration: underline; color: blue; } -.comment_date { white-space: nowrap; } - -div.diffTool { padding: 8px 4px; margin: 8px 0;} -.diffTool label { float:left; width:50px; } - -.markup_draft pre {line-height: 1.2em; margin: 0; } -.m_hdr, .m_ftr { color: #808080; } -.m_ftr { border-bottom: 1px solid #a0a0a0; } -.m_h { font-family: arial; font-weight:bold;} -.ietf-concluded-bg {background-color: #F8F8D0; } -.ietf-concluded-warning { background:red;color:white;padding:2px 2px;} -.ietf-proposed-bg { } -.ietf-proposed-warning { background:green;color:white;padding:2px 2px;} -{% endblock %} - -{% block pagehead %} - - -{% endblock %} - -{% block title %}{% include "wgcharter/wg_title.html" %}{% endblock title %} - -{% block content %} -

    {% include "wgcharter/wg_title.html" %} -{% ifequal wg.state_id "conclude" %}
    (concluded WG){% endifequal %} -{% ifequal wg.state_id "proposed" %}
    (proposed WG){% endifequal %} -

    - -
    - -
    - -{% block tab_content %}{% endblock %} - -
    -
    - -{% endblock content %} - -{% block scripts %} -function toggleComment(n) { - var el = document.getElementById("commentF"+n); - var el2 = document.getElementById("commentS"+n); - var el3 = document.getElementById("commentT"+n); - if (el.style.display == 'none') { - el.style.display = 'block'; - el2.style.display = 'none'; - el3.innerHTML = ""; //[hide]"; - } else { - el.style.display = 'none'; - el2.style.display= 'block'; - el3.innerHTML = "[show all]"; - } -} -{% endblock scripts %} diff --git a/ietf/templates/wgcharter/wg_tab_ballot.html b/ietf/templates/wgcharter/wg_tab_ballot.html deleted file mode 100644 index 09bf718f5..000000000 --- a/ietf/templates/wgcharter/wg_tab_ballot.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends "wgcharter/wg_main.html" %} -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} - -{% block tab_content %} -{% include "wgcharter/wg_ballot.html" %} -{% endblock tab_content %} - diff --git a/ietf/templates/wgcharter/wg_tab_base.html b/ietf/templates/wgcharter/wg_tab_base.html deleted file mode 100644 index c55982e5b..000000000 --- a/ietf/templates/wgcharter/wg_tab_base.html +++ /dev/null @@ -1,59 +0,0 @@ -{% extends "wgcharter/wg_main.html" %} - -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} - -{% block tab_content %} -{% block wg_revision %}{% endblock %} -
    - -{% block wg_metatable %}{% endblock %} -
    - -
    -{% block wg_metalinks %}{% endblock %} -
    - -{% block wg_metabuttons %} -{% if user|in_group:"Area_Director,Secretariat" %} -{% if not snapshot %} -
    -{% ifnotequal wg.charter.get_state_slug "notrev" %} -{% ifnotequal wg.charter.get_state_slug "approved" %} -Abandon effort -Change state -{% ifnotequal wg.state_id "conclude" %} -Edit charter -{% endifnotequal %} -{% else %} -Recharter -{% endifnotequal %} -{% else %} -Recharter -{% endifnotequal %} - -{% ifequal wg.state_id "active" %}{% ifequal wg.charter.get_state_slug "approved" %} -Conclude WG -{% endifequal %}{% endifequal %} - -{% ifnotequal wg.state_id "conclude" %} -Edit WG -{% endifnotequal %} - - -
    -{% endif %}{# if not snapshot #} -{% endif %}{# if user in group #} -{% endblock wg_metabuttons%} -
    - -
    -{% block charter_text %}{% endblock %} -
    -{% endblock tab_content %} - -{% block content_end %} -{% endblock content_end %} diff --git a/ietf/templates/wgcharter/wg_tab_charter.html b/ietf/templates/wgcharter/wg_tab_charter.html deleted file mode 100644 index 666f163f1..000000000 --- a/ietf/templates/wgcharter/wg_tab_charter.html +++ /dev/null @@ -1,81 +0,0 @@ -{% extends "wgcharter/wg_tab_base.html" %} - -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} - -{% block wg_revision %} -Snapshots: {% for d in versions reversed %}{% if forloop.first %}{% ifnotequal rev wg.charter.rev %}{% endifnotequal %}{% else %}{% ifnotequal rev d.rev %}{% ifequal d.rev wg.charter.rev %}{% else %}{% endifequal %}{% endifnotequal %}{% endif %}{{ d.rev }}{% ifnotequal rev d.rev %}{% endifnotequal %}{% if forloop.first %}{% ifnotequal rev wg.charter.rev %}{% endifnotequal %}{% endif %} {% endfor %} -{% endblock %} - -{% block wg_metatable %} -WG name:{% if not snapshot %}{{ wg.name }}{% else %} {{ gh.name }} {% endif %} -WG acronym:{% if not snapshot %}{{ wg.acronym }} {% if info.prev_acronyms %}(previous acronyms: {% for a in info.prev_acronyms %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}{% else %}{{ gh.acronym }}{% endif %} -IETF area:{% if not snapshot %}{{ wg.parent|default:"-" }}{% else %}{{ gh.parent|default:"-" }}{% endif %} - -
    - -WG chairs:{% if not snapshot %}{% for n in info.chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}{% else %}{% for n in info.history_chairs %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %}{% endif %} -{% if not snapshot %} -WG secretaries:{% for n in info.secr %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %} -WG technical advisors:{% for n in info.techadv %}{{ n }}{% if not forloop.last %}, {% endif %}{% endfor %} -{% endif %} -Assigned AD:{% if not snapshot %}{{ wg.ad }}{% else %}{{ gh.ad }}{% endif %} - -{% if not snapshot %} -
    - -Mailing list:{{ wg.list_email }}{% if info.prev_list_email %}
    (previous lists: {% for a in info.prev_list_email %}{{ a }}{% if not forloop.last %}, {% else %}){% endif %}{% endfor %}{% endif %} -Mailing list subscribe:{{ wg.list_subscribe }}{% if info.prev_list_subscribe %}
    (previous archives: {% for a in info.prev_list_subscribe %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %} -Mailing list archive:{{ wg.list_archive }}{% if info.prev_list_archive %}
    (previous archives: {% for a in info.prev_list_archive %}{{ a }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %} -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 }} -{% ifnotequal wg.state_id "conclude" %}Charter State: {{ wg.charter.get_state|safe }} {% ifequal wg.state_id "proposed" %}{% ifnotequal wg.charter.get_state_slug "notrev" %}(Initial Chartering){% endifnotequal %}{% else %}{% ifnotequal wg.charter.get_state_slug "approved" %}(Rechartering){% endifnotequal %}{% endifequal %}{% endifnotequal %} -{% ifequal wg.state_id "proposed" %} -{% if wg.comments %} -Reason for chartering:{{ wg.comments }} -{% endif %} -{% endifequal %} -{% ifequal wg.state_id "active" %} -{% ifnotequal wg.charter.get_state_slug "approved" %} -{% if wg.comments %} -Reason for rechartering:{{ wg.comments }} -{% endif %} -{% endifnotequal %} -{% endifequal %} - -
    - -Last updated: {{ info.last_update|date:"Y-m-d"|default:"(data missing)" }} -
    - -{% endif %}{# if not snapshot #} - -{% endblock wg_metatable %} - -{% block wg_metalinks %} -{% if not snapshot %} -
    Atom feed -{% endif %} -{% endblock %} - -{% block charter_text %} -{% ifnotequal charter.rev "" %} -

    Other versions: plain text

    - -

    Charter {{ charter.name }}-{{ rev }}

    - -
    -{{ content|fill:"80"|safe|linebreaksbr|keep_spacing|sanitize_html|safe }} -
    - -{% else %} -

    The WG does not yet have a charter

    -{% endifnotequal %} -{% endblock %}{# charter_text #} - diff --git a/ietf/templates/wgcharter/wg_tab_history.html b/ietf/templates/wgcharter/wg_tab_history.html deleted file mode 100644 index 9df8bab17..000000000 --- a/ietf/templates/wgcharter/wg_tab_history.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "wgcharter/wg_main.html" %} -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} - -{% block tab_content %} -{% include "wgcharter/charter_diffs.html" %} -

    WG History

    -{% if user|in_group:"Area_Director,Secretariat" %} - -{% endif %} - -{% include "wgcharter/wg_history.html" %} -{% endblock tab_content %} diff --git a/ietf/templates/wgcharter/wg_tab_writeup.html b/ietf/templates/wgcharter/wg_tab_writeup.html deleted file mode 100644 index 2a3814eb3..000000000 --- a/ietf/templates/wgcharter/wg_tab_writeup.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends "wgcharter/wg_main.html" %} -{% comment %} -Copyright The IETF Trust 2011, All Rights Reserved -{% endcomment %} - -{% load ietf_filters %} - -{% block tab_content %} -

    WG Review Announcement

    -{% if user|in_group:"Area_Director,Secretariat" %} -
    -

    - -Edit WG Review Announcement - -

    -{% endif %} -
    -{{ info.review_text|escape|urlize }}
    -
    -
    - -

    WG Action Announcement

    -{% if user|in_group:"Area_Director,Secretariat" %} -
    -

    - -Edit WG Action Announcement - -

    -{% endif %} -
    -{{ info.action_text|escape|urlize }}
    -
    - -
    - -

    Ballot Announcement

    -{% if user|in_group:"Area_Director,Secretariat" %} -
    -

    - -Edit Ballot Announcement - -

    -{% endif %} -
    -{{ info.ballot_text|escape|urlize }}
    -
    - -
    -{% endblock tab_content %} diff --git a/ietf/templates/wgcharter/wg_title.html b/ietf/templates/wgcharter/wg_title.html deleted file mode 100644 index 3b101b9e0..000000000 --- a/ietf/templates/wgcharter/wg_title.html +++ /dev/null @@ -1 +0,0 @@ -{{ wg.name }} ({{wg.acronym}}) diff --git a/ietf/templates/wginfo/chartering_wgs.html b/ietf/templates/wginfo/chartering_wgs.html new file mode 100644 index 000000000..bea3edbeb --- /dev/null +++ b/ietf/templates/wginfo/chartering_wgs.html @@ -0,0 +1,53 @@ +{% extends "base.html" %} + +{% block title %}Chartering or Re-Chartering Working Groups{% endblock %} + +{% block content %} +{% load ietf_filters %} +{% load ballot_icon %} + +

    Chartering or Re-Chartering Working Groups

    + +

    Groups with a charter in state +{% for s in charter_states %}{% if not forloop.first %}, {% if forloop.last %}or {% endif %}{% endif %}{{ s.name }}{% endfor %}.

    + +{% if user|has_role:"Area Director,Secretariat" %} +

    Start chartering new WG

    +{% endif %} + +{% if not groups %} +

    No groups found.

    +{% else %} + + + + + + + +{% for g in groups %} + + + + + + + +{% endfor %} +
    GroupCharterDateStatus
    + {{ g.acronym }} + + {{ g.name }} + {{ g.charter.time|date:"Y-m-d" }} + {{ g.charter.get_state.name }} + {% if g.chartering_type == "initial" %}(Initial Chartering){% endif %} + {% if g.chartering_type == "recharter" %}(Rechartering){% endif %} + {% if not g.chartering_type and g.state_id != "active" %}({{ g.state.name }}){% endif %} + + {% if g.charter.telechat_date %}
    IESG Telechat: {{ g.charter.telechat_date|date:"Y-m-d" }}{% endif %} +
    + {% ballot_icon g.charter %} +
    +{% endif %} + +{% endblock %} diff --git a/ietf/templates/wgcharter/conclude.html b/ietf/templates/wginfo/conclude.html similarity index 92% rename from ietf/templates/wgcharter/conclude.html rename to ietf/templates/wginfo/conclude.html index 664b07174..d233057c1 100644 --- a/ietf/templates/wgcharter/conclude.html +++ b/ietf/templates/wginfo/conclude.html @@ -28,7 +28,7 @@ form.conclude .actions { {{ form.as_table }} - Back + Back diff --git a/ietf/templates/wgcharter/edit_info.html b/ietf/templates/wginfo/edit.html similarity index 56% rename from ietf/templates/wgcharter/edit_info.html rename to ietf/templates/wginfo/edit.html index ef0209bc7..233fb7106 100644 --- a/ietf/templates/wgcharter/edit_info.html +++ b/ietf/templates/wginfo/edit.html @@ -2,7 +2,7 @@ {% block title %} {% if wg %} -Edit info on {{ wg.acronym }} +Edit WG {{ wg.acronym }} {% else %} Create WG {% endif %} @@ -42,7 +42,7 @@ form.edit-info #id_comments { {% block content %} {% load ietf_filters %}

    {% if wg %} -Edit info on {{ wg.acronym }} +Edit WG {{ wg.acronym }} {% else %} Create WG {% endif %} @@ -55,7 +55,7 @@ Create WG {{ field.label_tag }}: {{ field }} {% ifequal field.name "ad" %} - {% if user|in_group:"Area_Director" %} + {% if user|has_role:"Area Director" %} {% endif %} {% endifequal %} @@ -68,7 +68,7 @@ Create WG {% if wg %} - Back + Back {% else %} @@ -79,24 +79,8 @@ Create WG {% endblock %} -{% block scripts %} -$(document).ready(function () { - var chairs = eval($("#id_chairs").val()), - secretaries = eval($("#id_secretaries").val()), - techadv = eval($("#id_techadv").val()); - $("#id_chairs").tokenInput("/wgcharter/searchPerson/", { hintText: "", - preventDuplicates: true, - prePopulate: chairs }); - $("#id_secretaries").tokenInput("/wgcharter/searchPerson/", { hintText: "", - preventDuplicates: true, - prePopulate: secretaries }); - $("#id_techadv").tokenInput("/wgcharter/searchPerson/", { hintText: "", - preventDuplicates: true, - prePopulate: techadv }); - $("#id_name").focus(); -}); -{% endblock %} - {% block content_end %} + + {% endblock %} diff --git a/ietf/templates/wginfo/wg_charterREDESIGN.html b/ietf/templates/wginfo/wg_charterREDESIGN.html index 563eeabb6..60f87e077 100644 --- a/ietf/templates/wginfo/wg_charterREDESIGN.html +++ b/ietf/templates/wginfo/wg_charterREDESIGN.html @@ -36,80 +36,110 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% block wg_titledetail %}Charter{% endblock %} {% block wg_content %} -
    {% if concluded %} Note: The data for concluded WGs is occasionally incorrect. {% endif %} - - - + - - - - - - -{% if wg.techadvisors %} - - - -{% endif %} -{% if wg.editors %} - - -{% endif %} -{% if wg.secretaries %} - - -{% endif %} + + + + - - - + - - - + {% if wg.parent %} + + {% endif %} -{% if not concluded %} - - - + - - -{% endif %} + + + + + + + + + + + + + + + + {% if wg.techadvisors %} + + + + + {% endif %} + {% if wg.editors %} + + + {% endif %} + {% if wg.secretaries %} + + + + + {% endif %} + + + + + + + + {% if not concluded %} + + + + + {% endif %}
    -Personnel -
    Group
    Chair{{ wg.chairs|pluralize }}: -{% for chair in wg.chairs %} -{{ chair.person.name }} <{{ chair.address }}>
    -{% endfor %} -
    Area Director: -{% if not wg.ad %}?{% else %} -{{ wg.ad.name }} <{{ wg.areadirector.address }}>{% endif %} -
    Tech Advisor{{ wg.techadvisors|pluralize }}: -{% for techadvisor in wg.techadvisors %} -{{ techadvisor.person.name }} <{{ techadvisor.address }}>
    -{% endfor %} -
    Editor{{ wg.editors|pluralize }}: -{% for editor in wg.editors %} -{{ editor.person.name }} <{{ editor.address }}>
    -{% endfor %} -
    Secretar{{ wg.secretaries|pluralize:"y,ies" }}: -{% for secretary in wg.secretaries %} -{{ secretary.person.name }} <{{ secretary.address }}>
    -{% endfor %} -
    Name:{{ wg.name }}
    -
    Mailing List -
    Acronym:{{ wg.acronym }}
    Address:{{ wg.email_address|urlize }}
    To Subscribe:{{ wg.email_subscribe|urlize }}
    Archive:{{ wg.clean_email_archive|urlize }}
    Area:{{ wg.parent.name }} ({{ wg.parent.acronym }})
    -
    Jabber Chat -
    State:{{ wg.state.name }}
    Room Address:xmpp:{{wg}}@jabber.ietf.org
    Logs:http://jabber.ietf.org/logs/{{wg}}/
    Charter: + {% if wg.charter %} + {{ wg.charter.name }}-{{ wg.charter.rev }} ({{ wg.charter.get_state.name }}) + {% else %} + none + {% if user|has_role:"Area Director,Secretariat" %} + - Submit Charter + {% endif %} + {% endif %} +
    Personnel
    Chair{{ wg.chairs|pluralize }}: + {% for chair in wg.chairs %} + {{ chair.person.plain_name }} <{{ chair.address }}>
    + {% endfor %} +
    Area Director: + {% if not wg.ad %}?{% else %} + {{ wg.ad.plain_name }} <{{ wg.areadirector.address }}>{% endif %} +
    Tech Advisor{{ wg.techadvisors|pluralize }}: + {% for techadvisor in wg.techadvisors %} + {{ techadvisor.person.plain_name }} <{{ techadvisor.address }}>
    + {% endfor %} +
    Editor{{ wg.editors|pluralize }}: + {% for editor in wg.editors %} + {{ editor.person.plain_name }} <{{ editor.address }}>
    + {% endfor %} +
    Secretar{{ wg.secretaries|pluralize:"y,ies" }}: + {% for secretary in wg.secretaries %} + {{ secretary.person.plain_name }} <{{ secretary.address }}>
    + {% endfor %} +
    Mailing List
    Address:{{ wg.email_address|urlize }}
    To Subscribe:{{ wg.email_subscribe|urlize }}
    Archive:{{ wg.clean_email_archive|urlize }}
    Jabber Chat
    Room Address:xmpp:{{ wg.acronym }}@jabber.ietf.org
    Logs:http://jabber.ietf.org/logs/{{ wg.acronym }}/
    + +{% if user|has_role:"Area Director,Secretariat" %} +
    + {% for name, url in actions %} + {{ name }} + {% if not forloop.last %}|{% endif %} + {% endfor %} +
    +{% endif %}
    {% if wg.additional_urls %} diff --git a/ietf/urls.py b/ietf/urls.py index 56285d3a8..7c4d41534 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -9,7 +9,7 @@ from ietf.idtracker.feeds import DocumentComments, InLastCall from ietf.ipr.feeds import LatestIprDisclosures from ietf.proceedings.feeds import LatestWgProceedingsActivity from ietf.liaisons.feeds import Liaisons -from ietf.wgcharter.feeds import GroupComments +from ietf.wgcharter.feeds import GroupChanges from ietf.idtracker.sitemaps import IDTrackerMap, DraftMap from ietf.liaisons.sitemaps import LiaisonMap @@ -25,7 +25,7 @@ feeds = { 'iesg-agenda': IESGAgenda, 'last-call': InLastCall, 'comments': DocumentComments, - 'wgcomments': GroupComments, + 'group-changes': GroupChanges, 'ipr': LatestIprDisclosures, 'liaison': Liaisons, 'wg-proceedings' : LatestWgProceedingsActivity @@ -61,8 +61,8 @@ urlpatterns = patterns('', (r'^accounts/', include('ietf.ietfauth.urls')), (r'^doc/', include('ietf.idrfc.urls')), (r'^wg/', include('ietf.wginfo.urls')), - (r'^wgcharter/', include('ietf.wgcharter.urls')), (r'^cookies/', include('ietf.cookies.urls')), + (r'^person/', include('ietf.person.urls')), (r'^submit/', include('ietf.submit.urls')), (r'^streams/', include('ietf.ietfworkflows.urls')), diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index e008d7ee4..0494be591 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -263,12 +263,20 @@ def make_test_data(): order=1 ) - # draft has only one event + # fill in some useful default events DocEvent.objects.create( type="started_iesg_process", by=ad, doc=draft, - desc="Added draft", + desc="Started IESG process", + ) + + BallotDocEvent.objects.create( + type="created_ballot", + ballot_type=BallotType.objects.get(doc_type="draft", slug="approve"), + by=ad, + doc=draft, + desc="Created ballot", ) # IPR diff --git a/ietf/wgcharter/feeds.py b/ietf/wgcharter/feeds.py index a12b9ed7a..31369daf8 100644 --- a/ietf/wgcharter/feeds.py +++ b/ietf/wgcharter/feeds.py @@ -1,67 +1,58 @@ # Copyright The IETF Trust 2011, All Rights Reserved +import datetime, re, os + from django.conf import settings from django.contrib.syndication.feeds import Feed, FeedDoesNotExist from django.utils.feedgenerator import Atom1Feed -from django.core.exceptions import ObjectDoesNotExist -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse as urlreverse +from django.utils.html import strip_tags +from django.utils.text import truncate_words + from ietf.utils.history import find_history_active_at +from ietf.group.models import Group, GroupEvent +from ietf.doc.models import DocEvent -from ietf.group.models import Group -from ietf.wgcharter.views import _get_history, _get_html -from ietf.wgcharter import markup_txt -import datetime -import re, os - -class GroupComments(Feed): +class GroupChanges(Feed): feed_type = Atom1Feed - title_template = "feeds/wg_charter_title.html" - description_template = "feeds/wg_charter_description.html" + description_template = "feeds/group_description.html" def get_object(self, bits): if len(bits) != 1: - raise ObjectDoesNotExist + raise Group.DoesNotExist return Group.objects.get(acronym=bits[0]) def title(self, obj): - return "WG Record changes for %s" % obj.acronym + return u"Changes for %s %s" % (obj.acronym, obj.type) def link(self, obj): - if obj is None: + if not obj: raise FeedDoesNotExist - return reverse('wg_view', kwargs={'name': obj.acronym}) + return urlreverse('wg_charter', kwargs={'acronym': obj.acronym}) def description(self, obj): return self.title(obj) def items(self, obj): - history = _get_history(obj) - for h in history: - gh = find_history_active_at(obj, h['date']) - if gh: - h['chairs'] = [x.person.plain_name() for x in gh.rolehistory_set.filter(name__slug="chair")] - h['secr'] = [x.person.plain_name() for x in gh.rolehistory_set.filter(name__slug="secr")] - h['techadv'] = [x.person.plain_name() for x in gh.rolehistory_set.filter(name__slug="techadv")] - else: - h['chairs'] = [x.person.plain_name() for x in obj.role_set.filter(name__slug="chair")] - h['secr'] = [x.person.plain_name() for x in obj.role_set.filter(name__slug="secr")] - h['techadv'] = [x.person.plain_name() for x in obj.role_set.filter(name__slug="techadv")] - dh = find_history_active_at(obj.charter, h['date']) - if dh: - h['rev'] = dh.rev - h['charter'] = _get_html( - str(dh.name)+"-"+str(dh.rev)+",html", - os.path.join(dh.get_file_path(), dh.name+"-"+dh.rev+".txt")) - else: - h['rev'] = obj.charter.rev - h['charter'] = _get_html( - "charter-ietf-"+str(obj.acronym)+"-"+str(obj.charter.rev)+",html", - os.path.join(obj.charter.get_file_path(), "charter-ietf-"+obj.acronym+"-"+obj.charter.rev+".txt")) - return history + events = list(obj.groupevent_set.all().select_related("group")) + if obj.charter: + events += list(obj.charter.docevent_set.all()) + + events.sort(key=lambda e: (e.time, e.id), reverse=True) + + return events def item_link(self, obj): - return reverse('wg_view', kwargs={'name': obj['group'].acronym}) + if isinstance(obj, DocEvent): + return urlreverse("doc_view", kwargs={'name': obj.doc_id }) + elif isinstance(obj, GroupEvent): + return urlreverse('wg_charter', kwargs={'acronym': obj.group.acronym }) def item_pubdate(self, obj): - return obj['date'] + return obj.time + def item_title(self, obj): + title = u"%s - %s" % (truncate_words(strip_tags(obj.desc), 10), obj.by) + if isinstance(obj, DocEvent): + title = u"Chartering: %s" % title + return title diff --git a/ietf/wgcharter/mails.py b/ietf/wgcharter/mails.py index d222db6a6..2e3d3d1d6 100644 --- a/ietf/wgcharter/mails.py +++ b/ietf/wgcharter/mails.py @@ -1,7 +1,6 @@ # generation of mails -import textwrap -from datetime import datetime, date, time, timedelta +import textwrap, datetime from django.template.loader import render_to_string from django.utils.html import strip_tags @@ -9,31 +8,34 @@ from django.conf import settings from django.core.urlresolvers import reverse as urlreverse from ietf.utils.mail import send_mail, send_mail_text -from ietf.idtracker.models import * from ietf.ipr.search import iprs_from_docs -from ietf.doc.models import WriteupDocEvent, DocAlias, GroupBallotPositionDocEvent +from ietf.doc.models import WriteupDocEvent, DocAlias, BallotPositionDocEvent from ietf.person.models import Person - -# These become part of the subject of the email -types = {} -types['state'] = "State changed" -types['state-notrev'] = "State changed to Not currently under review" -types['state-infrev'] = "State changed to Informal review" -types['state-intrev'] = "State changed to Internal review" -types['state-extrev'] = "State changed to External review" -types['state-iesgrev'] = "State changed to IESG review" -types['state-approved'] = "Charter approved" -types['conclude'] = "Request closing of WG" +from ietf.wgcharter.utils import * def email_secretariat(request, wg, type, text): to = ["iesg-secretary@ietf.org"] + + types = {} + types['state'] = "State changed" + types['state-notrev'] = "State changed to Not currently under review" + types['state-infrev'] = "State changed to Informal review" + types['state-intrev'] = "State changed to Internal review" + types['state-extrev'] = "State changed to External review" + types['state-iesgrev'] = "State changed to IESG review" + types['state-approved'] = "Charter approved" + types['conclude'] = "Request closing of WG" + + subject = u"Regarding WG %s: %s" % (wg.acronym, types[type]) text = strip_tags(text) - send_mail(request, to, None, - "Regarding WG %s: %s" % (wg.acronym, types[type]), + send_mail(request, to, None, subject, "wgcharter/email_secretariat.txt", dict(text=text, - url=settings.IDTRACKER_BASE_URL + urlreverse('wg_view', kwargs=dict(name=wg.acronym)))) + wg_url=settings.IDTRACKER_BASE_URL + urlreverse('wg_charter', kwargs=dict(acronym=wg.acronym)), + charter_url=settings.IDTRACKER_BASE_URL + urlreverse('doc_view', kwargs=dict(name=wg.charter.name)), + ) + ) def generate_ballot_writeup(request, doc): e = WriteupDocEvent() @@ -46,13 +48,51 @@ def generate_ballot_writeup(request, doc): return e -def generate_issue_ballot_mail(request, charter): +def default_action_text(wg, charter, user, action): + e = WriteupDocEvent(doc=charter, by=user) + e.by = user + e.type = "changed_action_announcement" + e.desc = "WG action text was changed" + e.text = render_to_string("wgcharter/action_text.txt", + dict(wg=wg, + charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(), + charter_text=read_charter_text(charter), + chairs=wg.role_set.filter(name="chair"), + secr=wg.role_set.filter(name="secr"), + techadv=wg.role_set.filter(name="techadv"), + ad_email=wg.ad.role_email("ad") if wg.ad else None, + action_type=action, + )) + + e.save() + return e + +def default_review_text(wg, charter, user): + e = WriteupDocEvent(doc=charter, by=user) + e.by = user + e.type = "changed_review_announcement" + e.desc = "WG review text was changed" + e.text = render_to_string("wgcharter/review_text.txt", + dict(wg=wg, + charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(), + charter_text=read_charter_text(charter), + chairs=wg.role_set.filter(name="chair"), + secr=wg.role_set.filter(name="secr"), + techadv=wg.role_set.filter(name="techadv"), + ad_email=wg.ad.role_email("ad") if wg.ad else None, + review_date=(datetime.date.today() + datetime.timedelta(weeks=1)).isoformat(), + review_type="new" if wg.state_id == "proposed" else "recharter", + ) + ) + e.save() + return e + +def generate_issue_ballot_mail(request, doc, ballot): active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct() - e = charter.latest_event(type="started_iesg_process") seen = [] positions = [] - for p in GroupBallotPositionDocEvent.objects.filter(doc=charter, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad'): + for p in BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad'): if p.ad not in seen: positions.append(p) seen.append(p.ad) @@ -84,9 +124,9 @@ def generate_issue_ballot_mail(request, charter): if p.ad in active_ads: active_ad_positions.append(fmt) - if not p.pos_id == "block": - p.block_comment = "" - if p.comment or p.block_comment: + if not p.pos or not p.pos.blocking: + p.discuss = "" + if p.comment or p.discuss: ad_feedback.append(p) else: inactive_ad_positions.append(fmt) @@ -95,15 +135,15 @@ def generate_issue_ballot_mail(request, charter): inactive_ad_positions.sort() ad_feedback.sort(key=lambda p: p.ad.plain_name()) - e = charter.latest_event(WriteupDocEvent, type="changed_action_announcement") + e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement") approval_text = e.text if e else "" - e = charter.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text") + e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text") ballot_writeup = e.text if e else "" return render_to_string("wgcharter/issue_ballot_mail.txt", - dict(charter=charter, - charter_url=settings.IDTRACKER_BASE_URL + charter.get_absolute_url(), + dict(doc=doc, + doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), active_ad_positions=active_ad_positions, inactive_ad_positions=inactive_ad_positions, ad_feedback=ad_feedback, diff --git a/ietf/wgcharter/markup_txt.py b/ietf/wgcharter/markup_txt.py deleted file mode 100644 index 7fa7ff5c9..000000000 --- a/ietf/wgcharter/markup_txt.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -# All rights reserved. Contact: Pasi Eronen -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials provided -# with the distribution. -# -# * Neither the name of the Nokia Corporation and/or its -# subsidiary(-ies) nor the names of its contributors may be used -# to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (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.utils.html import escape -import string -import re - -def markup(content): - # normalize line endings to LF only - content = content.replace("\r\n", "\n") - content = content.replace("\r", "\n") - - # at this point, "content" is normal string - # fix most common non-ASCII characters - t1 = string.maketrans("\x91\x92\x93\x94\x95\x96\x97\xc6\xe8\xe9", "\'\'\"\"o--\'ee") - # map everything except printable ASCII, TAB, LF, FF to "?" - t2 = string.maketrans('','') - t3 = "?"*9 + "\t\n?\f" + "?"*19 + t2[32:127] + "?"*129 - t4 = t1.translate(t3) - content = content.translate(t4) - - # remove leading white space - content = content.lstrip() - # remove runs of blank lines - content = re.sub("\n\n\n+", "\n\n", content) - - # expand tabs + escape - content = escape(content.expandtabs()) - - content = re.sub("\n(.+\[Page \d+\])\n\f\n(.+)\n", """\n\g<1>\n\g<2>\n""", content) - content = re.sub("\n(.+\[Page \d+\])\n\s*$", """\n\g<1>\n""", content) - # remove remaining FFs (to be valid XHTML) - content = content.replace("\f","\n") - - 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) - - return "
    "+content+"
    \n" diff --git a/ietf/wgcharter/migrate.py b/ietf/wgcharter/migrate.py new file mode 100644 index 000000000..32121bd9e --- /dev/null +++ b/ietf/wgcharter/migrate.py @@ -0,0 +1,104 @@ +import sys, os, re, datetime + +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) +sys.path = [ basedir ] + sys.path + +from ietf import settings +from django.core import management +management.setup_environ(settings) + +from ietf.doc.models import * + +# make sure ballot positions and types are right +BallotPositionName.objects.get_or_create(slug="block", + order=3, + name="Block", + blocking=True, + ) + +BallotPositionName.objects.filter(slug="discuss").update(blocking=True) + +charter_positions = BallotPositionName.objects.filter(slug__in=["yes", "noobj", "block", "abstain", "norecord" ]) + +o,_ = BallotType.objects.get_or_create(doc_type_id="charter", + slug="r-extrev", + name="Ready for external review", + question="Is this charter ready for external review?", + order=1, + ) + +o.positions = charter_positions + +o,_ = BallotType.objects.get_or_create(doc_type_id="charter", + slug="r-wo-ext", + name="Ready w/o external review", + question="Is this charter ready for external review? Is this charter ready for approval without external review?", + order=2, + ) +o.positions = charter_positions + +o,_ = BallotType.objects.get_or_create(doc_type_id="charter", + slug="approve", + name="Approve", + question="Do we approve of this charter?", + order=3, + ) +o.positions = charter_positions + +draft_ballot,_ = BallotType.objects.get_or_create(doc_type_id="draft", + slug="approve", + name="Approve", + question="", + order=1, + ) +draft_ballot.positions = BallotPositionName.objects.filter(slug__in=["yes", "noobj", "discuss", "abstain", "recuse", "norecord"]) + + +# add events for drafts + +# prevent memory from leaking when settings.DEBUG=True +from django.db import connection +class DontSaveQueries(object): + def append(self, x): + pass +connection.queries = DontSaveQueries() + +relevant_docs = Document.objects.filter(type="draft", docevent__type__in=("changed_ballot_position", "sent_ballot_announcement")).distinct() +for d in relevant_docs.iterator(): + ballot = None + for e in d.docevent_set.order_by("time", "id").select_related("ballotpositiondocevent"): + if e.type == "created_ballot": + ballot = e + + if e.type == "closed_ballot": + ballot = None + + if not ballot and e.type in ("sent_ballot_announcement", "changed_ballot_position"): + ballot = BallotDocEvent(doc=e.doc, by=e.by) + ballot.type = "created_ballot" + ballot.ballot_type = draft_ballot + # place new event just before + ballot.time = e.time - datetime.timedelta(seconds=1) + ballot.desc = u'Created "%s" ballot' % draft_ballot + ballot.save() + + if e.type == "sent_ballot_announcement": + print "added ballot for", d.name + else: + print "MISSING ballot issue event, added ballot for", d.name + + if e.type == "changed_ballot_position" and not e.ballotpositiondocevent.ballot: + e.ballotpositiondocevent.ballot_id = ballot.id + e.ballotpositiondocevent.save() + + if e.type in ("iesg_approved", "iesg_disapproved") and ballot: + c = BallotDocEvent(doc=e.doc, by=e.by) + c.type = "closed_ballot" + c.ballot_type = draft_ballot + # place new event just before + c.time = e.time - datetime.timedelta(seconds=1) + c.desc = u'Closed "%s" ballot' % draft_ballot.name + c.save() + ballot = None + + print "closed ballot for", d.name diff --git a/ietf/wgcharter/templatetags/__init__.py b/ietf/wgcharter/templatetags/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/ietf/wgcharter/templatetags/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/ietf/wgcharter/templatetags/wg_ballot_icon.py b/ietf/wgcharter/templatetags/wg_ballot_icon.py deleted file mode 100644 index ffb6816b8..000000000 --- a/ietf/wgcharter/templatetags/wg_ballot_icon.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright The IETF Trust 2011, All Rights Reserved - -from django import template -from django.core.urlresolvers import reverse as urlreverse -from django.conf import settings -from ietf.idtracker.templatetags.ietf_filters import in_group, timesince_days -from ietf.doc.models import GroupBallotPositionDocEvent -from ietf.person.models import Person -from ietf.group.models import Group - -register = template.Library() - -def get_user_adid(context): - if 'user' in context and in_group(context['user'], "Area_Director"): - return context['user'].get_profile().id - else: - return None - -def get_user_name(context): - if 'user' in context and context['user'].is_authenticated(): - from ietf.person.models import Person - try: - return context['user'].get_profile().plain_name() - except Person.DoesNotExist: - return None - -def render_ballot_icon(context, name): - wg = Group.objects.get(acronym=name) - doc = wg.charter - adId = get_user_adid(context) - red = 0 - green = 0 - yellow = 0 - gray = 0 - blank = 0 - my = None - - active_ads = list(Person.objects.filter(email__role__name="ad", - email__role__group__type="area", - email__role__group__state="active").distinct()) - started_process = doc.latest_event(type="started_iesg_process") - latest_positions = [] - for p in active_ads: - p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=doc, ad=p).order_by("-time")) - if p_pos != []: - latest_positions.append(p_pos[0]) - for p in latest_positions: - if not p.pos_id: - blank = blank + 1 - elif (p.pos_id == "yes"): - green = green + 1 - elif (p.pos_id == "no"): - green = green + 1 - elif (p.pos_id == "block"): - red = red + 1 - elif (p.pos_id == "abstain"): - yellow = yellow + 1 - else: - blank = blank + 1 - if adId and (p.ad_id == adId): - my = p.pos.name - return render_ballot_icon2(wg.acronym, red,yellow,green,gray,blank, my, adId)+"" - -def render_ballot_icon2(acronym, red,yellow,green,gray,blank, my, adId): - edit_position_url = urlreverse('wg_edit_position', kwargs=dict(name=acronym)) - if adId: - res_cm = ' oncontextmenu="editWGBallot(\''+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 == "Block") - 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") - else: - c = "" - myMark = (y == 2) and (x == 4) and (my == "No Record") - if myMark: - res = res + '' - res = res + '
    ' - my = None - else: - res = res + '' - res = res + '
    ' - return 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) - -def do_ballot_icon(parser, token): - try: - tagName, docName = token.split_contents() - except ValueError: - raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents.split()[0] - return BallotIconNode(docName) - -register.tag('wg_ballot_icon', do_ballot_icon) - -@register.filter -def my_position(doc, user): - user_name = get_user_name({'user':user}) - p_pos = list(GroupBallotPositionDocEvent.objects.filter(doc=doc, ad=Person.objects.get(user__name=user_name)).order_by("-time")) - if p_pos == []: - return "No record" - else: - return p_pos[0].pos.name - diff --git a/ietf/wgcharter/tests.py b/ietf/wgcharter/tests.py index 93a5579aa..e5d2c4f3a 100644 --- a/ietf/wgcharter/tests.py +++ b/ietf/wgcharter/tests.py @@ -5,6 +5,7 @@ from StringIO import StringIO import django.test from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse from ietf.utils.mail import outbox from ietf.utils.test_data import make_test_data from ietf.utils.test_utils import login_testing_unauthorized @@ -12,64 +13,24 @@ from ietf.utils.test_utils import login_testing_unauthorized from pyquery import PyQuery from ietf.doc.models import * +from ietf.doc.utils import * from ietf.group.models import * from ietf.group.utils import * from ietf.name.models import * from ietf.person.models import * from ietf.iesg.models import TelechatDate +from ietf.wgcharter.utils import * -from utils import * - -class SearchTestCase(django.test.TestCase): +class EditCharterTestCase(django.test.TestCase): fixtures = ['names'] - def test_search(self): - make_test_data() + def setUp(self): + self.charter_dir = os.path.abspath("tmp-charter-dir") + os.mkdir(self.charter_dir) + settings.CHARTER_PATH = self.charter_dir - group = Group.objects.get(acronym="mars") - group.charter.set_state(State.objects.get(slug="infrev", type="charter")) - - r = self.client.get("/wgcharter/") - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search")) - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search_in_process")) - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search_by_area", kwargs=dict(name=group.parent.acronym))) - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=%s" % group.name.replace(" ", "+")) - self.assertEquals(r.status_code, 302) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something") - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=acronym&acronym=some") - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=state&state=active&charter_state=") - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=state&state=&charter_state=%s" % State.objects.get(type="charter", slug="approved").pk) - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=ad&ad=%s" % Person.objects.get(name="Aread Irector").pk) - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=area&area=%s" % group.parent.pk) - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=anyfield&anyfield=something") - self.assertEquals(r.status_code, 200) - - r = self.client.get(urlreverse("wg_search") + "?nameacronym=something&by=eacronym&eacronym=someold") - self.assertEquals(r.status_code, 200) - -class WgStateTestCase(django.test.TestCase): - fixtures = ['names'] + def tearDown(self): + shutil.rmtree(self.charter_dir) def test_change_state(self): make_test_data() @@ -77,8 +38,7 @@ class WgStateTestCase(django.test.TestCase): group = Group.objects.get(acronym="ames") charter = group.charter - # -- Test change state -- - url = urlreverse('wg_change_state', kwargs=dict(name=group.acronym)) + url = urlreverse('charter_change_state', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) first_state = charter.get_state() @@ -97,177 +57,36 @@ class WgStateTestCase(django.test.TestCase): self.assertEquals(charter.get_state(), first_state) # change state - for slug in ("intrev", "extrev", "iesgrev", "approved"): + for slug in ("intrev", "extrev", "iesgrev"): s = State.objects.get(type="charter", slug=slug) events_before = charter.docevent_set.count() mailbox_before = len(outbox) - r = self.client.post(url, dict(state="active", charter_state=s.pk, message="test message")) + r = self.client.post(url, dict(charter_state=str(s.pk), message="test message")) self.assertEquals(r.status_code, 302) charter = Document.objects.get(name="charter-ietf-%s" % group.acronym) self.assertEquals(charter.get_state_slug(), slug) - self.assertEquals(charter.docevent_set.count(), events_before + 1) - self.assertTrue("State changed" in charter.docevent_set.all()[0].desc) + self.assertTrue(charter.docevent_set.count() > events_before) + if slug in ("intrev", "iesgrev"): + self.assertEquals(charter.docevent_set.all()[0].type, "created_ballot") + self.assertTrue("State changed" in charter.docevent_set.all()[1].desc) + else: + self.assertTrue("State changed" in charter.docevent_set.all()[0].desc) if slug == "extrev": - self.assertEquals(len(outbox), mailbox_before + 2) + self.assertEquals(len(outbox), mailbox_before + 1) self.assertTrue("State changed" in outbox[-1]['Subject']) - self.assertTrue("State changed" in outbox[-2]['Subject']) else: self.assertEquals(len(outbox), mailbox_before + 1) - if slug == "approved": - self.assertTrue("Charter approved" in outbox[-1]['Subject']) - else: - self.assertTrue("State changed" in outbox[-1]['Subject']) + self.assertTrue("State changed" in outbox[-1]['Subject']) - def test_conclude(self): - make_test_data() - - # And make a charter for group - group = Group.objects.get(acronym="mars") - - # -- Test conclude WG -- - url = urlreverse('wg_conclude', kwargs=dict(name=group.acronym)) - login_testing_unauthorized(self, "secretary", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form textarea[name=instructions]')), 1) - - # faulty post - r = self.client.post(url, dict(instructions="")) # No instructions - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form ul.errorlist')) > 0) - - # conclusion request - r = self.client.post(url, dict(instructions="Test instructions")) - self.assertEquals(r.status_code, 302) - # The WG remains active until the state is set to conclude via change_state - group = Group.objects.get(acronym=group.acronym) - self.assertEquals(group.state_id, "active") - -class WgInfoTestCase(django.test.TestCase): - fixtures = ['names'] - - def setUp(self): - self.charter_dir = os.path.abspath("tmp-charter-dir") - os.mkdir(self.charter_dir) - settings.CHARTER_PATH = self.charter_dir - - def tearDown(self): - shutil.rmtree(self.charter_dir) - - def test_create(self): - make_test_data() - - # -- Test WG creation -- - url = urlreverse('wg_create') - login_testing_unauthorized(self, "secretary", url) - - num_wgs = len(Group.objects.filter(type="wg")) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=acronym]')), 1) - - # faulty post - r = self.client.post(url, dict(acronym="foobarbaz")) # No name - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form ul.errorlist')) > 0) - self.assertEquals(len(Group.objects.filter(type="wg")), num_wgs) - - # creation - r = self.client.post(url, dict(acronym="testwg", name="Testing WG")) - self.assertEquals(r.status_code, 302) - self.assertEquals(len(Group.objects.filter(type="wg")), num_wgs + 1) - group = Group.objects.get(acronym="testwg") - self.assertEquals(group.name, "Testing WG") - # check that a charter was created with the correct name - self.assertEquals(group.charter.name, "charter-ietf-testwg") - # and that it has no revision - self.assertEquals(group.charter.rev, "") - - - def test_edit_info(self): - make_test_data() - - # And make a charter for group - group = Group.objects.get(acronym="mars") - - url = urlreverse('wg_edit_info', kwargs=dict(name=group.acronym)) - login_testing_unauthorized(self, "secretary", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form select[name=parent]')), 1) - self.assertEquals(len(q('form input[name=acronym]')), 1) - - # faulty post - Group.objects.create(name="Collision Test Group", acronym="collide") - r = self.client.post(url, dict(acronym="collide")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form ul.errorlist')) > 0) - - # Create old acronym - group.acronym = "oldmars" - group.save() - save_group_in_history(group) - group.acronym = "mars" - group.save() - - # post with warning - r = self.client.post(url, dict(acronym="oldmars")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form ul.errorlist')) > 0) - - # edit info - area = group.parent - ad = Person.objects.get(name="Aread Irector") - r = self.client.post(url, - dict(name="Mars Not Special Interest Group", - acronym="mnsig", - parent=area.pk, - ad=ad.pk, - chairs="aread@ietf.org, ad1@ietf.org", - secretaries="aread@ietf.org, ad1@ietf.org, ad2@ietf.org", - techadv="aread@ietf.org", - list_email="mars@mail", - list_subscribe="subscribe.mars", - list_archive="archive.mars", - urls="http://mars.mars (MARS site)" - )) - self.assertEquals(r.status_code, 302) - - group = Group.objects.get(acronym="mnsig") - self.assertEquals(group.name, "Mars Not Special Interest Group") - self.assertEquals(group.parent, area) - self.assertEquals(group.ad, ad) - for k in ("chair", "secr", "techadv"): - self.assertTrue(group.role_set.filter(name=k, email__address="aread@ietf.org")) - self.assertEquals(group.list_email, "mars@mail") - self.assertEquals(group.list_subscribe, "subscribe.mars") - self.assertEquals(group.list_archive, "archive.mars") - self.assertEquals(group.groupurl_set.all()[0].url, "http://mars.mars") - self.assertEquals(group.groupurl_set.all()[0].name, "MARS site") - def test_edit_telechat_date(self): make_test_data() - # And make a charter for group group = Group.objects.get(acronym="mars") charter = group.charter - url = urlreverse('wg_edit_info', kwargs=dict(name=group.acronym)) + url = urlreverse('charter_telechat_date', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) # add to telechat @@ -299,11 +118,10 @@ class WgInfoTestCase(django.test.TestCase): def test_submit_charter(self): make_test_data() - # And make a charter for group group = Group.objects.get(acronym="mars") charter = group.charter - url = urlreverse('wg_submit', kwargs=dict(name=group.acronym)) + url = urlreverse('charter_submit', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) # normal get @@ -324,184 +142,7 @@ class WgInfoTestCase(django.test.TestCase): self.assertEquals(charter.rev, next_revision(prev_rev)) self.assertTrue("new_revision" in charter.latest_event().type) -class WgAddCommentTestCase(django.test.TestCase): - fixtures = ['names'] - - def test_add_comment(self): - make_test_data() - - group = Group.objects.get(acronym="mars") - url = urlreverse('wg_add_comment', kwargs=dict(name=group.acronym)) - login_testing_unauthorized(self, "secretary", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form textarea[name=comment]')), 1) - - # request resurrect - comments_before = group.groupevent_set.filter(type="added_comment").count() - - r = self.client.post(url, dict(comment="This is a test.")) - self.assertEquals(r.status_code, 302) - - self.assertEquals(group.groupevent_set.filter(type="added_comment").count(), comments_before + 1) - self.assertTrue("This is a test." in group.groupevent_set.filter(type="added_comment").order_by('-time')[0].desc) - -class WgEditPositionTestCase(django.test.TestCase): - fixtures = ['names', 'ballot'] - - def test_edit_position(self): - make_test_data() - - group = Group.objects.get(acronym="mars") - charter = group.charter - - url = urlreverse('wg_edit_position', kwargs=dict(name=group.acronym)) - login_testing_unauthorized(self, "ad", url) - - p = Person.objects.get(name="Aread Irector") - - e = DocEvent() - e.type = "started_iesg_process" - e.by = p - e.doc = charter - e.desc = "IESG process started" - e.save() - - charter.set_state(State.objects.get(type="charter", slug="iesgrev")) - charter.save() - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form input[name=position]')) > 0) - self.assertEquals(len(q('form textarea[name=comment]')), 1) - - # vote - pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() - self.assertTrue(not charter.docevent_set.filter(type="changed_ballot_position", by__name="Aread Irector")) - - r = self.client.post(url, dict(position="block", - block_comment="This is a blocking test.", - comment="This is a test.")) - self.assertEquals(r.status_code, 302) - - self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) - e = charter.latest_event(GroupBallotPositionDocEvent) - self.assertTrue("This is a blocking test." in e.block_comment) - self.assertTrue("This is a test." in e.comment) - self.assertTrue(e.pos_id, "block") - - # recast vote - pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() - - r = self.client.post(url, dict(position="yes")) - self.assertEquals(r.status_code, 302) - - self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) - e = charter.latest_event(GroupBallotPositionDocEvent) - self.assertTrue(e.pos_id, "yes") - - # clear vote - pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() - - r = self.client.post(url, dict(position="norecord")) - self.assertEquals(r.status_code, 302) - - self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) - e = charter.latest_event(GroupBallotPositionDocEvent) - self.assertTrue(e.pos_id, "norecord") - - def test_edit_position_as_secretary(self): - make_test_data() - - group = Group.objects.get(acronym="mars") - charter = group.charter - - url = urlreverse('wg_edit_position', kwargs=dict(name=group.acronym)) - p = Person.objects.get(name="Aread Irector") - url += "?ad=%d" % p.id - login_testing_unauthorized(self, "secretary", url) - - e = DocEvent() - e.type = "started_iesg_process" - e.by = p - e.doc = charter - e.desc = "IESG process started" - e.save() - - charter.set_state(State.objects.get(type="charter", slug="iesgrev")) - charter.save() - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form input[name=position]')) > 0) - - # vote for rhousley - pos_before = charter.docevent_set.filter(type="changed_ballot_position").count() - self.assertTrue(not charter.docevent_set.filter(type="changed_ballot_position", by__name="Sec Retary")) - - r = self.client.post(url, dict(position="no")) - self.assertEquals(r.status_code, 302) - - self.assertEquals(charter.docevent_set.filter(type="changed_ballot_position").count(), pos_before + 1) - e = charter.latest_event(GroupBallotPositionDocEvent) - self.assertTrue(e.pos_id, "no") - - def test_send_ballot_comment(self): - make_test_data() - - group = Group.objects.get(acronym="mars") - charter = group.charter - - url = urlreverse('wg_send_ballot_comment', kwargs=dict(name=group.acronym)) - login_testing_unauthorized(self, "ad", url) - - p = Person.objects.get(name="Aread Irector") - - e = DocEvent() - e.type = "started_iesg_process" - e.by = p - e.doc = charter - e.desc = "IESG process started" - e.save() - - charter.set_state(State.objects.get(type="charter", slug="iesgrev")) - charter.save() - - GroupBallotPositionDocEvent.objects.create( - doc=charter, - by=p, - type="changed_ballot_position", - pos=GroupBallotPositionName.objects.get(slug="block"), - ad=p, - block_comment="This is a block test", - comment="This is a test", - ) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue(len(q('form input[name="cc"]')) > 0) - - # send - p = Person.objects.get(name="Aread Irector") - mailbox_before = len(outbox) - - r = self.client.post(url, dict(cc="test@example.com", cc_state_change="1")) - self.assertEquals(r.status_code, 302) - - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("BLOCKING COMMENT" in outbox[-1]['Subject']) - self.assertTrue("COMMENT" in outbox[-1]['Subject']) - -class WgApproveBallotTestCase(django.test.TestCase): +class CharterApproveBallotTestCase(django.test.TestCase): fixtures = ['names'] def setUp(self): @@ -512,27 +153,28 @@ class WgApproveBallotTestCase(django.test.TestCase): def tearDown(self): shutil.rmtree(self.charter_dir) - def test_approve_ballot(self): + def test_approve(self): make_test_data() group = Group.objects.get(acronym="ames") charter = group.charter - url = urlreverse('wg_approve_ballot', kwargs=dict(name=group.acronym)) + url = urlreverse('charter_approve', kwargs=dict(name=charter.name)) login_testing_unauthorized(self, "secretary", url) - with open(os.path.join(self.charter_dir, "charter-ietf-%s-%s.txt" % (group.acronym, charter.rev)), "w") as f: + with open(os.path.join(self.charter_dir, "%s-%s.txt" % (charter.canonical_name(), charter.rev)), "w") as f: f.write("This is a charter.") p = Person.objects.get(name="Aread Irector") - e = DocEvent() - e.type = "started_iesg_process" - e.by = p - e.doc = charter - e.desc = "IESG process started" - e.save() - + BallotDocEvent.objects.create( + type="created_ballot", + ballot_type=BallotType.objects.get(doc_type="charter", slug="approve"), + by=p, + doc=charter, + desc="Created ballot", + ) + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) # normal get @@ -550,6 +192,7 @@ class WgApproveBallotTestCase(django.test.TestCase): charter = Document.objects.get(name=charter.name) self.assertEquals(charter.get_state_slug(), "approved") + self.assertTrue(not ballot_open(charter, "approve")) self.assertEquals(charter.rev, "01") self.assertTrue(os.path.exists(os.path.join(self.charter_dir, "charter-ietf-%s-%s.txt" % (group.acronym, charter.rev)))) diff --git a/ietf/wgcharter/urls.py b/ietf/wgcharter/urls.py index 042088d0c..1673cc92c 100644 --- a/ietf/wgcharter/urls.py +++ b/ietf/wgcharter/urls.py @@ -1,31 +1,18 @@ # Copyright The IETF Trust 2011, All Rights Reserved from django.conf.urls.defaults import patterns, url -from ietf.wgcharter import views, views_search, views_edit, views_ballot, views_submit from ietf.doc.models import State urlpatterns = patterns('django.views.generic.simple', url(r'^help/state/$', 'direct_to_template', { 'template': 'wgcharter/states.html', 'extra_context': { 'states': State.objects.filter(type="charter") } }, name='help_charter_states'), ) urlpatterns += patterns('', - (r'^/?$', views_search.search_main), - url(r'^create/$', views_edit.edit_info, name="wg_create"), - url(r'^search/$', views_search.search_results, name="wg_search"), - (r'^searchPerson/$', views_search.search_person), - url(r'^area/(?P[A-Za-z0-9.-]+)/$', views_search.by_area, name="wg_search_by_area"), - url(r'^in_process/$', views_search.in_process, name="wg_search_in_process"), - url(r'^(?P[A-Za-z0-9._+-]+)/((?P[0-9][0-9](-[0-9][0-9])?)/)?((?Pballot|writeup|history)/)?$', views.wg_main, name="wg_view"), - (r'^(?P[A-Za-z0-9._+-]+)/_ballot.data$', views.wg_ballot), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/$', views_edit.change_state, name='wg_change_state'), - url(r'^(?P[A-Za-z0-9._+-]+)/edit/(?P