From e686c5c7f5758511a99f301e20aec9e9d5b30112 Mon Sep 17 00:00:00 2001 From: Ryan Cross Date: Fri, 9 Jan 2015 21:29:57 +0000 Subject: [PATCH] Allow upload of bluesheets via materials upload interface - Legacy-Id: 8857 --- ietf/doc/models.py | 10 +- ietf/meeting/models.py | 13 + ietf/name/fixtures/names.json | 43 ++ ietf/name/migrations/0030_add_doctype.py | 471 ++++++++++++++++++ ietf/secr/proceedings/forms.py | 9 +- ietf/secr/proceedings/proc_utils.py | 32 +- ietf/secr/proceedings/tests.py | 47 +- ietf/secr/proceedings/views.py | 48 +- .../templates/proceedings/proceedings.html | 6 +- .../templates/proceedings/upload_unified.html | 10 + ietf/secr/utils/meeting.py | 23 +- ietf/utils/test_data.py | 9 + 12 files changed, 658 insertions(+), 63 deletions(-) create mode 100644 ietf/name/migrations/0030_add_doctype.py diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 3125e22cd..b24d898bf 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -79,9 +79,9 @@ class DocumentInfo(models.Model): if self.type_id == "draft": return settings.INTERNET_DRAFT_PATH - elif self.type_id in ("agenda", "minutes", "slides") and self.meeting_related(): - meeting = self.name.split("-")[1] - return os.path.join(settings.AGENDA_PATH, meeting, self.type_id) + "/" + elif self.type_id in ("agenda", "minutes", "slides", "bluesheets") and self.meeting_related(): + meeting = self.session_set.first().meeting + return os.path.join(meeting.get_materials_path(), self.type_id) + "/" elif self.type_id == "charter": return settings.CHARTER_PATH elif self.type_id == "conflrev": @@ -186,7 +186,7 @@ class DocumentInfo(models.Model): return None def meeting_related(self): - return(self.type_id in ("agenda", "minutes", "slides") and ( + return(self.type_id in ("agenda", "minutes", "slides", "bluesheets") and ( self.name.split("-")[1] == "interim" or (self.session_set.exists() if isinstance(self, Document) else self.doc.session_set.exists()))) @@ -263,7 +263,7 @@ class Document(DocumentInfo): name = self.name if self.type_id == "draft" and self.get_state_slug() == "rfc": name = self.canonical_name() - elif self.type_id in ('slides','agenda','minutes'): + elif self.type_id in ('slides','agenda','minutes','bluesheets'): session = self.session_set.first() if session: meeting = session.meeting diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py index 843cb0cc7..5227830bd 100644 --- a/ietf/meeting/models.py +++ b/ietf/meeting/models.py @@ -100,6 +100,19 @@ class Meeting(models.Model): date = cls.objects.all().filter(type="ietf").order_by('-date')[0].date return date + datetime.timedelta(days=-date.weekday(), weeks=1) + def get_materials_path(self): + path = '' + if self.type_id == 'ietf': + path = os.path.join(settings.AGENDA_PATH,self.number) + elif self.type_id == 'interim': + path = os.path.join(settings.AGENDA_PATH, + 'interim', + self.date.strftime('%Y'), + self.date.strftime('%m'), + self.date.strftime('%d'), + self.session_set.all()[0].group.acronym) + return path + # the various dates are currently computed def get_submission_start_date(self): return self.date + datetime.timedelta(days=settings.SUBMISSION_START_DAYS) diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json index 12af57eef..2b4071abc 100644 --- a/ietf/name/fixtures/names.json +++ b/ietf/name/fixtures/names.json @@ -744,6 +744,16 @@ "desc": "" } }, +{ + "pk": "bluesheets", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Bluesheets", + "desc": "" + } +}, { "pk": "uploaded", "model": "name.draftsubmissionstatename", @@ -1905,6 +1915,13 @@ "label": "Recording State" } }, +{ + "pk": "bluesheets", + "model": "doc.statetype", + "fields": { + "label": "Bluesheet State" + } +}, { "pk": 81, "model": "doc.state", @@ -3800,6 +3817,32 @@ "desc": "The RFC status changes have been abandoned" } }, +{ + "pk": 137, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "bluesheets", + "order": 0, + "desc": "" + } +}, +{ + "pk": 138, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "bluesheets", + "order": 1, + "desc": "" + } +}, { "pk": 5, "model": "doc.ballottype", diff --git a/ietf/name/migrations/0030_add_doctype.py b/ietf/name/migrations/0030_add_doctype.py new file mode 100644 index 000000000..4369f964f --- /dev/null +++ b/ietf/name/migrations/0030_add_doctype.py @@ -0,0 +1,471 @@ +# -*- coding: utf-8 -*- +from south.v2 import DataMigration + +class Migration(DataMigration): + + def forwards(self, orm): + "Write your forwards methods here." + # Note: Don't use "from appname.models import ModelName". + # Use orm.ModelName to refer to models in this application, + # and orm['appname.ModelName'] for models in other applications. + orm.DocTypeName.objects.create(slug="bluesheets",name="Bluesheets") + orm['doc.StateType'].objects.create(slug='bluesheets',label='State') + orm['doc.State'].objects.create(type_id='bluesheets',slug='active',name='Active') + orm['doc.State'].objects.create(type_id='bluesheets',slug='deleted',name='Deleted') + + def backwards(self, orm): + "Write your backwards methods here." + orm.DocTypeName.objects.filter(slug='bluesheets').delete() + orm['doc.State'].objects.filter(type='bluesheets').delete() + orm['doc.StateType'].objects.get(slug='bluesheets').delete() + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'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', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}) + }, + u'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'}), + u'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'}) + }, + u'doc.ballotdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotDocEvent', '_ormbases': [u'doc.DocEvent']}, + 'ballot_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.BallotType']"}), + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'doc.ballotpositiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'BallotPositionDocEvent', '_ormbases': [u'doc.DocEvent']}, + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}), + 'ballot': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"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'}), + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'pos': ('django.db.models.fields.related.ForeignKey', [], {'default': "'norecord'", 'to': u"orm['name.BallotPositionName']"}) + }, + u'doc.ballottype': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotType'}, + 'doc_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}), + u'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': u"orm['name.BallotPositionName']", 'symmetrical': 'False', 'blank': 'True'}), + 'question': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'doc.consensusdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': [u'doc.DocEvent']}, + 'consensus': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}), + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'doc.deletedevent': { + 'Meta': {'object_name': 'DeletedEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'json': ('django.db.models.fields.TextField', [], {}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + u'doc.docalias': { + 'Meta': {'object_name': 'DocAlias'}, + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + u'doc.docevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'DocEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Person']"}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'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': u"orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"orm['doc.DocHistoryAuthor']", 'blank': 'True'}), + 'doc': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'history_set'", 'to': u"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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['doc.DocAlias']", 'symmetrical': 'False', 'through': u"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': u"orm['person.Email']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + u'doc.dochistoryauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocHistoryAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {}) + }, + u'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': u"orm['doc.DocEvent']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocReminderTypeName']"}) + }, + u'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': u"orm['person.Person']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['person.Email']", 'symmetrical': 'False', 'through': u"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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'intended_std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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'}), + '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': u"orm['person.Email']"}), + 'states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'std_level': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StdLevelName']", 'null': 'True', 'blank': 'True'}), + 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.StreamName']", 'null': 'True', 'blank': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"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': u"orm['name.DocTypeName']", 'null': 'True', 'blank': 'True'}) + }, + u'doc.documentauthor': { + 'Meta': {'ordering': "['document', 'order']", 'object_name': 'DocumentAuthor'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['person.Email']"}), + 'document': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '1'}) + }, + u'doc.initialreviewdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'InitialReviewDocEvent', '_ormbases': [u'doc.DocEvent']}, + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'doc.lastcalldocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'LastCallDocEvent', '_ormbases': [u'doc.DocEvent']}, + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'doc.newrevisiondocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'NewRevisionDocEvent', '_ormbases': [u'doc.DocEvent']}, + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + u'doc.relateddochistory': { + 'Meta': {'object_name': 'RelatedDocHistory'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocHistory']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reversely_related_document_history_set'", 'to': u"orm['doc.DocAlias']"}) + }, + u'doc.relateddocument': { + 'Meta': {'object_name': 'RelatedDocument'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'relationship': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.DocRelationshipName']"}), + 'source': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.Document']"}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.DocAlias']"}) + }, + u'doc.state': { + 'Meta': {'ordering': "['type', 'order']", 'object_name': 'State'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['doc.State']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'doc.statedocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': [u'doc.DocEvent']}, + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.State']", 'null': 'True', 'blank': 'True'}), + 'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['doc.StateType']"}) + }, + u'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'}) + }, + u'doc.telechatdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'TelechatDocEvent', '_ormbases': [u'doc.DocEvent']}, + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"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'}) + }, + u'doc.writeupdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'WriteupDocEvent', '_ormbases': [u'doc.DocEvent']}, + u'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + u'group.group': { + 'Meta': {'object_name': 'Group'}, + 'acronym': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '40'}), + 'ad': ('django.db.models.fields.related.ForeignKey', [], {'to': u"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': u"orm['doc.Document']"}), + 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + u'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': u"orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupStateName']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['name.GroupTypeName']", 'null': 'True'}), + 'unused_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['doc.State']", 'symmetrical': 'False', 'blank': 'True'}), + 'unused_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['name.DocTagName']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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'}), + 'penalty': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.dbtemplatetypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DBTemplateTypeName'}, + '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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'next_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', 'to': u"orm['name.DraftSubmissionStateName']"}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.feedbacktypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackTypeName'}, + '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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.groupmilestonestatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupMilestoneStateName'}, + '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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.nomineepositionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionStateName'}, + '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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'name.roomresourcename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoomResourceName'}, + '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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': '32', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'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': u"orm['person.Person']", 'null': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + u'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'}), + u'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': u"orm['auth.User']", 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['doc', 'name'] + symmetrical = True diff --git a/ietf/secr/proceedings/forms.py b/ietf/secr/proceedings/forms.py index 3cd56993f..37161b011 100644 --- a/ietf/secr/proceedings/forms.py +++ b/ietf/secr/proceedings/forms.py @@ -17,6 +17,7 @@ from ietf.meeting.models import Meeting, Session VALID_SLIDE_EXTENSIONS = ('.doc','.docx','.pdf','.ppt','.pptx','.txt','.zip') VALID_MINUTES_EXTENSIONS = ('.txt','.html','.htm','.pdf') VALID_AGENDA_EXTENSIONS = ('.txt','.html','.htm') +VALID_BLUESHEET_EXTENSIONS = ('.pdf','.jpg','.jpeg') #---------------------------------------------------------- # Forms @@ -100,7 +101,7 @@ class ReplaceSlideForm(forms.ModelForm): class UnifiedUploadForm(forms.Form): acronym = forms.CharField(widget=forms.HiddenInput()) meeting_id = forms.CharField(widget=forms.HiddenInput()) - material_type = forms.ModelChoiceField(queryset=DocTypeName.objects.filter(name__in=('minutes','agenda','slides')),empty_label=None) + material_type = forms.ModelChoiceField(queryset=DocTypeName.objects.filter(slug__in=('minutes','agenda','slides','bluesheets')),empty_label=None) slide_name = forms.CharField(label='Name of Presentation',max_length=255,required=False,help_text="For presentations only") file = forms.FileField(label='Select File',help_text='
Note 1: You can only upload a presentation file in txt, pdf, doc, or ppt/pptx. System will not accept presentation files in any other format.

Note 2: All uploaded files will be available to the public immediately on the Preliminary Page. However, for the Proceedings, ppt/pptx files will be converted to html format and doc files will be converted to pdf format manually by the Secretariat staff.
') @@ -128,7 +129,7 @@ class UnifiedUploadForm(forms.Form): #if material_type == 1 and not file_ext[1] == '.pdf': # raise forms.ValidationError('Presentations must be a PDF file') - # validate file extensions based on material type (presentation,agenda,minutes) + # validate file extensions based on material type (slides,agenda,minutes,bluesheets) # valid extensions per online documentation: meeting-materials.html # 09-14-11 added ppt, pdf per Alexa # 04-19-12 txt/html for agenda, +pdf for minutes per Russ @@ -138,7 +139,9 @@ class UnifiedUploadForm(forms.Form): raise forms.ValidationError('Only these file types supported for agendas: %s' % ','.join(VALID_AGENDA_EXTENSIONS)) if material_type.slug == 'minutes' and ext not in VALID_MINUTES_EXTENSIONS: raise forms.ValidationError('Only these file types supported for minutes: %s' % ','.join(VALID_MINUTES_EXTENSIONS)) - + if material_type.slug == 'bluesheets' and ext not in VALID_BLUESHEET_EXTENSIONS: + raise forms.ValidationError('Only these file types supported for bluesheets: %s' % ','.join(VALID_BLUESHEET_EXTENSIONS)) + return cleaned_data diff --git a/ietf/secr/proceedings/proc_utils.py b/ietf/secr/proceedings/proc_utils.py index 3c08387a8..a4fad6971 100644 --- a/ietf/secr/proceedings/proc_utils.py +++ b/ietf/secr/proceedings/proc_utils.py @@ -83,7 +83,7 @@ def create_recording(session,meeting,group,url): desc='New revision available', time=doc.time) session.materials.add(doc) - + def mycomp(timeslot): ''' This takes a timeslot object and returns a key to sort by the area acronym or None @@ -330,20 +330,24 @@ def create_proceedings(meeting, group, is_final=False): updated_list = ['RFC %s' % get_rfc_num(x.source) for x in rdocs.filter(relationship='updates')] if updated_list: rfc.msg += 'updated by ' + ','.join(updated_list) - # ---------------------------------------------------------------------- - # check for blue sheets - pattern = os.path.join(meeting_root,'bluesheets','bluesheets-%s-%s-*' % (meeting.number,group.acronym.lower())) - files = glob.glob(pattern) - bluesheets = [] - for name in files: - basename = os.path.basename(name) - obj = {'name': basename, - 'url': url_root + "bluesheets/" + basename} - bluesheets.append(obj) - bluesheets = sorted(bluesheets, key = lambda x: x['name']) - # ---------------------------------------------------------------------- else: - drafts = rfcs = bluesheets = None + drafts = rfcs = None + + # ---------------------------------------------------------------------- + # check for blue sheets + if meeting.number.startswith('interim'): + pattern = os.path.join(meeting_root,'bluesheets','bluesheets-%s*' % (meeting.number)) + else: + pattern = os.path.join(meeting_root,'bluesheets','bluesheets-%s-%s-*' % (meeting.number,group.acronym.lower())) + files = glob.glob(pattern) + bluesheets = [] + for name in files: + basename = os.path.basename(name) + obj = {'name': basename, + 'url': url_root + "bluesheets/" + basename} + bluesheets.append(obj) + bluesheets = sorted(bluesheets, key = lambda x: x['name']) + # the simplest way to display the charter is to place it in a
 block
     # however, because this forces a fixed-width font, different than the rest of
diff --git a/ietf/secr/proceedings/tests.py b/ietf/secr/proceedings/tests.py
index 2a69a2789..ae8c3c95e 100644
--- a/ietf/secr/proceedings/tests.py
+++ b/ietf/secr/proceedings/tests.py
@@ -1,13 +1,21 @@
 import debug                            # pyflakes:ignore
+import os
+import shutil
+
+from StringIO import StringIO
 
 from django.core.urlresolvers import reverse
+from django.conf import settings
 
+from ietf.doc.models import Document
 from ietf.group.models import Group
 from ietf.meeting.models import Meeting, Session
 from ietf.meeting.test_data import make_meeting_test_data
 from ietf.utils.test_data import make_test_data
 from ietf.utils.test_utils import TestCase
 
+from ietf.secr.utils.meeting import get_proceedings_path
+
 SECR_USER='secretary'
 
 class MainTestCase(TestCase):
@@ -45,4 +53,41 @@ class RecordingTestCase(TestCase):
         url = reverse('proceedings_recording_edit', kwargs={'meeting_num':meeting.number,'name':doc.name})
         response = self.client.post(url,dict(external_url=external_url),follow=True)
         self.assertEqual(response.status_code, 200)
-        self.failUnless(external_url in response.content)
\ No newline at end of file
+        self.failUnless(external_url in response.content)
+        
+class BluesheetTestCase(TestCase):
+    def setUp(self):
+        self.proceedings_dir = os.path.abspath("tmp-proceedings-dir")
+        os.mkdir(self.proceedings_dir)
+        settings.AGENDA_PATH = self.proceedings_dir
+        
+        self.interim_listing_dir = os.path.abspath("tmp-interim-listing-dir")
+        os.mkdir(self.interim_listing_dir)
+        settings.SECR_INTERIM_LISTING_DIR = self.interim_listing_dir
+        
+    def tearDown(self):
+        shutil.rmtree(self.proceedings_dir)
+        shutil.rmtree(self.interim_listing_dir)
+        
+    def test_upload(self):
+        make_test_data()
+        meeting = Meeting.objects.filter(type='interim').first()
+        group = Group.objects.get(acronym='mars')
+        Session.objects.create(meeting=meeting,group=group,requested_by_id=1,status_id='sched')
+        url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting.number,'acronym':'mars'})
+        upfile = StringIO('dummy file')
+        upfile.name = "scan1.pdf"
+        self.client.login(username="marschairman", password="marschairman+password")
+        r = self.client.post(url,
+            dict(acronym='mars',meeting_id=meeting.id,material_type='bluesheets',file=upfile),follow=True)
+        self.assertEqual(r.status_code, 200)
+        doc = Document.objects.get(type='bluesheets')
+        self.failUnless(doc.external_url in r.content)
+        self.failUnless(os.path.exists(os.path.join(doc.get_file_path(),doc.external_url)))
+        # test that proceedings has bluesheets on it
+        path = get_proceedings_path(meeting,group)
+        self.failUnless(os.path.exists(path))
+        with open(path) as f:
+            data = f.read()
+        self.failUnless(doc.external_url in data)
+        
\ No newline at end of file
diff --git a/ietf/secr/proceedings/views.py b/ietf/secr/proceedings/views.py
index ac914d872..8f34437fa 100644
--- a/ietf/secr/proceedings/views.py
+++ b/ietf/secr/proceedings/views.py
@@ -21,15 +21,16 @@ from ietf.secr.sreq.forms import GroupSelectForm
 from ietf.secr.utils.decorators import check_permissions, sec_only
 from ietf.secr.utils.document import get_full_path
 from ietf.secr.utils.group import get_my_groups, groups_by_session
-from ietf.secr.utils.meeting import get_upload_root, get_materials, get_timeslot
+from ietf.secr.utils.meeting import get_upload_root, get_materials, get_timeslot, get_proceedings_path, get_proceedings_url
 from ietf.doc.models import Document, DocAlias, DocEvent, State, NewRevisionDocEvent
 from ietf.group.models import Group
 from ietf.ietfauth.utils import has_role, role_required
 from ietf.meeting.models import Meeting, Session, TimeSlot, ScheduledSession
 from ietf.secr.proceedings.forms import EditSlideForm, InterimMeetingForm, RecordingForm, RecordingEditForm, ReplaceSlideForm, UnifiedUploadForm
-from ietf.secr.proceedings.proc_utils import ( gen_acknowledgement, gen_agenda, gen_areas, gen_attendees,
-    gen_group_pages, gen_index, gen_irtf, gen_overview, gen_plenaries, gen_progress, gen_research,
-    gen_training, create_proceedings, create_interim_directory, create_recording )
+from ietf.secr.proceedings.proc_utils import ( gen_acknowledgement, gen_agenda, gen_areas,
+    gen_attendees, gen_group_pages, gen_index, gen_irtf, gen_overview, gen_plenaries,
+    gen_progress, gen_research, gen_training, create_proceedings, create_interim_directory,
+    create_recording )
 
 from ietf.secr.proceedings.models import InterimMeeting    # proxy model
 
@@ -137,27 +138,6 @@ def get_next_order_num(session):
 
     return max_order + 1 if max_order else 1
 
-# --- These could be properties/methods on meeting
-def get_proceedings_path(meeting,group):
-    if meeting.type_id == 'ietf':
-        path = os.path.join(get_upload_root(meeting),group.acronym + '.html')
-    elif meeting.type_id == 'interim':
-        path = os.path.join(get_upload_root(meeting),'proceedings.html')
-    return path
-
-def get_proceedings_url(meeting,group=None):
-    if meeting.type_id == 'ietf':
-        url = "%sproceedings/%s/" % (settings.MEDIA_URL,meeting.number)
-        if group:
-            url = url + "%s.html" % group.acronym
-
-    elif meeting.type_id == 'interim':
-        url = "%sproceedings/interim/%s/%s/proceedings.html" % (
-            settings.MEDIA_URL,
-            meeting.date.strftime('%Y/%m/%d'),
-            group.acronym)
-    return url
-
 def handle_upload_file(file,filename,meeting,subdir):
     '''
     This function takes a file object, a filename and a meeting object and subdir as string.
@@ -173,6 +153,8 @@ def handle_upload_file(file,filename,meeting,subdir):
             os.mkdir(path)
     else:
         path = os.path.join(get_upload_root(meeting),subdir)
+        if not os.path.exists(path):
+            os.makedirs(path)
 
     # agendas and minutes can only have one file instance so delete file if it already exists
     if subdir in ('agenda','minutes'):
@@ -324,7 +306,7 @@ def build(request,meeting_num,acronym):
     meeting = Meeting.objects.get(number=meeting_num)
     group = get_object_or_404(Group,acronym=acronym)
 
-    create_proceedings(meeting,group)
+    create_proceedings(meeting,group,is_final=True)
 
     messages.success(request,'proceedings.html was rebuilt')
     url = reverse('proceedings_upload_unified', kwargs={'meeting_num':meeting_num,'acronym':acronym})
@@ -877,8 +859,8 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
         session = sessions[0]
         session_name = ''
     elif session_id:
-        sessions = None
         session = get_object_or_404(Session, id=int(session_id))
+        sessions = [session]
         group = session.group
         session_name = session.name
 
@@ -945,11 +927,13 @@ def upload_unified(request, meeting_num, acronym=None, session_id=None):
 
             # create session relationship, per Henrik we should associate documents to all sessions
             # for the current meeting (until tools support different materials for diff sessions)
-            if sessions:
-                for s in sessions:
-                    s.materials.add(doc)
-            else:
-                session.materials.add(doc)
+            for s in sessions:
+                try:
+                    sp = s.sessionpresentation_set.get(document=doc)
+                    sp.rev = doc.rev
+                    sp.save()
+                except ObjectDoesNotExist:
+                    s.sessionpresentation_set.create(document=doc,rev=doc.rev)
 
             # create NewRevisionDocEvent instead of uploaded, per Ole
             NewRevisionDocEvent.objects.create(type='new_revision',
diff --git a/ietf/secr/templates/proceedings/proceedings.html b/ietf/secr/templates/proceedings/proceedings.html
index 4f81613f0..245952c3b 100644
--- a/ietf/secr/templates/proceedings/proceedings.html
+++ b/ietf/secr/templates/proceedings/proceedings.html
@@ -85,10 +85,10 @@ and end with
 

Recordings:

-{% if materials.record %} +{% if materials.recording %} {% else %} diff --git a/ietf/secr/templates/proceedings/upload_unified.html b/ietf/secr/templates/proceedings/upload_unified.html index 0b2d64b7f..8d2c18b18 100755 --- a/ietf/secr/templates/proceedings/upload_unified.html +++ b/ietf/secr/templates/proceedings/upload_unified.html @@ -1,4 +1,5 @@ {% extends "base_site.html" %} +{% load ietf_filters %} {% block title %}Proceedings{% endblock %} @@ -71,7 +72,16 @@ (not uploaded) {% endif %} + + {% if materials.bluesheets %} + {% for item in materials.bluesheets %} + + Bluesheet + {{ item.external_url }} + {% if user|has_role:"Secretariat" %}Delete{% endif %} + {% endfor %} + {% endif %} diff --git a/ietf/secr/utils/meeting.py b/ietf/secr/utils/meeting.py index bcd29a226..8c78bd7c7 100644 --- a/ietf/secr/utils/meeting.py +++ b/ietf/secr/utils/meeting.py @@ -14,7 +14,7 @@ def get_materials(group,meeting): NOTE, if the group has multiple sessions all materials but recordings will be attached to all sessions. ''' - materials = dict(slides=[],recording=[]) + materials = dict(slides=[],recording=[],bluesheets=[]) # TODO: status should only be sched, but there is a bug in the scheduler for session in Session.objects.filter(group=group,meeting=meeting,status__in=('sched','schedw')): for doc in session.materials.exclude(states__slug='deleted').order_by('order'): @@ -24,12 +24,25 @@ def get_materials(group,meeting): materials[doc.type.slug].append(doc) return materials -def get_proceedings_path(meeting, group): - if meeting.type.slug == 'interim': +def get_proceedings_path(meeting,group): + if meeting.type_id == 'ietf': + path = os.path.join(get_upload_root(meeting),group.acronym + '.html') + elif meeting.type_id == 'interim': path = os.path.join(get_upload_root(meeting),'proceedings.html') - else: - path = os.path.join(get_upload_root(meeting),'%s.html' % group.acronym) return path + +def get_proceedings_url(meeting,group=None): + if meeting.type_id == 'ietf': + url = "%sproceedings/%s/" % (settings.MEDIA_URL,meeting.number) + if group: + url = url + "%s.html" % group.acronym + + elif meeting.type_id == 'interim': + url = "%sproceedings/interim/%s/%s/proceedings.html" % ( + settings.MEDIA_URL, + meeting.date.strftime('%Y/%m/%d'), + group.acronym) + return url def get_session(timeslot, schedule=None): ''' diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 73b405f3a..a27c604f1 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -272,6 +272,15 @@ def make_test_data(): reg_area="Lobby", ) + # interim meeting + Meeting.objects.create( + number="interim-2015-mars-1", + type_id='interim', + date=datetime.date(2015,1,1), + city="New York", + country="US", + ) + # an independent submission before review doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft') doc.set_state(State.objects.get(used=True, type="draft", slug="active"))