diff --git a/bin/mkdiagram b/bin/mkdiagram index 765a7ba52..ef50ce673 100755 --- a/bin/mkdiagram +++ b/bin/mkdiagram @@ -9,7 +9,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ($0 $*)"; exit if [ "$*" ]; then apps="$@"; graph="${1%.*}"; else apps=$(ls */models.py | sed 's!/models.py!!'); graph="models"; fi newapps="doc group meeting message person name" -legacyapps="announcements idindex idrfc idtracker iesg ietfauth ietfworkflows ipr liaisons mailinglists proceedings redirects submit wgchairs wgcharter wginfo" +legacyapps="announcements idindex idrfc idtracker iesg ietfauth ipr liaisons mailinglists proceedings redirects submit wgcharter wginfo" proxy="$(grep ^class */proxy.py | tr '()' ' ' | awk '{printf $2 ","}')" names="$(grep ^class name/models.py | tr '()' ' ' | awk '{printf $2 ","}')" diff --git a/ietf/bin/expire-submissions b/ietf/bin/expire-submissions new file mode 100644 index 000000000..5801f721e --- /dev/null +++ b/ietf/bin/expire-submissions @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +import datetime, os +import syslog + +from ietf import settings +from django.core import management +management.setup_environ(settings) + +syslog.openlog(os.path.basename(__file__), syslog.LOG_PID, syslog.LOG_USER) + +from ietf.person.models import Person +from ietf.submit.utils import expirable_submissions, expire_submission + +system = Person.objects.get(name="(System)") + +for sub in expirable_submissions(older_than_days=30): + expire_submission(sub, system) + + syslog.syslog("Expired submission %s of %s-%s" % (sub.pk, sub.name, sub.rev)) diff --git a/ietf/bin/find-submission-confirmation-email-in-postfix-log b/ietf/bin/find-submission-confirmation-email-in-postfix-log index d38c20bb9..2270cb7e8 100644 --- a/ietf/bin/find-submission-confirmation-email-in-postfix-log +++ b/ietf/bin/find-submission-confirmation-email-in-postfix-log @@ -31,7 +31,7 @@ from django.conf import settings from ietf.utils.path import path as Path -from ietf.submit.models import IdSubmissionDetail +from ietf.submit.models import Submission from ietf.doc.models import Document @@ -56,13 +56,13 @@ from_email = settings.IDSUBMIT_FROM_EMAIL if "<" in from_email: from_email = from_email.split("<")[1].split(">")[0] -submission = IdSubmissionDetail.objects.filter(filename=draft).latest('submission_date') +submission = Submission.objects.filter(name=draft).latest('submission_date') document = Document.objects.get(name=draft) emails = [ author.address for author in document.authors.all() ] timestrings = [] -for file in [ Path(settings.INTERNET_DRAFT_PATH) / ("%s-%s.txt"%(draft, submission.revision)), - Path(settings.IDSUBMIT_STAGING_PATH) / ("%s-%s.txt"%(draft, submission.revision)) ]: +for file in [ Path(settings.INTERNET_DRAFT_PATH) / ("%s-%s.txt"%(draft, submission.rev)), + Path(settings.IDSUBMIT_STAGING_PATH) / ("%s-%s.txt"%(draft, submission.rev)) ]: if os.path.exists(file): upload_time = time.localtime(file.mtime) ts = time.strftime("%b %d %H:%M", upload_time) @@ -95,4 +95,3 @@ for log in logfiles: for qi in queue_ids: if qi in line: sys.stdout.write(line) - \ No newline at end of file diff --git a/ietf/bin/test-crawl b/ietf/bin/test-crawl index 405be9341..3c71f01f7 100755 --- a/ietf/bin/test-crawl +++ b/ietf/bin/test-crawl @@ -24,7 +24,7 @@ connection.queries = DontSaveQueries() MAX_URL_LENGTH = 500 SLOW_THRESHOLD = 1.0 -initial = ["/doc/all/", "/doc/in-last-call/"] +initial = ["/doc/all/", "/doc/in-last-call/", "/iesg/decisions/"] visited = set() urls = {} # url -> referrer diff --git a/ietf/community/display.py b/ietf/community/display.py index ac93159d0..3164192cc 100644 --- a/ietf/community/display.py +++ b/ietf/community/display.py @@ -3,7 +3,6 @@ import datetime from django.db.models import Q from django.core.urlresolvers import reverse as urlreverse -from ietf.ietfworkflows.utils import get_state_for_draft from ietf.doc.models import DocAlias, DocEvent diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py index 7f483ad5e..54242c747 100644 --- a/ietf/doc/mails.py +++ b/ietf/doc/mails.py @@ -9,7 +9,7 @@ from django.core.urlresolvers import reverse as urlreverse from ietf.utils.mail import send_mail, send_mail_text from ietf.ipr.search import iprs_from_docs, related_docs -from ietf.doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent +from ietf.doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent, DocTagName from ietf.person.models import Person from ietf.group.models import Group, Role @@ -413,3 +413,66 @@ def email_last_call_expired(doc): doc=doc, url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()), cc="iesg-secretary@ietf.org") + +def stream_state_email_recipients(doc, extra_recipients=[]): + persons = set() + res = [] + for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")).select_related("person", "email"): + res.append(r.formatted_email()) + persons.add(r.person) + + for email in doc.authors.all(): + if email.person not in persons: + res.append(email.formatted_email()) + persons.add(email.person) + + for p in extra_recipients: + if not p in persons: + res.append(p.formatted_email()) + persons.add(p) + + return res + +def email_draft_adopted(request, doc, by, comment): + recipients = stream_state_email_recipients(doc) + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, + u"%s adopted in %s %s" % (doc.name, doc.group.acronym, doc.group.type.name), + 'doc/mail/draft_adopted_email.txt', + dict(doc=doc, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + by=by, + comment=comment)) + +def email_stream_state_changed(request, doc, prev_state, new_state, by, comment=""): + recipients = stream_state_email_recipients(doc) + + state_type = (prev_state or new_state).type + + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, + u"%s changed for %s" % (state_type.label, doc.name), + 'doc/mail/stream_state_changed_email.txt', + dict(doc=doc, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + state_type=state_type, + prev_state=prev_state, + new_state=new_state, + by=by, + comment=comment)) + +def email_stream_tags_changed(request, doc, added_tags, removed_tags, by, comment=""): + extra_recipients = [] + + if DocTagName.objects.get(slug="sheph-u") in added_tags and doc.shepherd: + extra_recipients.append(doc.shepherd) + + recipients = stream_state_email_recipients(doc, extra_recipients) + + send_mail(request, recipients, settings.DEFAULT_FROM_EMAIL, + u"Tags changed for %s" % doc.name, + 'doc/mail/stream_tags_changed_email.txt', + dict(doc=doc, + url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(), + added=added_tags, + removed=removed_tags, + by=by, + comment=comment)) diff --git a/ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py b/ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py new file mode 100644 index 000000000..f12b880d9 --- /dev/null +++ b/ietf/doc/migrations/0015_split_state_changing_iesg_approve_events.py @@ -0,0 +1,435 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + state_mapping = dict((s.name.lower(), s) for s in orm.State.objects.filter(type="draft-iesg")) + doc_type_mapping = { + 'draft': " the document", + 'conflrev': " the conflict review response", + 'statchg': " the status change", + 'charter': " the charter", + } + + import re + approve_re = re.compile(r"IESG has approved( the charter| the document| the conflict review response| the status change)?( and state has been changed to '?(.*?)'?( by .*)?\.?)?$") + disapprove_re = re.compile("(DNP|Do Not Publish) note has been sent to( the)? RFC Editor( and state has been changed to '?(.*?)'?( by .*)?\.?)?$") + for e in orm.DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved")).order_by("id").select_related("doc").iterator(): + if e.type == "iesg_approved": + m = approve_re.match(e.desc) + if not m: + print "FAIL", e.desc + else: + doc_type, state_set, state_name, by = m.groups() + + if state_set: + state = state_mapping[state_name.lower()] + + s = orm.StateDocEvent() + s.doc_id = e.doc_id + s.by_id = e.by_id + s.type = "changed_state" + s.state_type_id = state.type_id + s.state = state + s.desc = "%s changed to %s" % (state.type.label, state.name) + s.time = e.time + s.save() + + #print e.doc_id, s.desc + + if not doc_type: + doc_type = doc_type_mapping[e.doc.type_id] + + new_desc = "IESG has approved%s" % doc_type + + if new_desc != e.desc: + #print e.doc_id, e.desc, "->", new_desc + orm.DocEvent.objects.filter(id=e.id).update(desc=new_desc) + + elif e.type == "iesg_disapproved": + m = disapprove_re.match(e.desc) + if not m: + print "FAIL", e.desc + else: + dnp, the, state_set, state_name, by = m.groups() + + if state_set: + state = state_mapping[state_name.lower()] + + s = orm.StateDocEvent() + s.doc_id = e.doc_id + s.by_id = e.by_id + s.type = "changed_state" + s.state_type_id = state.type_id + s.state = state + s.desc = "%s changed to %s" % (state.type.label, state.name) + s.time = e.time + s.save() + + #print e.doc_id, s.desc + + new_desc = "Do Not Publish note has been sent to the RFC Editor" + + if new_desc != e.desc: + #print e.doc_id, e.desc, "->", new_desc + orm.DocEvent.objects.filter(id=e.id).update(desc=new_desc) + + + def backwards(self, orm): + "Write your backwards methods here." + + + 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', [], {'default': 'None', '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.consensusdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': ['doc.DocEvent']}, + 'consensus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'doc.deletedevent': { + 'Meta': {'object_name': 'DeletedEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + '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'}) + }, + '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', 'db_index': 'True'}), + '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'}), + '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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', '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.statedocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.State']", 'null': 'True', 'blank': 'True'}), + 'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}) + }, + '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'}, + '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'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + '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/0016_add_docevent_time_index.py b/ietf/doc/migrations/0016_add_docevent_time_index.py new file mode 100644 index 000000000..d77cff9c7 --- /dev/null +++ b/ietf/doc/migrations/0016_add_docevent_time_index.py @@ -0,0 +1,370 @@ +# 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 index on 'DocEvent', fields ['time'] + db.create_index('doc_docevent', ['time']) + + + def backwards(self, orm): + + # Removing index on 'DocEvent', fields ['time'] + db.delete_index('doc_docevent', ['time']) + + + 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', [], {'default': 'None', '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.consensusdocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'ConsensusDocEvent', '_ormbases': ['doc.DocEvent']}, + 'consensus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'doc.deletedevent': { + 'Meta': {'object_name': 'DeletedEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + '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'}) + }, + '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', 'db_index': 'True'}), + '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'}), + '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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', '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.statedocevent': { + 'Meta': {'ordering': "['-time', '-id']", 'object_name': 'StateDocEvent', '_ormbases': ['doc.DocEvent']}, + 'docevent_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['doc.DocEvent']", 'unique': 'True', 'primary_key': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.State']", 'null': 'True', 'blank': 'True'}), + 'state_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['doc.StateType']"}) + }, + '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'}, + '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'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + '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/models.py b/ietf/doc/models.py index 5632077f9..7cfdcf55b 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -80,6 +80,8 @@ class DocumentInfo(models.Model): return settings.CHARTER_PATH elif self.type_id == "conflrev": return settings.CONFLICT_REVIEW_PATH + elif self.type_id == "statchg": + return settings.STATUS_CHANGE_PATH else: raise NotImplemented @@ -120,7 +122,7 @@ class DocumentInfo(models.Model): if not hasattr(self, "state_cache") or self.state_cache == None: self.state_cache = {} - for s in self.states.all().select_related(): + for s in self.states.all().select_related("type"): self.state_cache[s.type_id] = s return self.state_cache.get(state_type, None) @@ -147,8 +149,8 @@ class DocumentInfo(models.Model): def active_ballot(self): """Returns the most recently created ballot if it isn't closed.""" - ballot = self.latest_event(BallotDocEvent, type="created_ballot") - if ballot and self.ballot_open(ballot.ballot_type.slug): + ballot = self.latest_event(BallotDocEvent, type__in=("created_ballot", "closed_ballot")) + if ballot and ballot.type == "created_ballot": return ballot else: return None @@ -306,7 +308,7 @@ class Document(DocumentInfo): if g: if g.type_id == "area": return g.acronym - elif g.type_id != "individ": + elif g.type_id != "individ" and g.parent: return g.parent.acronym else: return None @@ -339,7 +341,7 @@ class Document(DocumentInfo): return self.latest_event(LastCallDocEvent,type="sent_last_call") def displayname_with_link(self): - return '%s-%s' % (self.get_absolute_url(), self.name , self.rev) + return mark_safe('%s-%s' % (self.get_absolute_url(), self.name , self.rev)) def rfc_number(self): n = self.canonical_name() @@ -568,7 +570,7 @@ EVENT_TYPES = [ class DocEvent(models.Model): """An occurrence for a document, used for tracking who, when and what.""" - time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened") + time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened", db_index=True) type = models.CharField(max_length=50, choices=EVENT_TYPES) by = models.ForeignKey(Person) doc = models.ForeignKey('doc.Document') @@ -614,17 +616,15 @@ class BallotDocEvent(DocEvent): active_ads = list(Person.objects.filter(role__name="ad", role__group__state="active")) res = {} - if self.doc.latest_event(BallotDocEvent, type="created_ballot") == self: - - positions = BallotPositionDocEvent.objects.filter(type="changed_ballot_position",ad__in=active_ads, ballot=self).select_related('ad', 'pos').order_by("-time", "-id") - - for pos in positions: - if pos.ad not in res: - res[pos.ad] = pos - - for ad in active_ads: - if ad not in res: - res[ad] = None + positions = BallotPositionDocEvent.objects.filter(type="changed_ballot_position",ad__in=active_ads, ballot=self).select_related('ad', 'pos').order_by("-time", "-id") + + for pos in positions: + if pos.ad not in res: + res[pos.ad] = pos + + for ad in active_ads: + if ad not in res: + res[ad] = None return res def all_positions(self): diff --git a/ietf/doc/proxy.py b/ietf/doc/proxy.py index 21993174c..6b2fbe15c 100644 --- a/ietf/doc/proxy.py +++ b/ietf/doc/proxy.py @@ -194,11 +194,6 @@ class InternetDraft(Document): def authors(self): return IDAuthor.objects.filter(document=self) - @property - def protowriteup_set(self): - from ietf.wgchairs.models import ProtoWriteUpProxy - return ProtoWriteUpProxy.objects.filter(doc=self, type="changed_protocol_writeup") - # methods from InternetDraft def displayname(self): return self.name diff --git a/ietf/doc/templatetags/ballot_icon.py b/ietf/doc/templatetags/ballot_icon.py index 3604beaa8..18ca432cb 100644 --- a/ietf/doc/templatetags/ballot_icon.py +++ b/ietf/doc/templatetags/ballot_icon.py @@ -40,31 +40,35 @@ from django.utils.safestring import mark_safe from ietf.ietfauth.utils import user_is_person, has_role from ietf.doc.models import BallotDocEvent, BallotPositionDocEvent, IESG_BALLOT_ACTIVE_STATES, IESG_SUBSTATE_TAGS +from ietf.name.models import BallotPositionName register = template.Library() -def render_ballot_icon(user, doc): +@register.filter +def showballoticon(doc): + if doc.type_id == "draft": + if doc.get_state_slug("draft-iesg") not in IESG_BALLOT_ACTIVE_STATES: + return False + elif doc.type_id == "charter": + if doc.get_state_slug() not in ("intrev", "iesgrev"): + return False + elif doc.type_id == "conflrev": + if doc.get_state_slug() not in ("iesgeval","defer"): + return False + elif doc.type_id == "statchg": + if doc.get_state_slug() not in ("iesgeval","defer"): + return False + + return True + + +def render_ballot_icon(doc, user): if not doc: return "" - # FIXME: temporary backwards-compatibility hack - from ietf.doc.models import Document - if not isinstance(doc, Document): - doc = doc._draft - - if doc.type_id == "draft": - if doc.get_state_slug("draft-iesg") not in IESG_BALLOT_ACTIVE_STATES: - return "" - elif doc.type_id == "charter": - if doc.get_state_slug() not in ("intrev", "iesgrev"): - return "" - elif doc.type_id == "conflrev": - if doc.get_state_slug() not in ("iesgeval","defer"): - return "" - elif doc.type_id == "statchg": - if doc.get_state_slug() not in ("iesgeval","defer"): - return "" + if not showballoticon(doc): + return "" ballot = doc.active_ballot() if not ballot: @@ -119,7 +123,7 @@ class BallotIconNode(template.Node): self.doc_var = doc_var def render(self, context): doc = template.resolve_variable(self.doc_var, context) - return render_ballot_icon(context.get("user"), doc) + return render_ballot_icon(doc, context.get("user")) def do_ballot_icon(parser, token): try: @@ -132,29 +136,24 @@ register.tag('ballot_icon', do_ballot_icon) @register.filter -def my_position(doc, user): - if not has_role(user, "Area Director"): +def ballotposition(doc, user): + if not showballoticon(doc) or not has_role(user, "Area Director"): return None - # FIXME: temporary backwards-compatibility hack - from ietf.doc.models import Document - if not isinstance(doc, Document): - doc = doc._draft ballot = doc.active_ballot() - pos = "No Record" - if ballot: - changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__user=user, ballot=ballot) - if changed_pos: - pos = changed_pos.pos.name; + if not ballot: + return None + + changed_pos = doc.latest_event(BallotPositionDocEvent, type="changed_ballot_position", ad__user=user, ballot=ballot) + if changed_pos: + pos = changed_pos.pos + else: + pos = BallotPositionName.objects.get(slug="norecord") return pos -@register.filter() -def state_age_colored(doc): - # FIXME: temporary backwards-compatibility hack - from ietf.doc.models import Document - if not isinstance(doc, Document): - doc = doc._draft +@register.filter +def state_age_colored(doc): if doc.type_id == 'draft': if not doc.get_state_slug() in ["active", "rfc"]: # Don't show anything for expired/withdrawn/replaced drafts @@ -174,7 +173,6 @@ def state_age_colored(doc): Q(desc__istartswith="State Changes to ")| Q(desc__istartswith="Sub state has been changed to ")| Q(desc__istartswith="State has been changed to ")| - Q(desc__istartswith="IESG has approved and state has been changed to")| Q(desc__istartswith="IESG process started in state") ).order_by('-time')[0].time.date() except IndexError: diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 1b9b8b493..638d739f7 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -9,6 +9,7 @@ from django.template.defaultfilters import linebreaksbr, wordwrap, stringfilter, from django.template import resolve_variable from django.utils.safestring import mark_safe, SafeData from django.utils import simplejson +from django.utils.html import strip_tags try: from email import utils as emailutils except ImportError: @@ -276,6 +277,10 @@ def truncate_ellipsis(text, arg): else: return escape(text) +@register.filter +def split(text, splitter=None): + return text.split(splitter) + @register.filter(name="wrap_long_lines") def wrap_long_lines(text): """Wraps long lines without loosing the formatting and indentation @@ -413,14 +418,6 @@ def equal(x, y): def startswith(x, y): return unicode(x).startswith(y) -# based on http://www.djangosnippets.org/snippets/847/ by 'whiteinge' -@register.filter -def in_group(user, groups): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return has_role(user, groups.replace("Area_Director", "Area Director")) - - return user and user.is_authenticated() and bool(user.groups.filter(name__in=groups.split(',')).values('name')) - @register.filter def has_role(user, role_names): from ietf.ietfauth.decorators import has_role @@ -475,6 +472,18 @@ def state(doc, slug): slug = "%s-stream-%s" % (doc.type_id, doc.stream_id) return doc.get_state(slug) +@register.filter +def statehelp(state): + "Output help icon with tooltip for state." + from django.core.urlresolvers import reverse as urlreverse + tooltip = escape(strip_tags(state.desc)) + url = urlreverse("state_help", kwargs=dict(type=state.type_id)) + "#" + state.slug + return mark_safe('?' % (url, tooltip)) + +@register.filter +def sectionlevel(section_number): + return section_number.count(".") + 1 + def _test(): import doctest doctest.testmod() @@ -483,13 +492,13 @@ if __name__ == "__main__": _test() @register.filter -def plural(text, list, arg=u's'): +def plural(text, seq, arg=u's'): "Similar to pluralize, but looks at the text, too" from django.template.defaultfilters import pluralize if text.endswith('s'): return text else: - return text + pluralize(len(list), arg) + return text + pluralize(len(seq), arg) @register.filter def ics_esc(text): diff --git a/ietf/doc/templatetags/streams_menu.py b/ietf/doc/templatetags/streams_menu.py new file mode 100644 index 000000000..82f62620e --- /dev/null +++ b/ietf/doc/templatetags/streams_menu.py @@ -0,0 +1,32 @@ +from django import template +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse +from django.contrib.auth.models import AnonymousUser + +from ietf.ietfauth.utils import has_role +from ietf.group.models import Group +from ietf.name.models import StreamName + +register = template.Library() + +@register.inclusion_tag('base/streams_menu.html', takes_context=True) +def streams_menu(context): + editable_streams = [] + + user = context["request"].user if "request" in context else AnonymousUser() + + if user.is_authenticated(): + streams = StreamName.objects.exclude(slug="legacy") + + if has_role(user, "Secretariat"): + editable_streams.extend(streams) + else: + acronyms = Group.objects.filter(acronym__in=(s.slug for s in streams), + role__name="chair", + role__person__user=user).distinct().values_list("acronym", flat=True) + + for s in streams: + if s.slug in acronyms: + editable_streams.append(s) + + return { 'editable_streams': editable_streams } diff --git a/ietf/doc/templatetags/wg_menu.py b/ietf/doc/templatetags/wg_menu.py index 4f58eb6c1..079757734 100644 --- a/ietf/doc/templatetags/wg_menu.py +++ b/ietf/doc/templatetags/wg_menu.py @@ -61,7 +61,7 @@ class WgMenuNode(template.Node): areas = [a for a in areas if a.active_groups] - res = loader.render_to_string('base_wgmenu.html', {'areas':areas}) + res = loader.render_to_string('base/wg_menu.html', {'areas':areas}) cache.set('base_left_wgmenu', x, 30*60) return res diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 80b6e1f3f..390588995 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -24,9 +24,6 @@ from ietf.doc.tests_status_change import * class SearchTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_search(self): draft = make_test_data() @@ -127,9 +124,6 @@ class SearchTestCase(TestCase): class DocTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_document_draft(self): draft = make_test_data() @@ -251,9 +245,6 @@ class DocTestCase(TestCase): class AddCommentTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_add_comment(self): draft = make_test_data() url = urlreverse('doc_add_comment', kwargs=dict(name=draft.name)) diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py index a4ed02c63..d39212cc7 100644 --- a/ietf/doc/tests_ballot.py +++ b/ietf/doc/tests_ballot.py @@ -20,10 +20,7 @@ from ietf.utils.test_data import make_test_data from ietf.utils.mail import outbox from ietf.utils import TestCase -class EditPositionTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class EditPositionTests(TestCase): def test_edit_position(self): draft = make_test_data() url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=draft.name, @@ -170,10 +167,7 @@ class EditPositionTestCase(TestCase): self.assertTrue("Test!" in str(m)) -class DeferBallotTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class DeferBallotTests(TestCase): def test_defer_ballot(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) @@ -217,10 +211,8 @@ class DeferBallotTestCase(TestCase): draft = Document.objects.get(name=draft.name) self.assertEquals(draft.get_state_slug("draft-iesg"), "iesg-eva") -class BallotWriteupsTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class BallotWriteupsTests(TestCase): def test_edit_last_call_text(self): draft = make_test_data() url = urlreverse('doc_ballot_lastcall', kwargs=dict(name=draft.name)) @@ -409,10 +401,8 @@ class BallotWriteupsTestCase(TestCase): draft = Document.objects.get(name=draft.name) self.assertTrue("Subject: Results of IETF-conflict review" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text) -class ApproveBallotTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class ApproveBallotTests(TestCase): def test_approve_ballot(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) # make sure it's approvable @@ -460,10 +450,8 @@ class ApproveBallotTestCase(TestCase): self.assertEquals(len(outbox), mailbox_before + 3) self.assertTrue("NOT be published" in str(outbox[-1])) -class MakeLastCallTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class MakeLastCallTests(TestCase): def test_make_last_call(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="lc-req")) diff --git a/ietf/doc/tests_conflict_review.py b/ietf/doc/tests_conflict_review.py index a61b2b752..7f9acf0e8 100644 --- a/ietf/doc/tests_conflict_review.py +++ b/ietf/doc/tests_conflict_review.py @@ -22,10 +22,7 @@ from ietf.group.models import Person from ietf.iesg.models import TelechatDate -class ConflictReviewTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class ConflictReviewTests(TestCase): def test_start_review(self): doc = Document.objects.get(name='draft-imaginary-independent-submission') @@ -254,10 +251,7 @@ class ConflictReviewTestCase(TestCase): make_test_data() -class ConflictReviewSubmitTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names',] - +class ConflictReviewSubmitTests(TestCase): def test_initial_submission(self): doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission') url = urlreverse('conflict_review_submit',kwargs=dict(name=doc.name)) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index 28e5f1e27..dc746f9ac 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -10,6 +10,7 @@ from pyquery import PyQuery import debug from ietf.doc.models import * +from ietf.doc .utils import * from ietf.name.models import * from ietf.group.models import * from ietf.person.models import * @@ -21,10 +22,7 @@ from ietf.utils.mail import outbox from ietf.utils import TestCase -class ChangeStateTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class ChangeStateTests(TestCase): def test_change_state(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="ad-eval")) @@ -178,10 +176,7 @@ class ChangeStateTestCase(TestCase): self.assertTrue("Last call was requested" in draft.latest_event().desc) -class EditInfoTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class EditInfoTests(TestCase): def test_edit_info(self): draft = make_test_data() url = urlreverse('doc_edit_info', kwargs=dict(name=draft.name)) @@ -361,10 +356,7 @@ class EditInfoTestCase(TestCase): self.assertEqual(draft.latest_event(ConsensusDocEvent, type="changed_consensus").consensus, True) -class ResurrectTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class ResurrectTests(TestCase): def test_request_resurrect(self): draft = make_test_data() draft.set_state(State.objects.get(used=True, type="draft", slug="expired")) @@ -429,10 +421,7 @@ class ResurrectTestCase(TestCase): self.assertEquals(len(outbox), mailbox_before + 1) -class ExpireIDsTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class ExpireIDsTests(TestCase): def setUp(self): self.id_dir = os.path.abspath("tmp-id-dir") self.archive_dir = os.path.abspath("tmp-id-archive") @@ -612,10 +601,8 @@ class ExpireIDsTestCase(TestCase): self.assertTrue(not os.path.exists(os.path.join(self.id_dir, txt))) self.assertTrue(os.path.exists(os.path.join(self.archive_dir, "deleted_tombstones", txt))) -class ExpireLastCallTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class ExpireLastCallTests(TestCase): def test_expire_last_call(self): from ietf.doc.lastcall import get_expired_last_calls, expire_last_call @@ -662,10 +649,8 @@ class ExpireLastCallTestCase(TestCase): self.assertEquals(len(outbox), mailbox_before + 1) self.assertTrue("Last Call Expired" in outbox[-1]["Subject"]) -class IndividualInfoFormsTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class IndividualInfoFormsTests(TestCase): def test_doc_change_stream(self): url = urlreverse('doc_change_stream', kwargs=dict(name=self.docname)) login_testing_unauthorized(self, "secretary", url) @@ -890,10 +875,8 @@ class IndividualInfoFormsTestCase(TestCase): self.docname='draft-ietf-mars-test' self.doc = Document.objects.get(name=self.docname) -class SubmitToIesgTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class SubmitToIesgTests(TestCase): def verify_permissions(self): def verify_fail(remote_user): @@ -951,10 +934,8 @@ class SubmitToIesgTestCase(TestCase): self.doc = Document.objects.get(name=self.docname) self.doc.unset_state('draft-iesg') -class RequestPublicationTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] +class RequestPublicationTests(TestCase): def test_request_publication(self): draft = make_test_data() draft.stream = StreamName.objects.get(slug="iab") @@ -990,3 +971,123 @@ class RequestPublicationTestCase(TestCase): # the IANA copy self.assertTrue("Document Action" in outbox[-1]['Subject']) self.assertTrue(not outbox[-1]['CC']) + +class AdoptDraftTests(TestCase): + def test_adopt_document(self): + draft = make_test_data() + draft.stream = None + draft.group = Group.objects.get(type="individ") + draft.save() + draft.unset_state("draft-stream-ietf") + + url = urlreverse('doc_adopt_draft', kwargs=dict(name=draft.name)) + login_testing_unauthorized(self, "marschairman", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + self.assertEqual(len(q('form select[name="group"] option')), 1) # we can only select "mars" + + # adopt in mars WG + mailbox_before = len(outbox) + events_before = draft.docevent_set.count() + r = self.client.post(url, + dict(comment="some comment", + group=Group.objects.get(acronym="mars").pk, + weeks="10")) + self.assertEqual(r.status_code, 302) + + draft = Document.objects.get(pk=draft.pk) + self.assertEqual(draft.group.acronym, "mars") + self.assertEqual(draft.stream_id, "ietf") + self.assertEqual(draft.docevent_set.count() - events_before, 4) + self.assertEqual(len(outbox), mailbox_before + 1) + self.assertTrue("adopted" in outbox[-1]["Subject"].lower()) + self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) + self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + +class ChangeStreamStateTests(TestCase): + def test_set_tags(self): + draft = make_test_data() + draft.tags = DocTagName.objects.filter(slug="w-expert") + draft.group.unused_tags.add("w-refdoc") + + url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name, state_type="draft-stream-ietf")) + login_testing_unauthorized(self, "marschairman", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + # make sure the unused tags are hidden + unused = draft.group.unused_tags.values_list("slug", flat=True) + for t in q("input[name=tags]"): + self.assertTrue(t.attrib["value"] not in unused) + + # set tags + mailbox_before = len(outbox) + events_before = draft.docevent_set.count() + r = self.client.post(url, + dict(new_state=draft.get_state("draft-stream-%s" % draft.stream_id).pk, + comment="some comment", + weeks="10", + tags=["need-aut", "sheph-u"], + )) + self.assertEqual(r.status_code, 302) + + draft = Document.objects.get(pk=draft.pk) + self.assertEqual(draft.tags.count(), 2) + self.assertEqual(draft.tags.filter(slug="w-expert").count(), 0) + self.assertEqual(draft.tags.filter(slug="need-aut").count(), 1) + self.assertEqual(draft.tags.filter(slug="sheph-u").count(), 1) + self.assertEqual(draft.docevent_set.count() - events_before, 2) + self.assertEqual(len(outbox), mailbox_before + 1) + self.assertTrue("tags changed" in outbox[-1]["Subject"].lower()) + self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) + self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + self.assertTrue("plain@example.com" in unicode(outbox[-1])) + + def test_set_state(self): + draft = make_test_data() + + url = urlreverse('doc_change_stream_state', kwargs=dict(name=draft.name, state_type="draft-stream-ietf")) + login_testing_unauthorized(self, "marschairman", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + q = PyQuery(r.content) + # make sure the unused states are hidden + unused = draft.group.unused_states.values_list("pk", flat=True) + for t in q("select[name=new_state]").find("option[name=tags]"): + self.assertTrue(t.attrib["value"] not in unused) + self.assertEqual(len(q('select[name=new_state]')), 1) + + # set new state + old_state = draft.get_state("draft-stream-%s" % draft.stream_id ) + new_state = State.objects.get(used=True, type="draft-stream-%s" % draft.stream_id, slug="parked") + self.assertNotEqual(old_state, new_state) + mailbox_before = len(outbox) + events_before = draft.docevent_set.count() + + r = self.client.post(url, + dict(new_state=new_state.pk, + comment="some comment", + weeks="10", + tags=[t.pk for t in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))], + )) + self.assertEqual(r.status_code, 302) + + draft = Document.objects.get(pk=draft.pk) + self.assertEqual(draft.get_state("draft-stream-%s" % draft.stream_id), new_state) + self.assertEqual(draft.docevent_set.count() - events_before, 2) + reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s") + self.assertEqual(len(reminder), 1) + due = datetime.datetime.now() + datetime.timedelta(weeks=10) + self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1)) + self.assertEqual(len(outbox), mailbox_before + 1) + self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) + self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) + self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) + diff --git a/ietf/doc/tests_status_change.py b/ietf/doc/tests_status_change.py index b3f4d23d4..36666440d 100644 --- a/ietf/doc/tests_status_change.py +++ b/ietf/doc/tests_status_change.py @@ -22,10 +22,7 @@ from ietf.group.models import Person from ietf.iesg.models import TelechatDate -class StatusChangeTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class StatusChangeTests(TestCase): def test_start_review(self): url = urlreverse('start_rfc_status_change',kwargs=dict(name="")) @@ -353,10 +350,7 @@ class StatusChangeTestCase(TestCase): make_test_data() -class StatusChangeSubmitTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class StatusChangeSubmitTests(TestCase): def test_initial_submission(self): doc = Document.objects.get(name='status-change-imaginary-mid-review') url = urlreverse('status_change_submit',kwargs=dict(name=doc.name)) diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 5ed5c8002..887c4a26f 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -66,7 +66,6 @@ urlpatterns += patterns('', 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_json), (r'^(?P[A-Za-z0-9._+-]+)/ballotpopup/(?P[0-9]+)/$', views_doc.ballot_popup), - #(r'^(?P[A-Za-z0-9._+-]+)/ballot.json$', views_doc.ballot_json), # legacy view url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/$', views_draft.change_state, name='doc_change_state'), # IESG state url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/(?Piana-action|iana-review)/$', views_draft.change_iana_state, name='doc_change_iana_state'), @@ -88,6 +87,8 @@ urlpatterns += patterns('', url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherd/$', views_draft.edit_shepherd, name='doc_edit_shepherd'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/shepherdwriteup/$', views_draft.edit_shepherd_writeup, name='doc_edit_shepherd_writeup'), url(r'^(?P[A-Za-z0-9._+-]+)/edit/requestpublication/$', views_draft.request_publication, name='doc_request_publication'), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/adopt/$', views_draft.adopt_draft, name='doc_adopt_draft'), + url(r'^(?P[A-Za-z0-9._+-]+)/edit/state/(?Pdraft-stream-[a-z]+)/$', views_draft.change_stream_state, name='doc_change_stream_state'), 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'), diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index b211e767d..52b815207 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -5,6 +5,8 @@ from django.conf import settings from ietf.utils import markup_txt from ietf.doc.models import * +from ietf.group.models import Role +from ietf.ietfauth.utils import has_role from ietf.utils import draft @@ -39,6 +41,20 @@ def get_tags_for_stream_id(stream_id): else: return [] +def can_adopt_draft(user, doc): + if not user.is_authenticated(): + return False + + if has_role(user, "Secretariat"): + return True + + return (doc.stream_id in (None, "ietf", "irtf") + and doc.group.type_id == "individ" + and Role.objects.filter(name__in=("chair", "delegate", "secr"), + group__type__in=("wg", "rg"), + group__state="active", + person__user=user).exists()) + 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 @@ -227,7 +243,30 @@ def add_state_change_event(doc, by, prev_state, new_state, timestamp=None): e.time = timestamp e.save() return e - + +def update_reminder(doc, reminder_type_slug, event, due_date): + reminder_type = DocReminderTypeName.objects.get(slug=reminder_type_slug) + + try: + reminder = DocReminder.objects.get(event__doc=doc, type=reminder_type, active=True) + except DocReminder.DoesNotExist: + reminder = None + + if due_date: + # activate/update reminder + if not reminder: + reminder = DocReminder(type=reminder_type) + + reminder.event = event + reminder.due = due_date + reminder.active = True + reminder.save() + else: + # deactivate reminder + if reminder: + reminder.active = False + reminder.save() + def prettify_std_name(n): if re.match(r"(rfc|bcp|fyi|std)[0-9]+", n): return n[:3].upper() + " " + n[3:] diff --git a/ietf/doc/views_ballot.py b/ietf/doc/views_ballot.py index b33426008..1e8814c52 100644 --- a/ietf/doc/views_ballot.py +++ b/ietf/doc/views_ballot.py @@ -693,7 +693,7 @@ def approve_ballot(request, name): e = DocEvent(doc=doc, by=login) if action == "do_not_publish": e.type = "iesg_disapproved" - e.desc = "Do Not Publish note has been sent to RFC Editor" + e.desc = "Do Not Publish note has been sent to the RFC Editor" else: e.type = "iesg_approved" e.desc = "IESG has approved the document" diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 37b76363e..97df6b8fa 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -61,12 +61,10 @@ def render_document_top(request, doc, tab, name): ballot = doc.latest_event(BallotDocEvent, type="created_ballot") if doc.type_id in ("draft","conflrev", "statchg"): - # if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot: tabs.append(("IESG Evaluation Record", "ballot", urlreverse("doc_ballot", kwargs=dict(name=name)), ballot, None if ballot else "IESG Evaluation Ballot has not been created yet")) elif doc.type_id == "charter": tabs.append(("IESG Review", "ballot", urlreverse("doc_ballot", kwargs=dict(name=name)), ballot, None if ballot else "IEST Review Ballot has not been created yet")) - # FIXME: if doc.in_ietf_process and doc.ietf_process.has_iesg_ballot: if doc.type_id not in ["conflrev", "statchg"]: tabs.append(("IESG Writeups", "writeup", urlreverse("doc_writeup", kwargs=dict(name=name)), True)) @@ -254,9 +252,11 @@ def document_main(request, name, rev=None): resurrected_by = e.by # stream info + stream_state_type_slug = None stream_state = None if doc.stream: - stream_state = doc.get_state("draft-stream-%s" % doc.stream_id) + stream_state_type_slug = "draft-stream-%s" % doc.stream_id + stream_state = doc.get_state(stream_state_type_slug) stream_tags = doc.tags.filter(slug__in=get_tags_for_stream_id(doc.stream_id)) shepherd_writeup = doc.latest_event(WriteupDocEvent, type="changed_protocol_writeup") @@ -286,13 +286,8 @@ def document_main(request, name, rev=None): # remaining actions actions = [] - if ((not doc.stream_id or doc.stream_id in ("ietf", "irtf")) and group.type_id == "individ" and - (request.user.is_authenticated() and - Role.objects.filter(person__user=request.user, name__in=("chair", "secr", "delegate"), - group__type__in=("wg","rg"), - group__state="active") - or has_role(request.user, "Secretariat"))): - actions.append(("Adopt in Group", urlreverse('edit_adopt', kwargs=dict(name=doc.name)))) + if can_adopt_draft(request.user, doc): + actions.append(("Adopt in Group", urlreverse('doc_adopt_draft', kwargs=dict(name=doc.name)))) if doc.get_state_slug() == "expired" and not resurrected_by and can_edit: actions.append(("Request Resurrect", urlreverse('doc_request_resurrect', kwargs=dict(name=doc.name)))) @@ -366,6 +361,7 @@ def document_main(request, name, rev=None): has_errata=doc.tags.filter(slug="errata"), published=doc.latest_event(type="published_rfc"), file_urls=file_urls, + stream_state_type_slug=stream_state_type_slug, stream_state=stream_state, stream_tags=stream_tags, milestones=doc.groupmilestone_set.filter(state="active"), @@ -742,48 +738,6 @@ def document_json(request, name): return HttpResponse(json.dumps(data, indent=2), mimetype='text/plain') -def ballot_json(request, name): - # REDESIGN: this view needs to be deleted or updated - def get_ballot(name): - from ietf.doc.models import DocAlias - alias = get_object_or_404(DocAlias, name=name) - d = alias.document - from ietf.idtracker.models import InternetDraft, BallotInfo - from ietf.idrfc.idrfc_wrapper import BallotWrapper, IdWrapper, RfcWrapper - id = None - bw = None - dw = None - if (d.type_id=='draft'): - id = get_object_or_404(InternetDraft, name=d.name) - try: - if not id.ballot.ballot_issued: - raise Http404 - except BallotInfo.DoesNotExist: - raise Http404 - - bw = BallotWrapper(id) # XXX Fixme: Eliminate this as we go forward - # Python caches ~100 regex'es -- explicitly compiling it inside a method - # (where you then throw away the compiled version!) doesn't make sense at - # all. - if re.search("^rfc([1-9][0-9]*)$", name): - id.viewing_as_rfc = True - dw = RfcWrapper(id) - else: - dw = IdWrapper(id) - # XXX Fixme: Eliminate 'dw' as we go forward - - try: - b = d.latest_event(BallotDocEvent, type="created_ballot") - except BallotDocEvent.DoesNotExist: - raise Http404 - - return (bw, dw, b, d) - - ballot, doc, b, d = get_ballot(name) - response = HttpResponse(mimetype='text/plain') - response.write(json.dumps(ballot.dict(), indent=2)) - return response - class AddCommentForm(forms.Form): comment = forms.CharField(required=True, widget=forms.Textarea) diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index cba26223e..fe12c7a34 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -15,6 +15,7 @@ from django.db.models import Max from django.conf import settings from django.forms.util import ErrorList from django.contrib.auth.decorators import login_required +from django.template.defaultfilters import pluralize from ietf.utils.mail import send_mail_text, send_mail_message from ietf.ietfauth.decorators import role_required @@ -27,11 +28,6 @@ from ietf.person.forms import EmailsField from ietf.group.models import Group from ietf.secr.lib import jsonapi -from ietf.ietfworkflows.models import Stream -from ietf.ietfworkflows.utils import update_stream -from ietf.ietfworkflows.streams import get_stream_from_draft -from ietf.ietfworkflows.accounts import can_edit_state - from ietf.doc.models import * from ietf.doc.utils import * from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName @@ -402,7 +398,7 @@ def replaces(request, name): return HttpResponseRedirect(doc.get_absolute_url()) else: form = ReplacesForm(doc=doc) - return render_to_response('idrfc/change_replaces.html', + return render_to_response('doc/draft/change_replaces.html', dict(form=form, doc=doc, ), @@ -1180,7 +1176,7 @@ def request_publication(request, name): doc = get_object_or_404(Document, type="draft", name=name, stream__in=("iab", "ise", "irtf")) - if not can_edit_state(request.user, doc): + if not is_authorized_in_doc_stream(request.user, doc): return HttpResponseForbidden("You do not have the necessary permissions to view this page") m = Message() @@ -1255,3 +1251,225 @@ def request_publication(request, name): ), context_instance = RequestContext(request)) +class AdoptDraftForm(forms.Form): + group = forms.ModelChoiceField(queryset=Group.objects.filter(type__in=["wg", "rg"], state="active").order_by("-type", "acronym"), required=True, empty_label=None) + comment = forms.CharField(widget=forms.Textarea, required=False, label="Comment", help_text="Optional comment explaining the reasons for the adoption") + weeks = forms.IntegerField(required=False, label="Expected weeks in adoption state") + + def __init__(self, *args, **kwargs): + user = kwargs.pop("user") + + super(AdoptDraftForm, self).__init__(*args, **kwargs) + + if has_role(user, "Secretariat"): + pass # all groups + else: + self.fields["group"].queryset = self.fields["group"].queryset.filter(role__person__user=user, role__name__in=("chair", "delegate", "secr")).distinct() + + self.fields['group'].choices = [(g.pk, '%s - %s' % (g.acronym, g.name)) for g in self.fields["group"].queryset] + + +@login_required +def adopt_draft(request, name): + doc = get_object_or_404(Document, type="draft", name=name) + + if not can_adopt_draft(request.user, doc): + return HttpResponseForbidden("You don't have permission to access this page") + + if request.method == 'POST': + form = AdoptDraftForm(request.POST, user=request.user) + + if form.is_valid(): + # adopt + by = request.user.get_profile() + + save_document_in_history(doc) + + doc.time = datetime.datetime.now() + + group = form.cleaned_data["group"] + if group.type.slug == "rg": + new_stream = StreamName.objects.get(slug="irtf") + adopt_state_slug = "active" + else: + new_stream = StreamName.objects.get(slug="ietf") + adopt_state_slug = "c-adopt" + + # stream + if doc.stream != new_stream: + e = DocEvent(type="changed_stream", time=doc.time, by=by, doc=doc) + e.desc = u"Changed stream to %s" % new_stream.name + if doc.stream: + e.desc += u" from %s" % doc.stream.name + e.save() + doc.stream = new_stream + + # group + if group != doc.group: + e = DocEvent(type="changed_group", time=doc.time, by=by, doc=doc) + e.desc = u"Changed group to %s (%s)" % (group.name, group.acronym.upper()) + if doc.group.type_id != "individ": + e.desc += " from %s (%s)" % (doc.group.name, doc.group.acronym.upper()) + e.save() + doc.group = group + + doc.save() + + # state + prev_state = doc.get_state("draft-stream-%s" % doc.stream_id) + new_state = State.objects.get(slug=adopt_state_slug, type="draft-stream-%s" % doc.stream_id, used=True) + if new_state != prev_state: + doc.set_state(new_state) + e = add_state_change_event(doc, by, prev_state, new_state, doc.time) + + due_date = None + if form.cleaned_data["weeks"] != None: + due_date = datetime.date.today() + datetime.timedelta(weeks=form.cleaned_data["weeks"]) + + update_reminder(doc, "stream-s", e, due_date) + + # comment + comment = form.cleaned_data["comment"].strip() + if comment: + e = DocEvent(type="added_comment", time=doc.time, by=by, doc=doc) + e.desc = comment + e.save() + + email_draft_adopted(request, doc, by, comment) + + return HttpResponseRedirect(doc.get_absolute_url()) + else: + form = AdoptDraftForm(user=request.user) + + return render_to_response('doc/draft/adopt_draft.html', + {'doc': doc, + 'form': form, + }, + context_instance=RequestContext(request)) + +class ChangeStreamStateForm(forms.Form): + new_state = forms.ModelChoiceField(queryset=State.objects.filter(used=True), label='State', help_text=u"Only select 'Submitted to IESG for Publication' to correct errors. Use the document's main page to request publication.") + weeks = forms.IntegerField(label='Expected weeks in state',required=False) + comment = forms.CharField(widget=forms.Textarea, required=False, help_text="Optional comment for the document history") + tags = forms.ModelMultipleChoiceField(queryset=DocTagName.objects.filter(used=True), widget=forms.CheckboxSelectMultiple, required=False) + + def __init__(self, *args, **kwargs): + doc = kwargs.pop("doc") + state_type = kwargs.pop("state_type") + super(ChangeStreamStateForm, self).__init__(*args, **kwargs) + + f = self.fields["new_state"] + f.queryset = f.queryset.filter(type=state_type) + if doc.group: + unused_states = doc.group.unused_states.values_list("pk", flat=True) + f.queryset = f.queryset.exclude(pk__in=unused_states) + f.label = state_type.label + + f = self.fields['tags'] + f.queryset = f.queryset.filter(slug__in=get_tags_for_stream_id(doc.stream_id)) + if doc.group: + unused_tags = doc.group.unused_tags.values_list("pk", flat=True) + f.queryset = f.queryset.exclude(pk__in=unused_tags) + +def next_states_for_stream_state(doc, state_type, current_state): + # find next states + next_states = [] + if current_state: + next_states = current_state.next_states.all() + + if doc.stream_id == "ietf" and doc.group: + transitions = doc.group.groupstatetransitions_set.filter(state=current_state) + if transitions: + next_states = transitions[0].next_states.all() + else: + # return the initial state + states = State.objects.filter(used=True, type=state_type).order_by('order') + if states: + next_states = states[:1] + + if doc.group: + unused_states = doc.group.unused_states.values_list("pk", flat=True) + next_states = [n for n in next_states if n.pk not in unused_states] + + return next_states + +@login_required +def change_stream_state(request, name, state_type): + doc = get_object_or_404(Document, type="draft", name=name) + if not doc.stream: + raise Http404 + + state_type = get_object_or_404(StateType, slug=state_type) + + if not is_authorized_in_doc_stream(request.user, doc): + return HttpResponseForbidden("You don't have permission to access this page") + + prev_state = doc.get_state(state_type.slug) + next_states = next_states_for_stream_state(doc, state_type, prev_state) + + if request.method == 'POST': + form = ChangeStreamStateForm(request.POST, doc=doc, state_type=state_type) + if form.is_valid(): + by = request.user.get_profile() + + save_document_in_history(doc) + + doc.time = datetime.datetime.now() + comment = form.cleaned_data["comment"].strip() + + # state + new_state = form.cleaned_data["new_state"] + if new_state != prev_state: + doc.set_state(new_state) + e = add_state_change_event(doc, by, prev_state, new_state, doc.time) + + due_date = None + if form.cleaned_data["weeks"] != None: + due_date = datetime.date.today() + datetime.timedelta(weeks=form.cleaned_data["weeks"]) + + update_reminder(doc, "stream-s", e, due_date) + + email_stream_state_changed(request, doc, prev_state, new_state, by, comment) + + # tags + existing_tags = set(doc.tags.all()) + new_tags = set(form.cleaned_data["tags"]) + + if existing_tags != new_tags: + doc.tags = new_tags + + e = DocEvent(type="changed_document", time=doc.time, by=by, doc=doc) + added_tags = new_tags - existing_tags + removed_tags = existing_tags - new_tags + l = [] + if added_tags: + l.append(u"Tag%s %s set." % (pluralize(added_tags), ", ".join(t.name for t in added_tags))) + if removed_tags: + l.append(u"Tag%s %s cleared." % (pluralize(removed_tags), ", ".join(t.name for t in removed_tags))) + e.desc = " ".join(l) + e.save() + + email_stream_tags_changed(request, doc, added_tags, removed_tags, by, comment) + + # comment + if comment: + e = DocEvent(type="added_comment", time=doc.time, by=by, doc=doc) + e.desc = comment + e.save() + + return HttpResponseRedirect(doc.get_absolute_url()) + else: + form = ChangeStreamStateForm(initial=dict(new_state=prev_state.pk if prev_state else None), + doc=doc, state_type=state_type) + + milestones = doc.groupmilestone_set.all() + + + return render_to_response("doc/draft/change_stream_state.html", + {"doc": doc, + "form": form, + "milestones": milestones, + "state_type": state_type, + "next_states": next_states, + }, + context_instance=RequestContext(request)) diff --git a/ietf/doc/views_help.py b/ietf/doc/views_help.py index 68104d624..936c27bf1 100644 --- a/ietf/doc/views_help.py +++ b/ietf/doc/views_help.py @@ -3,12 +3,14 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from ietf.doc.models import * +from ietf.doc.utils import get_tags_for_stream_id def state_help(request, type): slug, title = { "draft-iesg": ("draft-iesg", "IESG States For Internet-Drafts"), "draft-rfceditor": ("draft-rfceditor", "RFC Editor States For Internet-Drafts"), "draft-iana-action": ("draft-iana-action", "IANA Action States For Internet-Drafts"), + "draft-stream-ietf": ("draft-stream-ietf", "IETF Stream States For Internet-Drafts"), "charter": ("charter", "Charter States"), "conflict-review": ("conflrev", "Conflict Review States"), "status-change": ("statchg", "RFC Status Change States"), @@ -35,6 +37,9 @@ def state_help(request, type): states.insert(0, fake_state) tags = DocTagName.objects.filter(slug__in=IESG_SUBSTATE_TAGS) + elif state_type.slug.startswith("draft-stream-"): + possible = get_tags_for_stream_id(state_type.slug.replace("draft-stream-", "")) + tags = DocTagName.objects.filter(slug__in=possible) return render_to_response("doc/state_help.html", { "title": title, @@ -42,5 +47,4 @@ def state_help(request, type): "states": states, "has_next_states": has_next_states, "tags": tags, - }, - context_instance=RequestContext(request)) + }, context_instance=RequestContext(request)) diff --git a/ietf/group/models.py b/ietf/group/models.py index 1f70f2e1c..ae69dc5f9 100644 --- a/ietf/group/models.py +++ b/ietf/group/models.py @@ -58,26 +58,15 @@ class Group(GroupInfo): e = model.objects.filter(group=self).filter(**filter_args).order_by('-time', '-id')[:1] return e[0] if e else None - def is_chair(self, user): - chair = self.get_chair() - if chair: - return self.get_chair().person.user == user - else: - return False - - def is_member(self, user): - members = self.get_members() - users = [member.person.user for member in members] - return user in users + def has_role(self, user, role_names): + if isinstance(role_names, str) or isinstance(role_names, unicode): + role_names = [role_names] + return user.is_authenticated() and self.role_set.filter(name__in=role_names, person__user=user).exists() def get_chair(self): chair = self.role_set.filter(name__slug='chair')[:1] return chair and chair[0] or None - def get_members(self): - members = self.role_set.filter(name__slug__in=["chair", "member", "advisor", "liaison"]) - return members - # these are copied to Group because it is still proxied. @property def upcase_acronym(self): diff --git a/ietf/group/proxy.py b/ietf/group/proxy.py index c76759524..52c5ce487 100644 --- a/ietf/group/proxy.py +++ b/ietf/group/proxy.py @@ -94,6 +94,19 @@ def proxied_role_emails(emails): proxy_role_email(e) return emails +class WGDelegateProxy(Role): + #person = models.ForeignKey(PersonOrOrgInfo) # same name + #wg = models.ForeignKey(IETFWG) + @property + def wg(self): + return self.group + + def __unicode__(self): + return u"%s" % self.person + + class Meta: + proxy = True + class IETFWG(Group): objects = TranslatingManager(dict(group_acronym="id", group_acronym__acronym="acronym", @@ -211,8 +224,7 @@ class IETFWG(Group): return d @property def wgdelegate_set(self): - from ietf.wgchairs.models import WGDelegate - return WGDelegate.objects.filter(group=self, name="delegate") + return WGDelegateProxy.objects.filter(group=self, name="delegate") class Meta: proxy = True diff --git a/ietf/group/stream_urls.py b/ietf/group/stream_urls.py index c256c41a7..39fbb7176 100644 --- a/ietf/group/stream_urls.py +++ b/ietf/group/stream_urls.py @@ -2,13 +2,10 @@ from django.conf.urls.defaults import patterns, include -import views +import views_stream urlpatterns = patterns('', - (r'^$', views.streams), - (r'^(?P[a-zA-Z0-9-]+)/$', views.stream_documents, None), -# (r'^(?P[a-zA-Z0-9-]+)/history/$', views.stream_history), -# (r'^(?P[a-zA-Z0-9-]+)/edit/$', views.stream_edit) - (r'^management/', include('ietf.ietfworkflows.urls')), - + (r'^$', views_stream.streams), + (r'^(?P[a-zA-Z0-9-]+)/$', views_stream.stream_documents, None), + (r'^(?P[a-zA-Z0-9-]+)/edit/$', views_stream.stream_edit), ) diff --git a/ietf/group/tests.py b/ietf/group/tests.py new file mode 100644 index 000000000..423d30897 --- /dev/null +++ b/ietf/group/tests.py @@ -0,0 +1,31 @@ +import os, shutil, datetime + +import django.test +from django.core.urlresolvers import reverse as urlreverse + +from pyquery import PyQuery + +from ietf.utils.mail import outbox +from ietf.utils.test_utils import login_testing_unauthorized +from ietf.utils.test_data import make_test_data + +from ietf.name.models import * +from ietf.group.models import * +from ietf.person.models import * + +class StreamTests(TestCase): + def test_stream_edit(self): + make_test_data() + + stream_acronym = "ietf" + + url = urlreverse("ietf.group.views_stream.stream_edit", kwargs=dict(acronym=stream_acronym)) + login_testing_unauthorized(self, "secretary", url) + + # get + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + + r = self.client.post(url, dict(delegates="ad2@ietf.org")) + self.assertEqual(r.status_code, 302) + self.assertTrue(Role.objects.filter(name="delegate", group__acronym=stream_acronym, email__address="ad2@ietf.org")) diff --git a/ietf/group/views.py b/ietf/group/views.py deleted file mode 100644 index 9d7eefbbd..000000000 --- a/ietf/group/views.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright The IETF Trust 2008, All Rights Reserved - -from django.shortcuts import render_to_response -from django.template import RequestContext, loader -from django.http import Http404, HttpResponse - -from ietf.group.models import Group -from ietf.doc.models import Document -from ietf.doc.views_search import SearchForm, retrieve_search_results -from ietf.name.models import StreamName - -import debug - -def streams(request): - streams = [ s.slug for s in StreamName.objects.all().exclude(slug__in=['ietf', 'legacy']) ] - streams = Group.objects.filter(acronym__in=streams) - return render_to_response('group/index.html', {'streams':streams}, context_instance=RequestContext(request)) - -def stream_documents(request, acronym): - streams = [ s.slug for s in StreamName.objects.all().exclude(slug__in=['ietf', 'legacy']) ] - if not acronym in streams: - raise Http404("No such stream: %s" % acronym) - stream = StreamName.objects.get(slug=acronym) - form = SearchForm({'by':'stream', 'stream':acronym, - 'rfcs':'on', 'activedrafts':'on'}) - docs, meta = retrieve_search_results(form) - return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta }, context_instance=RequestContext(request)) - - \ No newline at end of file diff --git a/ietf/group/views_stream.py b/ietf/group/views_stream.py new file mode 100644 index 000000000..8795f2306 --- /dev/null +++ b/ietf/group/views_stream.py @@ -0,0 +1,75 @@ +# Copyright The IETF Trust 2008, All Rights Reserved + +from django.shortcuts import render_to_response, get_object_or_404, redirect +from django.template import RequestContext, loader +from django.http import Http404, HttpResponse, HttpResponseForbidden +from django import forms + +from ietf.group.models import * +from ietf.group.utils import * +from ietf.doc.models import Document +from ietf.doc.views_search import SearchForm, retrieve_search_results +from ietf.name.models import StreamName +from ietf.ietfauth.utils import has_role +from ietf.person.forms import EmailsField + +import debug + +def streams(request): + streams = [ s.slug for s in StreamName.objects.all().exclude(slug__in=['ietf', 'legacy']) ] + streams = Group.objects.filter(acronym__in=streams) + return render_to_response('group/index.html', {'streams':streams}, context_instance=RequestContext(request)) + +def stream_documents(request, acronym): + streams = [ s.slug for s in StreamName.objects.all().exclude(slug__in=['ietf', 'legacy']) ] + if not acronym in streams: + raise Http404("No such stream: %s" % acronym) + stream = StreamName.objects.get(slug=acronym) + form = SearchForm({'by':'stream', 'stream':acronym, + 'rfcs':'on', 'activedrafts':'on'}) + docs, meta = retrieve_search_results(form) + return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta }, context_instance=RequestContext(request)) + +class StreamEditForm(forms.Form): + delegates = EmailsField(label="Delegates", required=False, help_text=u"Type in name to search for person") + +def stream_edit(request, acronym): + group = get_object_or_404(Group, acronym=acronym) + + if not (has_role(request.user, "Secretariat") or group.has_role(request.user, "chair")): + return HttpResponseForbidden("You don't have permission to access this page.") + + chairs = Email.objects.filter(role__group=group, role__name="chair").select_related("person") + + if request.method == 'POST': + form = StreamEditForm(request.POST) + + if form.is_valid(): + save_group_in_history(group) + + # update roles + attr, slug, title = ('delegates', 'delegate', "Delegates") + + new = form.cleaned_data[attr] + old = Email.objects.filter(role__group=group, role__name=slug).select_related("person") + if set(new) != set(old): + desc = "%s changed to %s from %s" % ( + title, ", ".join(x.get_name() for x in new), ", ".join(x.get_name() for x in old)) + + GroupEvent.objects.create(group=group, by=request.user.get_profile(), type="info_changed", desc=desc) + + group.role_set.filter(name=slug).delete() + for e in new: + Role.objects.get_or_create(name_id=slug, email=e, group=group, person=e.person) + + return redirect("ietf.group.views.streams") + else: + form = StreamEditForm(initial=dict(delegates=Email.objects.filter(role__group=group, role__name="delegate"))) + + return render_to_response('group/stream_edit.html', + {'group': group, + 'chairs': chairs, + 'form': form, + }, + context_instance=RequestContext(request)) + diff --git a/ietf/idindex/tests.py b/ietf/idindex/tests.py index 86ab08169..9e42ef588 100644 --- a/ietf/idindex/tests.py +++ b/ietf/idindex/tests.py @@ -9,10 +9,7 @@ from ietf.doc.models import * from ietf.idindex.index import * -class IndexTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class IndexTests(TestCase): def setUp(self): self.id_dir = os.path.abspath("tmp-id-dir") os.mkdir(self.id_dir) diff --git a/ietf/idrfc/.gitignore b/ietf/idrfc/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/idrfc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/idrfc/__init__.py b/ietf/idrfc/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/ietf/idrfc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/ietf/idrfc/idrfc_wrapper.py b/ietf/idrfc/idrfc_wrapper.py deleted file mode 100644 index feb57d199..000000000 --- a/ietf/idrfc/idrfc_wrapper.py +++ /dev/null @@ -1,967 +0,0 @@ -# Copyright (C) 2009-2010 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 ietf.idtracker.models import InternetDraft, IDInternal, BallotInfo, IESGDiscuss, IESGLogin, DocumentComment, Acronym, IDState -from ietf.idrfc.models import RfcEditorQueue -from ietf.ipr.models import IprRfc, IprDraft, IprDetail -from ietf.doc.models import BallotDocEvent - -import re -from datetime import date -from django.utils import simplejson as json -from django.db.models import Q -from django.db import models -from django.core.urlresolvers import reverse -from django.conf import settings -import types -import debug - -BALLOT_ACTIVE_STATES = ['In Last Call', - 'Waiting for Writeup', - 'Waiting for AD Go-Ahead', - 'IESG Evaluation', - 'IESG Evaluation - Defer'] - -def jsonify_helper(obj, keys): - result = {} - for k in keys: - if hasattr(obj, k): - v = getattr(obj, k) - if callable(v): - v = v() - if v == None: - pass - elif isinstance(v, (types.StringType, types.IntType, types.BooleanType, types.LongType, types.ListType, types.UnicodeType)): - result[k] = v - elif isinstance(v, date): - result[k] = str(v) - else: - result[k] = 'Unknown type '+str(type(v)) - return result - -# Wrappers to make writing templates less painful - -# --------------------------------------------------------------------------- - -class IdWrapper: - _draft = None - _idinternal = None - - is_id_wrapper = True - is_rfc_wrapper = False - - draft_name = None - # Active/Expired/RFC/Withdrawn by Submitter/Replaced/Withdrawn by IETF - draft_status = None - # Revision is sometimes incorrect (+1 too large) if status != Active - latest_revision = None - # Set if and only if draft_status is "RFC" - rfc_number = None - title = None - tracker_id = None - publication_date = None - ietf_process = None - - def __init__(self, draft): - self.id = self - if isinstance(draft, IDInternal) and not settings.USE_DB_REDESIGN_PROXY_CLASSES: - self._idinternal = draft - self._draft = self._idinternal.draft - else: - self._draft = draft - if draft.idinternal: - self._idinternal = draft.idinternal - if self._idinternal: - self.ietf_process = IetfProcessData(self._idinternal) - - self.draft_name = self._draft.filename - self.draft_status = str(self._draft.status) - if self.draft_status == "RFC": - if self._draft.rfc_number: - self.rfc_number = self._draft.rfc_number - else: - # Handle incorrect database entries - self.draft_status = "Expired" - self.latest_revision = self._draft.revision_display() - self.title = self._draft.title - self.tracker_id = self._draft.id_document_tag - self.resurrect_requested_by = self._idinternal.resurrect_requested_by if self._idinternal else None - self.publication_date = self._draft.revision_date - if not self.publication_date: - # should never happen -- but unfortunately it does. Return an - # obviously bogus date - self.publication_date = date(1990,1,1) - - def rfc_editor_state(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - s = self._draft.get_state("draft-rfceditor") - if s: - # extract possible extra annotations - tags = self._draft.tags.filter(slug__in=("iana", "ref")) - return "*".join([s.name] + [t.slug.upper() for t in tags]) - else: - return None - - try: - qs = self._draft.rfc_editor_queue_state - return qs.state - except RfcEditorQueue.DoesNotExist: - pass - return None - - def replaced_by(self): - try: - if self._draft.replaced_by: - return [self._draft.replaced_by.filename] - except InternetDraft.DoesNotExist: - pass - return None - def replaces(self): - r = [str(r.filename) for r in self._draft.replaces_set.all()] - if len(r) > 0: - return r - else: - return None - def in_ietf_process(self): - return self.ietf_process != None - - def submission(self): - - if self._draft.stream_id != u'ietf': - return self._draft.stream - - if self._draft.group_id == Acronym.INDIVIDUAL_SUBMITTER: - return "Individual" - - if self._draft.group and self._draft.group.type_id == "area": - return u"Individual in %s area" % self._draft.group.acronym - - a = self.group_acronym() - if a: - if self._draft.stream_id == "ietf" and self._draft.get_state_slug("draft-stream-ietf") == "c-adopt": - return "candidate for %s WG" % (a, a) - - return "%s WG" % (a, a) - - return "" - submission.allow_tags = True - - def search_archive(self): - - if self._idinternal and self._idinternal.stream in ("IRTF","ISE"): - return "www.ietf.org/mail-archive/web/" - - if self._draft.group_id == Acronym.INDIVIDUAL_SUBMITTER or (settings.USE_DB_REDESIGN_PROXY_CLASSES and self._draft.group.type_id == "area"): - return "www.ietf.org/mail-archive/web/" - - a = self._draft.group_ml_archive() - if a: - return a - - return "" - - def file_types(self): - return self._draft.file_type.split(",") - - def group_acronym(self): - if self._draft.group_id != 0 and self._draft.group != None and str(self._draft.group) != "none": - if settings.USE_DB_REDESIGN_PROXY_CLASSES and self._draft.group.type_id == "area": - return None - return str(self._draft.group) - else: - return None - - # TODO: Returning integers here isn't nice - # 0=Unknown, 1=IETF, 2=IAB, 3=IRTF, 4=Independent - def stream_id(self): - if self.draft_name.startswith("draft-iab-"): - return 2 - elif self.draft_name.startswith("draft-irtf-"): - return 3 - elif self._idinternal: - if self._idinternal.stream == "ISE": - return 4 - else: - return 1 - elif self.group_acronym(): - return 1 - else: - return 0 - - def draft_name_and_revision(self): - return self.draft_name+"-"+self.latest_revision - - def friendly_state(self): - if self.draft_status == "RFC": - return "RFC %d" % (reverse('doc_view', args=['rfc%d' % self.rfc_number]), self.rfc_number) - elif self.draft_status == "Active": - if self.in_ietf_process(): - if self.ietf_process.main_state == "Dead": - # Many drafts in "Dead" state are not dead; they're - # just not currently under IESG processing. Show - # them as "I-D Exists (IESG: Dead)" instead... - return "I-D Exists (IESG: "+self.ietf_process.state+")" - elif self.ietf_process.main_state == "In Last Call": - return self.ietf_process.state + " (ends "+str(self._idinternal.document().lc_expiration_date)+")" - else: - return self.ietf_process.state - else: - return "I-D Exists" - else: - if self.in_ietf_process() and self.ietf_process.main_state == "Dead": - return self.draft_status+" (IESG: "+self.ietf_process.state+")" - # Expired/Withdrawn by Submitter/IETF - return self.draft_status - - def abstract(self): - return self._draft.clean_abstract() - - # TODO: ugly hack - def authors(self): - return self._draft.authors - - def expected_expiration_date(self): - if self.draft_status == "Active" and self._draft.can_expire(): - return self._draft.expiration() - else: - return None - - def ad_name(self): - if self.in_ietf_process(): - return self.ietf_process.ad_name() - else: - return None - - def get_absolute_url(self): - return "/doc/"+self.draft_name+"/" - def displayname_with_link(self): - return '%s' % (self.get_absolute_url(), self.draft_name_and_revision()) - - def underlying_document(self): - """ Expose the Document object underneath the proxy """ - from ietf.doc.models import Document - return Document.objects.get(docalias__name=self.draft_name) - - def to_json(self): - result = jsonify_helper(self, ['draft_name', 'draft_status', 'latest_revision', 'rfc_number', 'title', 'tracker_id', 'publication_date','rfc_editor_state', 'replaced_by', 'replaces', 'in_ietf_process', 'file_types', 'group_acronym', 'stream_id','friendly_state', 'abstract', 'ad_name']) - if self.in_ietf_process(): - result['ietf_process'] = self.ietf_process.dict() - return json.dumps(result, indent=2) - -# --------------------------------------------------------------------------- - -class RfcWrapper: - _rfc = None - _rfcindex = None - _idinternal = None - - is_id_wrapper = False - is_rfc_wrapper = True - - rfc_number = None - title = None - publication_date = None - maturity_level = None - ietf_process = None - draft_name = None - - def __init__(self, rfcindex, rfc=None, idinternal=None): - self._rfcindex = rfcindex - self._rfc = rfc - self._idinternal = idinternal - self.rfc = self - - if not self._idinternal: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - pub = rfcindex.rfc_published_date - started = rfcindex.started_iesg_process if hasattr(rfcindex, 'started_iesg_process') else rfcindex.latest_event(type="started_iesg_process") - if pub and started and pub < started.time.date(): - self._idinternal = rfcindex - else: - try: - self._idinternal = IDInternal.objects.get(rfc_flag=1, draft=self._rfcindex.rfc_number) - except IDInternal.DoesNotExist: - pass - - if self._idinternal: - self.ietf_process = IetfProcessData(self._idinternal) - - self.rfc_number = self._rfcindex.rfc_number - self.title = self._rfcindex.title - self.publication_date = self._rfcindex.rfc_published_date - self.maturity_level = self._rfcindex.current_status - if not self.maturity_level: - self.maturity_level = "Unknown" - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if not rfcindex.name.startswith('rfc'): - self.draft_name = rfcindex.name - return # we've already done the lookup while importing so skip the rest - - ids = InternetDraft.objects.filter(rfc_number=self.rfc_number) - if len(ids) >= 1: - self.draft_name = ids[0].filename - elif self._rfcindex and self._rfcindex.draft: - # rfcindex occasionally includes drafts that were not - # really submitted to IETF (e.g. April 1st) - ids = InternetDraft.objects.filter(filename=self._rfcindex.draft) - if len(ids) > 0: - self.draft_name = self._rfcindex.draft - - def _rfc_doc_list(self, name): - if (not self._rfcindex) or (not getattr(self._rfcindex, name)): - return None - else: - s = getattr(self._rfcindex, name) - s = s.replace(",", ", ") - s = re.sub("([A-Z])([0-9])", "\\1 \\2", s) - return s - def obsoleted_by(self): - return self._rfc_doc_list("obsoleted_by") - def obsoletes(self): - return self._rfc_doc_list("obsoletes") - def updated_by(self): - return self._rfc_doc_list("updated_by") - def updates(self): - return self._rfc_doc_list("updates") - def also(self): - return self._rfc_doc_list("also") - def has_errata(self): - return self._rfcindex and (self._rfcindex.has_errata > 0) - def stream_name(self): - if not self._rfcindex: - return None - else: - x = self._rfcindex.stream - if x == "INDEPENDENT": - return "Independent Submission Stream" - elif x == "LEGACY": - return "Legacy Stream" - else: - return x+" Stream" - - def in_ietf_process(self): - return self.ietf_process != None - - def file_types(self): - types = self._rfcindex.file_formats - types = types.replace("ascii","txt") - return ["."+x for x in types.split(",")] - - def friendly_state(self): - if self.in_ietf_process(): - s = self.ietf_process.main_state - if not s in ["RFC Published", "AD is watching", "Dead"]: - return "RFC %d (%s)
%s (to %s)" % (self.rfc_number, self.maturity_level, self.ietf_process.state, self.ietf_process.intended_maturity_level()) - return "RFC %d (%s)" % (self.rfc_number, self.maturity_level) - - def ad_name(self): - if self.in_ietf_process(): - return self.ietf_process.ad_name() - else: - # TODO: get AD name of the draft - return None - def filename(self): - return self._rfcindex.filename - - @models.permalink - def get_absolute_url(self): - return ('ietf.doc.views_doc.document_main', ['rfc%s' % (str(self.rfc_number))]) - def displayname_with_link(self): - return 'RFC %d' % (self.get_absolute_url(), self.rfc_number) - - def to_json(self): - result = jsonify_helper(self, ['rfc_number', 'title', 'publication_date', 'maturity_level', 'obsoleted_by','obsoletes','updated_by','updates','also','has_errata','stream_name','file_types','in_ietf_process', 'friendly_state']) - if self.in_ietf_process(): - result['ietf_process'] = self.ietf_process.dict() - return json.dumps(result, indent=2) - - def underlying_document(self): - """ Expose the Document object underneath the proxy """ - # Things like RFC500 are special - there may not _be_ a docalias for them - from ietf.doc.models import Document - q = Document.objects.filter(docalias__name='rfc%04d'%self.rfc_number) - if q: - return q[0] - else: - return None - -# --------------------------------------------------------------------------- - -class IetfProcessData: - _idinternal = None - main_state = None - sub_state = None - state = None - _ballot = None - def __init__(self, idinternal): - self._idinternal = idinternal - i = self._idinternal - self.main_state = str(i.cur_state) - if i.cur_sub_state_id > 0: - self.sub_state = str(i.cur_sub_state) - self.state = self.main_state + "::" + self.sub_state - else: - self.sub_state = None - self.state = self.main_state - - def has_iesg_ballot(self): - try: - if self._idinternal.ballot.ballot_issued: - return True - except BallotInfo.DoesNotExist: - pass - return False - - def has_active_iesg_ballot(self): - if not self.has_iesg_ballot(): - return False - if not self.main_state in BALLOT_ACTIVE_STATES: - return False - if (not self._idinternal.rfc_flag) and self._idinternal.draft.status_id != 1: - # Active - return False - return True - - # don't call this unless has_[active_]iesg_ballot returns True - def iesg_ballot(self): - if not self._ballot: - self._ballot = BallotWrapper(self._idinternal) - return self._ballot - - # don't call this unless has_[active_]iesg_ballot returns True - def iesg_ballot_needed( self ): - standardsTrack = 'Standard' in self.intended_maturity_level() or \ - self.intended_maturity_level() in ("BCP", "Best Current Practice") - return self.iesg_ballot().ballot.needed( standardsTrack ) - - def ad_name(self): - return str(self._idinternal.job_owner) - - def iesg_note(self): - if self._idinternal.note: - n = self._idinternal.note - # Hide unnecessary note of form "RFC 1234" - if re.match("^RFC\s*\d+$", n): - return None - return n - else: - return None - - def state_date(self): - try: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return self._idinternal.docevent_set.filter( - Q(desc__istartswith="Draft Added by ")| - Q(desc__istartswith="Draft Added in state ")| - Q(desc__istartswith="Draft added in state ")| - Q(desc__istartswith="State changed to ")| - Q(desc__istartswith="State Changes to ")| - Q(desc__istartswith="Sub state has been changed to ")| - Q(desc__istartswith="State has been changed to ")| - Q(desc__istartswith="IESG has approved and state has been changed to")| - Q(desc__istartswith="IESG process started in state")).order_by('-time')[0].time.date() - return self._idinternal.comments().filter( - Q(comment_text__istartswith="Draft Added by ")| - Q(comment_text__istartswith="Draft Added in state ")| - Q(comment_text__istartswith="Draft added in state ")| - Q(comment_text__istartswith="State changed to ")| - Q(comment_text__istartswith="State Changes to ")| - Q(comment_text__istartswith="Sub state has been changed to ")| - Q(comment_text__istartswith="State has been changed to ")| - Q(comment_text__istartswith="IESG has approved and state has been changed to")).order_by('-id')[0].date - except IndexError: - # should never happen -- return an obviously bogus date - return date(1990,1,1) - - def dict(self): - result = {'main_state':self.main_state, - 'sub_state':self.sub_state, - 'state':self.state, - 'state_date':str(self.state_date()), - 'has_iesg_ballot':self.has_iesg_ballot(), - 'has_active_iesg_ballot':self.has_active_iesg_ballot(), - 'ad_name':self.ad_name(), - 'intended_maturity_level':self.intended_maturity_level(), - 'telechat_date':self.telechat_date()} - if result['telechat_date']: - result['telechat_date'] = str(result['telechat_date']) - result['telechat_returning_item'] = self.telechat_returning_item() - if self.iesg_note(): - result['iesg_note'] = self.iesg_note() - if self.has_iesg_ballot(): - result['iesg_ballot'] = self.iesg_ballot().dict() - return result - - def intended_maturity_level(self): - if self._idinternal.rfc_flag: - s = str(self._idinternal.document().intended_status) - # rfc_intend_status table uses different names, argh! - if s == "Proposed": - s = "Proposed Standard" - elif s == "Draft": - s = "Draft Standard" - elif s == "None": - s = None - else: - s = str(self._idinternal.draft.intended_status) - if s == "None": - s = None - elif s == "Request": - s = None - return s - - def telechat_date(self): - # return date only if it's on upcoming agenda - if self._idinternal.agenda: - return self._idinternal.telechat_date - else: - return None - - def telechat_returning_item(self): - # should be called only if telechat_date() returns non-None - return bool(self._idinternal.returning_item) - - def state_change_notice_to(self): - return self._idinternal.state_change_notice_to - - # comment_log? - -# --------------------------------------------------------------------------- - -class IdRfcWrapper: - rfc = None - id = None - iprCount = None - iprUrl = None - - def __init__(self, id, rfc): - self.id = id - self.rfc = rfc - if id: - iprs = IprDraft.objects.filter(document=self.id.tracker_id, ipr__status__in=[1,3]) - self.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(self.id.tracker_id) - elif rfc: - iprs = IprRfc.objects.filter(document=self.rfc.rfc_number, ipr__status__in=[1,3]) - self.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(self.rfc.rfc_number) - else: - raise ValueError("Construction with null id and rfc") - # iprs is a list of docs which contain IPR - self.iprCount = len(iprs) - - def title(self): - if self.rfc: - return self.rfc.title - else: - return self.id.title - - def friendly_state(self): - if self.rfc: - return self.rfc.friendly_state() - else: - return self.id.friendly_state() - - def get_absolute_url(self): - if self.rfc: - return self.rfc.get_absolute_url() - else: - return self.id.get_absolute_url() - - def comment_count(self): - if self.rfc: - return DocumentComment.objects.filter(document=self.rfc.rfc_number,rfc_flag=1).count() - else: - return DocumentComment.objects.filter(document=self.id.tracker_id).exclude(rfc_flag=1).count() - - def ad_name(self): - if self.rfc: - s = self.rfc.ad_name() - if s: - return s - if self.id: - return self.id.ad_name() - return None - - def publication_date(self): - if self.rfc: - return self.rfc.publication_date - else: - return self.id.publication_date - - def telechat_date(self): - if self.rfc and self.rfc.in_ietf_process(): - return self.rfc.ietf_process.telechat_date() - elif self.id and self.id.in_ietf_process(): - return self.id.ietf_process.telechat_date() - else: - return None - - def view_sort_group(self): - if self.rfc: - return 'RFC' - elif self.id.draft_status == "Active": - return 'Active Internet-Draft' - else: - return 'Old Internet-Draft' - - def view_sort_group_byad(self): - if self.rfc: - return 'RFC' - elif self.id.draft_status == "Active": - if self.id.in_ietf_process(): - if self.id.ietf_process._idinternal.cur_state_id == IDState.DEAD: - return 'IESG Dead Internet-Draft' - else: - return "%s Internet-Draft" % self.id.ietf_process._idinternal.cur_state - else: - return 'Active Internet-Draft' - else: - return 'Old Internet-Draft' - - def view_sort_key(self, sort_by=None): - if sort_by is None: - if self.rfc: - return "2%04d" % self.rfc.rfc_number - elif self.id.draft_status == "Active": - return "1"+self.id.draft_name - else: - return "3"+self.id.draft_name - else: - if self.rfc: - sort_key = "2" - elif self.id.draft_status == "Active": - sort_key = "1" - else: - sort_key = "3" - - # Depending on what we're sorting on, we may - # need to do some conversion. - if sort_by == "title": - sort_key += self.title() - elif sort_by == "date": - sort_key = sort_key + str(self.publication_date()) - elif sort_by == "status": - if self.rfc: - sort_key += "%04d" % self.rfc.rfc_number - else: - sort_key += self.id.draft_status - elif sort_by == "ipr": - sort_key += self.iprUrl - elif sort_by == "ad": - return self.view_sort_key_byad() - else: - # sort default or unknown sort value, revert to default - if self.rfc: - sort_key += "%04d" % self.rfc.rfc_number - else: - sort_key += self.id.draft_name - - return sort_key - - def view_sort_key_byad(self): - if self.rfc: - return "2%04d" % self.rfc.rfc_number - elif self.id.draft_status == "Active": - if self.id.in_ietf_process(): - return "11%02d" % (self.id.ietf_process._idinternal.cur_state_id) - else: - return "10" - else: - return "3" - -# --------------------------------------------------------------------------- - -class BallotWrapper: - _idinternal = None - ballot = None - ballot_active = False - _positions = None - position_values = ["Discuss", "Yes", "No Objection", "Abstain", "Recuse", "No Record"] - - def __init__(self, idinternal): - self._idinternal = idinternal - self.ballot = idinternal.ballot - if not idinternal.rfc_flag: - self.ballot_active = self.ballot.ballot_issued and (str(idinternal.cur_state) in BALLOT_ACTIVE_STATES) and str(idinternal.draft.status)=="Active"; - else: - self.ballot_active = self.ballot.ballot_issued and (str(idinternal.cur_state) in BALLOT_ACTIVE_STATES) - self._ballot_set = None - - def approval_text(self): - return self.ballot.approval_text - def ballot_writeup(self): - return self.ballot.ballot_writeup - def is_active(self): - return self.ballot_active - def ballot_id(self): - return self._idinternal.ballot_id - def was_deferred(self): - return self.ballot.defer - def deferred_by(self): - return self.ballot.defer_by - def deferred_date(self): - return self.ballot.defer_date - def is_ballot_set(self): - if not self._ballot_set: - self._ballot_set = self._idinternal.ballot_set() - return len(list(self._ballot_set)) > 1 - def ballot_set_other(self): - if not self.is_ballot_set(): - return [] - else: - return self._ballot_set.exclude(draft=self._idinternal) - - def _init(self): - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - self.old_init() - return - - from ietf.person.models import Person - from ietf.doc.models import BallotPositionDocEvent, NewRevisionDocEvent, BallotDocEvent - - active_ads = Person.objects.filter(role__name="ad", role__group__state="active").distinct() - - positions = [] - seen = {} - - new_revisions = list(NewRevisionDocEvent.objects.filter(doc=self.ballot, type="new_revision").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 - position=pos.pos.name, - is_old_ad=pos.ad not in active_ads, - old_positions=[]) - - rev = pos.doc.rev - for n in new_revisions: - if n.time <= pos.time: - rev = n.rev - break - - if pos.pos.slug == "discuss": - p["has_text"] = True - p["discuss_text"] = pos.discuss - p["discuss_date"] = pos.discuss_time.date() - p["discuss_revision"] = rev - - if pos.comment: - p["has_text"] = True - p["comment_text"] = pos.comment - p["comment_date"] = pos.comment_time.date() - p["comment_revision"] = rev - - positions.append(p) - seen[pos.ad] = p - else: - latest = seen[pos.ad] - if latest["old_positions"]: - prev = latest["old_positions"][-1] - else: - prev = latest["position"] - - if prev != pos.pos.name: - seen[pos.ad]["old_positions"].append(pos.pos.name) - - # add any missing ADs as No Record - if self.ballot_active: - for ad in active_ads: - if ad not in seen: - d = dict(ad_name=ad.plain_name(), - ad_username=ad.pk, - position="No Record", - ) - positions.append(d) - - self._positions = positions - - def old_init(self): - try: - ads = set() - except NameError: - # for Python 2.3 - from sets import Set as set - ads = set() - - positions = [] - all_comments = self.ballot.comments.all().select_related('ad') - for p in self.ballot.positions.all().select_related('ad'): - po = create_position_object(self.ballot, p, all_comments) - #if not self.ballot_active: - # if 'is_old_ad' in po: - # del po['is_old_ad'] - ads.add(str(p.ad)) - positions.append(po) - for c in all_comments: - if (str(c.ad) not in ads) and c.ad.is_current_ad(): - positions.append({'has_text':True, - 'comment_text':c.text, - 'comment_date':c.date, - 'comment_revision':str(c.revision), - 'ad_name':str(c.ad), - 'ad_username': c.ad.login_name, - 'position':'No Record', - 'is_old_ad':False}) - ads.add(str(c.ad)) - if self.ballot_active: - for ad in IESGLogin.active_iesg(): - if str(ad) not in ads: - positions.append(dict(ad_name=str(ad), - ad_username=ad.login_name, - position="No Record")) - self._positions = positions - - def position_for_ad(self, ad_name): - pl = self.position_list() - for p in pl: - if p["ad_name"] == ad_name: - return p["position"] - return None - - def position_list(self): - if not self._positions: - self._init() - return self._positions - - def get(self, v): - return [p for p in self.position_list() if p['position']==v] - - def get_discuss(self): - return self.get("Discuss") - def get_yes(self): - return self.get("Yes") - def get_no_objection(self): - return self.get("No Objection") - def get_abstain(self): - return self.get("Abstain") - def get_recuse(self): - return self.get("Recuse") - def get_no_record(self): - return self.get("No Record") - - def get_texts(self): - return [p for p in self.position_list() if ('has_text' in p) and p['has_text']] - - def dict(self): - summary = {} - for key in self.position_values: - tag = key.lower().replace(" ", "_") - summary[tag] = [ pos["ad_name"] for pos in self.get(key) ] - positions = self.position_list() - for i in range(len(positions)): - for key in ["comment_date", "discuss_date", ]: - if key in positions[i]: - positions[i][key] = positions[i][key].strftime("%Y-%m-%d") - return { - "active": self.is_active(), - "approval_text": self.approval_text(), - "ballot_writeup": self.ballot_writeup(), - "ballot_id": self.ballot_id(), - "deferred_by": unicode(self.deferred_by()), - "deferred_date": self.deferred_date() and self.deferred_date().strftime("%Y-%m-%d") , - "positions": positions, - "summary": summary, - "was_deferred": self.was_deferred(), - } - -def position_to_string(position): - positions = {"yes":"Yes", - "noobj":"No Objection", - "discuss":"Discuss", - "abstain":"Abstain", - "recuse":"Recuse"} - if not position: - return "No Record" - p = None - for k,v in positions.iteritems(): - if getattr(position, k) > 0: - p = v - if not p: - p = "No Record" - return p - -def create_position_object(ballot, position, all_comments): - positions = {"yes":"Yes", - "noobj":"No Objection", - "discuss":"Discuss", - "abstain":"Abstain", - "recuse":"Recuse"} - p = None - for k,v in positions.iteritems(): - if position.__dict__[k] > 0: - p = v - if not p: - p = "No Record" - r = dict(ad_name=str(position.ad), - ad_username=position.ad.login_name, - position=p) - if not position.ad.is_current_ad(): - r['is_old_ad'] = True - else: - r['is_old_ad'] = False - - was = [v for k,v in positions.iteritems() if position.__dict__[k] < 0] - if len(was) > 0: - r['old_positions'] = was - - comment = None - for c in all_comments: - if c.ad == position.ad: - comment = c - break - if comment and comment.text: - r['has_text'] = True - r['comment_text'] = comment.text - r['comment_date'] = comment.date - r['comment_revision'] = str(comment.revision) - - if p == "Discuss": - try: - discuss = ballot.discusses.get(ad=position.ad) - if discuss.text: - r['discuss_text'] = discuss.text - else: - r['discuss_text'] = '(empty)' - r['discuss_revision'] = str(discuss.revision) - r['discuss_date'] = discuss.date - except IESGDiscuss.DoesNotExist: - # this should never happen, but unfortunately it does - # fill in something to keep other parts of the code happy - r['discuss_text'] = "(error: discuss text not found)" - r['discuss_revision'] = "00" - r['discuss_date'] = date(2000, 1,1) - r['has_text'] = True - return r - diff --git a/ietf/idrfc/mirror_draft_versions.py b/ietf/idrfc/mirror_draft_versions.py deleted file mode 100644 index faffe5c35..000000000 --- a/ietf/idrfc/mirror_draft_versions.py +++ /dev/null @@ -1,88 +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 ietf import settings -from django.core import management -management.setup_environ(settings) -from django import db - -import urllib2 -from datetime import datetime -import socket -import sys - -URL = "http://merlot.tools.ietf.org/~pasi/draft_versions.txt" -TABLE = "draft_versions_mirror" - -log_data = "" -def log(line): - global log_data - if len(sys.argv) > 1: - print line - else: - log_data += line + "\n" - -try: - log("output from mirror_draft_versions.py:\n") - log("time: "+str(datetime.now())) - log("host: "+socket.gethostname()) - log("url: "+URL) - - log("downloading...") - socket.setdefaulttimeout(30) - response = urllib2.urlopen(URL) - #log("got \n"+str(response.info())) - log("parsing...") - data = [] - for line in response.readlines(): - rec = line[:-1].split("\t") - data.append(rec) - - log("got " + str(len(data)) + " entries") - if len(data) < 10000: - raise Exception('not enough data') - - log("connecting to database...") - cursor = db.connection.cursor() - log("removing old data...") - cursor.execute("DELETE FROM "+TABLE) - log("inserting new data...") - cursor.executemany("INSERT INTO "+TABLE+" (filename, revision, revision_date) VALUES (%s, %s, %s)", data) - cursor.close() - db.connection._commit() - db.connection.close() - - log("all done!") - log_data = "" -finally: - if len(log_data) > 0: - print log_data diff --git a/ietf/idrfc/models.py b/ietf/idrfc/models.py deleted file mode 100644 index 7687eb2b0..000000000 --- a/ietf/idrfc/models.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (C) 2009-2010 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.db import models -from ietf.idtracker.models import InternetDraft - -class RfcEditorQueue(models.Model): - STREAM_CHOICES = ( - (0, 'Unknown'), - (1, 'IETF'), - (2, 'IAB'), - (3, 'IRTF'), - (4, 'Independent') - ) - draft = models.OneToOneField(InternetDraft, db_column="id_document_tag", related_name="rfc_editor_queue_state",primary_key=True) - date_received = models.DateField() - state = models.CharField(max_length=200, blank=True, null=True) - # currently, queue2.xml does not have this information, so - # this field will be NULL (but we could get it from other sources) - state_date = models.DateField(blank=True,null=True) - stream = models.IntegerField(choices=STREAM_CHOICES) - auth48_url = models.CharField(max_length=200, blank=True, null=True) - rfc_number = models.IntegerField(null=True) - def __str__(self): - return "RfcEditorQueue"+str([self.draft, self.date_received, self.state, self.state_date, self.stream]) - class Meta: - db_table = "rfc_editor_queue_mirror" - -class RfcEditorQueueRef(models.Model): - source = models.ForeignKey(InternetDraft, db_column="source", related_name="rfc_editor_queue_refs") - destination = models.CharField(max_length=200) - in_queue = models.BooleanField() - direct = models.BooleanField() # Is this a direct (or indirect) depencency? - class Meta: - db_table = "rfc_editor_queue_mirror_refs" - -class RfcIndex(models.Model): - rfc_number = models.IntegerField(primary_key=True) - title = models.CharField(max_length=250) - authors = models.CharField(max_length=250) - rfc_published_date = models.DateField() - current_status = models.CharField(max_length=50,null=True) - updates = models.CharField(max_length=200,blank=True,null=True) - updated_by = models.CharField(max_length=200,blank=True,null=True) - obsoletes = models.CharField(max_length=200,blank=True,null=True) - obsoleted_by = models.CharField(max_length=200,blank=True,null=True) - also = models.CharField(max_length=50,blank=True,null=True) - draft = models.CharField(max_length=200,null=True) - has_errata = models.BooleanField() - stream = models.CharField(max_length=15,blank=True,null=True) - wg = models.CharField(max_length=15,blank=True,null=True) - file_formats = models.CharField(max_length=20,blank=True,null=True) - def __str__(self): - return "RfcIndex"+str(self.rfc_number) - class Meta: - db_table = "rfc_index_mirror" - -class DraftVersions(models.Model): - # Django does not support multi-column primary keys, so - # we can't use filename+revision. But the key for this table - # does not really matter, so we'll have an 'id' field - id = models.AutoField(primary_key=True) - filename = models.CharField(max_length=200, db_index=True) - revision = models.CharField(max_length=2) - revision_date = models.DateField() - def __str__(self): - return "DraftVersions"+self.filename+self.revision+str(self.revision_date) - class Meta: - db_table = "draft_versions_mirror" - - -from django.conf import settings -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - RfcIndexOld = RfcIndex - from ietf.doc.proxy import RfcIndex diff --git a/ietf/iesg/admin.py b/ietf/iesg/admin.py index 7f33b717e..8dfe86601 100644 --- a/ietf/iesg/admin.py +++ b/ietf/iesg/admin.py @@ -5,9 +5,5 @@ class TelechatAgendaItemAdmin(admin.ModelAdmin): pass admin.site.register(TelechatAgendaItem, TelechatAgendaItemAdmin) -class WGActionAdmin(admin.ModelAdmin): - pass -admin.site.register(WGAction, WGActionAdmin) - admin.site.register(TelechatDate) diff --git a/ietf/iesg/agenda.py b/ietf/iesg/agenda.py new file mode 100644 index 000000000..d9c69ac81 --- /dev/null +++ b/ietf/iesg/agenda.py @@ -0,0 +1,217 @@ +# utilities for constructing agendas for IESG telechats + +import codecs, re, os, datetime + +# FIXME: once we're on Python 2.7, replace with: from collections import OrderedDict +from django.utils.datastructures import SortedDict as OrderedDict + +from django.http import Http404 +from django.conf import settings + +from ietf.iesg.models import TelechatDate, TelechatAgendaItem +from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent +from ietf.group.models import Group, GroupMilestone + +def get_agenda_date(date=None): + if not date: + try: + return TelechatDate.objects.active().order_by('date')[0].date + except IndexError: + return datetime.date.today() + else: + try: + # FIXME: .active() + return TelechatDate.objects.all().get(date=datetime.datetime.strptime(date, "%Y-%m-%d").date()).date + except (ValueError, TelechatDate.DoesNotExist): + raise Http404 + +def get_doc_section(doc): + if doc.type_id == 'draft': + if doc.intended_std_level_id in ["bcp", "ds", "ps", "std"]: + s = "2" + else: + s = "3" + + g = doc.group_acronym() + if g and str(g) != 'none': + s += ".1" + elif s == "3" and doc.stream_id in ("ise","irtf"): + s += ".3" + else: + s += ".2" + if doc.get_state_slug() != "rfc" and doc.get_state_slug('draft-iesg') not in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"): + s += ".3" + elif doc.returning_item(): + s += ".2" + else: + s += ".1" + + elif doc.type_id == 'charter': + s = "4" + if doc.group.state_id in ('active', 'dormant'): + s += ".2" + else: + s += ".1" + if doc.get_state_slug() in ('extrev', 'iesgrev'): + s += '.2' + else: + s += '.1' + + elif doc.type_id == 'statchg': + protocol_action = False + for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')): + if relation.relationship.slug in ('tops','tois') or relation.target.document.std_level.slug in ('std','ds','ps'): + protocol_action = True + if protocol_action: + s = "2.3" + else: + s = "3.3" + if doc.get_state_slug() not in ("iesgeval", "defer", "appr-pr", "appr-pend", "appr-sent"): + s += ".3" + elif doc.returning_item(): + s += ".2" + else: + s += ".1" + + elif doc.type_id == 'conflrev': + if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'): + s = "3.4.3" + elif doc.returning_item(): + s = "3.4.2" + else: + s = "3.4.1" + + return s + +def agenda_sections(): + return OrderedDict([ + ('1', {'title':"Administrivia"}), + ('1.1', {'title':"Roll Call"}), + ('1.2', {'title':"Bash the Agenda"}), + ('1.3', {'title':"Approval of the Minutes of Past Telechats"}), + ('1.4', {'title':"List of Remaining Action Items from Last Telechat"}), + ('2', {'title':"Protocol Actions"}), + ('2.1', {'title':"WG Submissions"}), + ('2.1.1', {'title':"New Items", 'docs': []}), + ('2.1.2', {'title':"Returning Items", 'docs':[]}), + ('2.1.3', {'title':"For Action", 'docs':[]}), + ('2.2', {'title':"Individual Submissions"}), + ('2.2.1', {'title':"New Items", 'docs':[]}), + ('2.2.2', {'title':"Returning Items", 'docs':[]}), + ('2.2.3', {'title':"For Action", 'docs':[]}), + ('2.3', {'title':"Status Changes"}), + ('2.3.1', {'title':"New Items", 'docs':[]}), + ('2.3.2', {'title':"Returning Items", 'docs':[]}), + ('2.3.3', {'title':"For Action", 'docs':[]}), + ('3', {'title':"Document Actions"}), + ('3.1', {'title':"WG Submissions"}), + ('3.1.1', {'title':"New Items", 'docs':[]}), + ('3.1.2', {'title':"Returning Items", 'docs':[]}), + ('3.1.3', {'title':"For Action", 'docs':[]}), + ('3.2', {'title':"Individual Submissions Via AD"}), + ('3.2.1', {'title':"New Items", 'docs':[]}), + ('3.2.2', {'title':"Returning Items", 'docs':[]}), + ('3.2.3', {'title':"For Action", 'docs':[]}), + ('3.3', {'title':"Status Changes"}), + ('3.3.1', {'title':"New Items", 'docs':[]}), + ('3.3.2', {'title':"Returning Items", 'docs':[]}), + ('3.3.3', {'title':"For Action", 'docs':[]}), + ('3.4', {'title':"IRTF and Independent Submission Stream Documents"}), + ('3.4.1', {'title':"New Items", 'docs':[]}), + ('3.4.2', {'title':"Returning Items", 'docs':[]}), + ('3.4.3', {'title':"For Action", 'docs':[]}), + ('4', {'title':"Working Group Actions"}), + ('4.1', {'title':"WG Creation"}), + ('4.1.1', {'title':"Proposed for IETF Review", 'docs':[]}), + ('4.1.2', {'title':"Proposed for Approval", 'docs':[]}), + ('4.2', {'title':"WG Rechartering"}), + ('4.2.1', {'title':"Under Evaluation for IETF Review", 'docs':[]}), + ('4.2.2', {'title':"Proposed for Approval", 'docs':[]}), + ('5', {'title':"IAB News We Can Use"}), + ('6', {'title':"Management Issues"}), + ('7', {'title':"Working Group News"}), + ]) + +def fill_in_agenda_administrivia(date, sections): + extra_info_files = ( + ("1.1", "roll_call", settings.IESG_ROLL_CALL_FILE), + ("1.3", "minutes", settings.IESG_MINUTES_FILE), + ("1.4", "action_items", settings.IESG_TASK_FILE), + ) + + for s, key, filename in extra_info_files: + try: + with codecs.open(filename, 'r', 'utf-8', 'replace') as f: + t = f.read().strip() + except IOError: + t = u"(Error reading %s)" % filename + + sections[s]["text"] = t + +def fill_in_agenda_docs(date, sections, matches=None): + if not matches: + matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date) + matches = matches.select_related("stream", "group").distinct() + + docs = [] + for doc in matches: + if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: + continue + + e = doc.latest_event(type="started_iesg_process") + doc.balloting_started = e.time if e else datetime.datetime.min + + if doc.type_id == "draft": + s = doc.get_state("draft-iana-review") + if s: # and s.slug in ("not-ok", "changed", "need-rev"): + doc.iana_review_state = str(s) + + if doc.get_state_slug("draft-iesg") == "lc": + e = doc.latest_event(LastCallDocEvent, type="sent_last_call") + if e: + doc.lastcall_expires = e.expires + + if doc.stream_id in ("ietf", "irtf", "iab"): + doc.consensus = "Unknown" + e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") + if e: + doc.consensus = "Yes" if e.consensus else "No" + elif doc.type_id == "conflrev": + doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document + elif doc.type_id == "charter": + #if doc.group.state_id not in ("proposed", "active"): + # continue + + doc.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (doc.canonical_name(), doc.rev) + + num = get_doc_section(doc) + if num: # and num in sections + sections[num]["docs"].append(doc) + + # prune empty "For action" sections + empty_for_action = [num for num, section in sections.iteritems() + if section["title"] == "For Action" and not section["docs"]] + for num in empty_for_action: + del sections[num] + + # Be careful to keep this the same as what's used in agenda_documents + for s in sections.itervalues(): + if "docs" in s: + s["docs"].sort(key=lambda d: d.balloting_started) + +def fill_in_agenda_management_issues(date, sections): + s = "6.%s" + for i, item in enumerate(TelechatAgendaItem.objects.filter(type=3).order_by('id'), start=1): + sections[s % i] = { "title": item.title, "text": item.text } + +def agenda_data(date=None): + """Return a dict with the different IESG telechat agenda components.""" + date = get_agenda_date(date) + sections = agenda_sections() + + fill_in_agenda_administrivia(date, sections) + fill_in_agenda_docs(date, sections) + fill_in_agenda_management_issues(date, sections) + + return { 'date': date.isoformat(), 'sections': sections } + diff --git a/ietf/iesg/feeds.py b/ietf/iesg/feeds.py index 72c77ebd1..a17441f03 100644 --- a/ietf/iesg/feeds.py +++ b/ietf/iesg/feeds.py @@ -8,27 +8,31 @@ import datetime class IESGAgenda(Feed): title = "Documents on Future IESG Telechat Agendas" - link = "http://datatracker.ietf.org/iesg/agenda/" + link = settings.IDTRACKER_BASE_URL + "/iesg/agenda/" feed_type = Atom1Feed def items(self): from ietf.doc.models import TelechatDocEvent - drafts = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.min).distinct() - for d in drafts: + docs = Document.objects.filter(docevent__telechatdocevent__telechat_date__gte=datetime.date.today()).distinct() + for d in docs: d.latest_telechat_event = d.latest_event(TelechatDocEvent, type="scheduled_for_telechat") - drafts = [d for d in drafts if d.latest_telechat_event.telechat_date] - drafts.sort(key=lambda d: d.latest_telechat_event.telechat_date) - return drafts + docs = [d for d in docs if d.latest_telechat_event.telechat_date] + docs.sort(key=lambda d: d.latest_telechat_event.telechat_date, reverse=True) + return docs + def item_categories(self, doc): + return [ str(doc.telechat_date) ] - def item_categories(self, item): - return [ str(item.telechat_date) ] - - def item_pubdate(self, item): - return item.latest_telechat_event.time + def item_pubdate(self, doc): + return doc.latest_telechat_event.time - def item_author_name(self, item): - return str( item.ad ) if item.ad else "None" + def item_author_name(self, doc): + return doc.ad.plain_name() if doc.ad else "None" - def item_author_email(self, item): - return str( item.ad.role_email("ad") ) if item.ad else "" + def item_author_email(self, doc): + if not doc.ad: + return "" + e = doc.ad.role_email("ad") + if not e: + return "" + return e.address diff --git a/ietf/iesg/fixtures/sieve-charter.txt b/ietf/iesg/fixtures/sieve-charter.txt deleted file mode 100644 index 389aef8d0..000000000 --- a/ietf/iesg/fixtures/sieve-charter.txt +++ /dev/null @@ -1,143 +0,0 @@ -Sieve Mail Filtering Language (sieve) -------------------------------------- -Current Status: Active -Last updated: 2010-05-07 - -Chairs: -Cyrus Daboo -Aaron Stone - -Applications Area Directors: -Alexey Melnikov -Peter Saint-Andre - -Applications Area Advisor: -Alexey Melnikov - -Mailing Lists: -General Discussion: sieve@ietf.org -To Subscribe: sieve-request@ietf.org -Archive: -http://www.ietf.org/mail-archive/web/sieve/current/maillist.html - -Description of Working Group: - -The SIEVE email filtering language is specified in RFC 5228, together -with a number of extensions. - -The SIEVE working group is being re-chartered to: - -(1) Finish work on existing in-progress Working Group documents: -(a) External lists (draft-ietf-sieve-external-lists) -(b) Notify SIP (draft-ietf-sieve-notify-sip-message) -(c) RegEx (draft-ietf-sieve-regex) -(d) Include/multi-script (draft-ietf-sieve-include) -(e) Sieve in IMAP (draft-ietf-sieve-imap-sieve) - -(2) Finalize and publish the following SIEVE extensions as proposed -standards: -(a) General Auto-reply (draft-george-sieve-autoreply) -(b) Notify presence (draft-george-sieve-notify-presence) -(c) Vacation time (draft-george-sieve-vacation-time) -(d) Convert messages (draft-melnikov-sieve-convert) - -Additional drafts may be added to this list, but only via a charter -revision. There must also be demonstrable willingness in the SIEVE -development community to actually implement a given extension before it -can be added to this charter. - -(3) Work on a specification for iCalendar and vCard extraction, and -cooperate with the VCARDDAV WG for address book tests in Sieve. - -(4) Work on a specification to describe how EAI/IDN issues should be -handled in SIEVE. - -(5) Work on a "Benefits of SIEVE" guide for client and server vendors -that: -(a) Describes the SIEVE protocol and its suite of extensions. -(b) Explains the benefits of server-side filtering in practical terms. -(c) Shows how client-side filtering can be migrated to SIEVE. - -(6) Produce one or more informational RFCs containing a set of test -scripts and test email messages that are to be filtered by the scripts, -and the expected results of that filtering. This will serve as the basis -of a interoperability test suite to help determine the suitability of -moving the base specification and selected extensions to Draft status. - - -Goals and Milestones: -Done - Submit revised variables draft. -Done - Submit revised vacation draft. -Done - WG last call for variables draft. -Done - Initial submission of RFC 3028bis. -Done - WG last call for RFC 3028bis. -Done - Initial submission of revised relational draft. -Done - Initial submission of revised subaddress draft. -Done - Initial submission of revised spamtest/virustest draft. -Done - Submit revised editheader draft. -Done - Submit revised imapflags draft. -Done - WG last call of revised subaddress draft. -Done - Submit revised body test draft. -Done - Submit revised reject before delivery draft. -Done - WG last call for editheader draft. -Done - WG last call for body test draft. -Done - WG last call for refuse draft -Done - WG last call of revised spamtest draft -Done - Submit variables draft to IESG -Done - Submit revised loop draft -Done - Submit revised notification action draft -Done - WG last call of revised relational draft -Done - WG last call for imap-flags draft -Done - WG last call for vacation draft -Done - WG last call of revised subaddress draft -Done - Submit revised relational draft to IESG -Done - Submit vacation draft to IESG -Done - Submit revised subaddress draft to IESG -Done - Submit imapflags draft to IESG -Done - Submit revised spamtest draft to IESG -Done - Submit 3028bis to IESG -Done - Submit editheader draft to IESG -Done - Submit body test draft to IESG -Done - WG last call for notification action draft -Done - Submit notification action draft to IESG -Done - Submit refuse-reject to IESG -Done - Submit notify-mailto to IESG -Done - Submit mime-loops to IESG -Done - WGLC iHave -Done - WGLC Notary -Done - Submit iHave to IESG -Done - Submit Notary to IESG -Done - WGLC sieve-in-xml -Done - Submit sieve-in-xml to IESG -Done - WGLC ManageSIEVE -Done - Submit ManageSIEVE to IESG -Done - WGLC Notify-sip -Done - WGLC Metadata -Done - Submit Metadata to IESG -Done - Publish refuse/reject - RFC 5429 -Done - Publish notify base spec - RFC 5435 -Done - Publish notify mailto extension - RFC 5436 -Done - Publish notify xmpp extension - RFC 5437 -Done - Publish ihave - RFC 5463 -Done - Publish meta-data - RFC 5490 -Done - Publish mime loops - RFC 5703 -Done - Publish Sieve in XML - RFC 5784 -Done - Revised RegEx draft -Apr 2010 - Revised Include/multi-script draft -Apr 2010 - WGLC external-lists -May 2010 - WGLC Include/multi-script -May 2010 - Submit external-lists to IESG -Jun 2010 - Submit Include/multi-script to IESG -Jun 2010 - WGLC Notify-SIP -Jul 2010 - Initial eai-issues draft -Jul 2010 - Submit Notify-SIP to IESG -Aug 2010 - WGLC RegEx -Aug 2010 - Initial test-scripts draft -Aug 2010 - Initial benefits draft -Sep 2010 - Submit RegEx to IESG -Oct 2010 - WGLC eai-issues -Nov 2010 - Submit eai-issues to IESG -Nov 2010 - WGLC benefits -Jan 2011 - Submit benefits to IESG -Mar 2011 - WGLC test-scripts -Apr 2011 - Submit test-scripts to IESG \ No newline at end of file diff --git a/ietf/iesg/models.py b/ietf/iesg/models.py index f5b73b922..a797f9ab6 100644 --- a/ietf/iesg/models.py +++ b/ietf/iesg/models.py @@ -32,53 +32,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. -from django.db import models -from django.conf import settings -from ietf.idtracker.models import Acronym import datetime -# This table is not used by any code right now, and according to Glen, -# probably not currently (Aug 2009) maintained by the secretariat. -#class TelechatMinutes(models.Model): -# telechat_date = models.DateField(null=True, blank=True) -# telechat_minute = models.TextField(blank=True) -# exported = models.IntegerField(null=True, blank=True) -# def get_absolute_url(self): -# return "/iesg/telechat/%d/" % self.id -# def __str__(self): -# return "IESG Telechat Minutes for %s" % self.telechat_date -# class Meta: -# db_table = 'telechat_minutes' -# verbose_name = "Telechat Minute Text" -# verbose_name_plural = "Telechat Minutes" - -# this model is deprecated -class TelechatDates(models.Model): - date1 = models.DateField(primary_key=True, null=True, blank=True) - date2 = models.DateField(null=True, blank=True) - date3 = models.DateField(null=True, blank=True) - date4 = models.DateField(null=True, blank=True) - def dates(self): - l = [] - if self.date1: - l.append(self.date1) - if self.date2: - l.append(self.date2) - if self.date3: - l.append(self.date3) - if self.date4: - l.append(self.date4) - return l - - def save(self): - # date1 isn't really a primary id, so save() doesn't work - raise NotImplemented - - def __str__(self): - return " / ".join([str(d) for d in [self.date1,self.date2,self.date3,self.date4]]) - class Meta: - db_table = "telechat_dates" - verbose_name = "Next Telechat Date" +from django.db import models +from django.conf import settings class TelechatAgendaItem(models.Model): TYPE_CHOICES = ( @@ -91,41 +48,10 @@ class TelechatAgendaItem(models.Model): text = models.TextField(blank=True, db_column='template_text') type = models.IntegerField(db_column='template_type', choices=TYPE_CHOICES, default=3) title = models.CharField(max_length=255, db_column='template_title') - #The following fields are apparently not used - #note = models.TextField(null=True,blank=True) - #discussed_status_id = models.IntegerField(null=True, blank=True) - #decision = models.TextField(null=True,blank=True) + def __unicode__(self): type_name = self.TYPE_CHOICES_DICT.get(self.type, str(self.type)) return u'%s: %s' % (type_name, self.title or "") - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'templates' - -class WGAction(models.Model): - CATEGORY_CHOICES = ( - (11, "WG Creation::In Internal Review"), - (12, "WG Creation::Proposed for IETF Review"), - (13, "WG Creation::Proposed for Approval"), - (21, "WG Rechartering::In Internal Review"), - (22, "WG Rechartering::Under evaluation for IETF Review"), - (23, "WG Rechartering::Proposed for Approval") - ) - # note that with the new schema, Acronym is monkey-patched and is really Group - group_acronym = models.ForeignKey(Acronym, db_column='group_acronym_id', primary_key=True, unique=True) - note = models.TextField(blank=True,null=True) - status_date = models.DateField() - agenda = models.BooleanField("On Agenda") - token_name = models.CharField(max_length=25) - category = models.IntegerField(db_column='pwg_cat_id', choices=CATEGORY_CHOICES, default=11) - telechat_date = models.DateField() #choices = [(x.telechat_date,x.telechat_date) for x in Telechat.objects.all().order_by('-telechat_date')]) - def __str__(self): - return str(self.telechat_date)+": "+str(self.group_acronym) - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'group_internal' - ordering = ['-telechat_date'] - verbose_name = "WG Action" class Telechat(models.Model): telechat_id = models.IntegerField(primary_key=True) @@ -136,6 +62,7 @@ class Telechat(models.Model): management_issue = models.TextField(blank=True) frozen = models.IntegerField(null=True, blank=True) mi_frozen = models.IntegerField(null=True, blank=True) + class Meta: db_table = u'telechat' @@ -160,55 +87,3 @@ class TelechatDate(models.Model): class Meta: ordering = ['-date'] - -class TelechatDatesProxyDummy(object): - def all(self): - class Dummy(object): - def __getitem__(self, i): - return self - - def get_date(self, index): - if not hasattr(self, "date_cache"): - self.date_cache = TelechatDate.objects.active().order_by("date") - - if index < len(self.date_cache): - return self.date_cache[index].date - return None - - #date1 = models.DateField(primary_key=True, null=True, blank= True) - @property - def date1(self): - return self.get_date(0) - #date2 = models.DateField(null=True, blank=True) - @property - def date2(self): - return self.get_date(1) - #date3 = models.DateField(null=True, blank=True) - @property - def date3(self): - return self.get_date(2) - #date4 = models.DateField(null=True, blank=True) - @property - def date4(self): - return self.get_date(3) - - def dates(self): - l = [] - if self.date1: - l.append(self.date1) - if self.date2: - l.append(self.date2) - if self.date3: - l.append(self.date3) - if self.date4: - l.append(self.date4) - return l - - return Dummy() - -class TelechatDatesProxy(object): - objects = TelechatDatesProxyDummy() - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - TelechatDatesOld = TelechatDates - TelechatDates = TelechatDatesProxy diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 91b85f875..16c23ebc6 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -1,67 +1,367 @@ -from datetime import timedelta -import os, shutil +import os, shutil, json from django.core.urlresolvers import reverse as urlreverse from django.conf import settings from pyquery import PyQuery -from ietf.idtracker.models import * +from ietf.utils.test_data import make_test_data +from ietf.doc.models import * +from ietf.person.models import Person +from ietf.group.models import Group +from ietf.name.models import StreamName from ietf.iesg.models import * -from ietf.utils.test_utils import TestCase, SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized -from ietf.ietfworkflows.models import Stream +from ietf.utils.test_utils import TestCase, login_testing_unauthorized +from ietf.iesg.agenda import get_agenda_date, agenda_data -class RescheduleOnAgendaTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['base', 'draft'] +class ReviewDecisionsTests(TestCase): + def test_review_decisions(self): + draft = make_test_data() - def test_reschedule(self): - draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6") - draft.idinternal.telechat_date = TelechatDates.objects.all()[0].dates()[0] - draft.idinternal.agenda = True - draft.idinternal.returning_item = True - draft.idinternal.save() + e = DocEvent(type="iesg_approved") + e.doc = draft + e.by = Person.objects.get(name="Aread Irector") + e.save() - form_id = draft.idinternal.draft_id - telechat_date_before = draft.idinternal.telechat_date - - url = urlreverse('ietf.iesg.views.agenda_documents') - self.client.login(remote_user="klm") + url = urlreverse('ietf.iesg.views.review_decisions') - # normal get r = self.client.get(url) self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form select[name=%s-telechat_date]' % form_id)), 1) - self.assertEquals(len(q('form input[name=%s-clear_returning_item]' % form_id)), 1) + self.assertTrue(draft.name in r.content) - # reschedule - comments_before = draft.idinternal.comments().count() - d = TelechatDates.objects.all()[0].dates()[2] +class IESGAgendaTests(TestCase): + def setUp(self): + make_test_data() - r = self.client.post(url, { '%s-telechat_date' % form_id: d.strftime("%Y-%m-%d"), - '%s-clear_returning_item' % form_id: "1" }) + ise_draft = Document.objects.get(name="draft-imaginary-independent-submission") + ise_draft.stream = StreamName.objects.get(slug="ise") + ise_draft.save() + + self.telechat_docs = { + "ietf_draft": Document.objects.get(name="draft-ietf-mars-test"), + "ise_draft": ise_draft, + "conflrev": Document.objects.get(name="conflict-review-imaginary-irtf-submission"), + "statchg": Document.objects.get(name="status-change-imaginary-mid-review"), + "charter": Document.objects.filter(type="charter")[0], + } + + by = Person.objects.get(name="Aread Irector") + date = get_agenda_date() + + self.draft_dir = os.path.abspath("tmp-agenda-draft-dir") + os.mkdir(self.draft_dir) + settings.INTERNET_DRAFT_PATH = self.draft_dir + + for d in self.telechat_docs.values(): + TelechatDocEvent.objects.create(type="scheduled_for_telechat", + doc=d, + by=by, + telechat_date=date, + returning_item=True) + + + def tearDown(self): + shutil.rmtree(self.draft_dir) + + def test_fill_in_agenda_docs(self): + draft = self.telechat_docs["ietf_draft"] + statchg = self.telechat_docs["statchg"] + conflrev = self.telechat_docs["conflrev"] + charter = self.telechat_docs["charter"] + + # put on agenda + date = datetime.date.today() + datetime.timedelta(days=50) + TelechatDate.objects.create(date=date) + telechat_event = TelechatDocEvent.objects.create( + type="scheduled_for_telechat", + doc=draft, + by=Person.objects.get(name="Aread Irector"), + telechat_date=date, + returning_item=False) + date_str = date.isoformat() + + # 2.1 protocol WG submissions + draft.intended_std_level_id = "ps" + draft.group = Group.objects.get(acronym="mars") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.1.3"]["docs"]) + + # 2.2 protocol individual submissions + draft.group = Group.objects.get(type="individ") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["2.2.3"]["docs"]) + + # 3.1 document WG submissions + draft.intended_std_level_id = "inf" + draft.group = Group.objects.get(acronym="mars") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.1.3"]["docs"]) + + # 3.2 document individual submissions + draft.group = Group.objects.get(type="individ") + draft.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + draft.set_state(State.objects.get(type="draft-iesg", slug="pub-req")) + self.assertTrue(draft in agenda_data(date_str)["sections"]["3.2.3"]["docs"]) + + # 2.3 protocol status changes + telechat_event.doc = statchg + telechat_event.save() + + relation = RelatedDocument.objects.create( + source=statchg, + target=DocAlias.objects.filter(name__startswith='rfc', document__std_level="ps")[0], + relationship_id="tohist") + + statchg.group = Group.objects.get(acronym="mars") + statchg.save() + statchg.set_state(State.objects.get(type="statchg", slug="iesgeval")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + statchg.set_state(State.objects.get(type="statchg", slug="adrev")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.3"]["docs"]) + + # 3.3 document status changes + relation.target = DocAlias.objects.filter(name__startswith='rfc', document__std_level="inf")[0] + relation.save() + + statchg.group = Group.objects.get(acronym="mars") + statchg.save() + statchg.set_state(State.objects.get(type="statchg", slug="iesgeval")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + statchg.set_state(State.objects.get(type="statchg", slug="adrev")) + self.assertTrue(statchg in agenda_data(date_str)["sections"]["3.3.3"]["docs"]) + + # 3.4 IRTF/ISE conflict reviews + telechat_event.doc = conflrev + telechat_event.save() + + conflrev.group = Group.objects.get(acronym="mars") + conflrev.save() + conflrev.set_state(State.objects.get(type="conflrev", slug="iesgeval")) + self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.1"]["docs"]) + + telechat_event.returning_item = True + telechat_event.save() + self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.2"]["docs"]) + + telechat_event.returning_item = False + telechat_event.save() + conflrev.set_state(State.objects.get(type="conflrev", slug="needshep")) + self.assertTrue(conflrev in agenda_data(date_str)["sections"]["3.4.3"]["docs"]) + + + # 4 WGs + telechat_event.doc = charter + telechat_event.save() + + charter.group = Group.objects.get(acronym="mars") + charter.save() + + charter.group.state_id = "bof" + charter.group.save() + + charter.set_state(State.objects.get(type="charter", slug="infrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.1.1"]["docs"]) + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.1.2"]["docs"]) + + charter.group.state_id = "active" + charter.group.save() + + charter.set_state(State.objects.get(type="charter", slug="infrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.2.1"]["docs"]) + + charter.set_state(State.objects.get(type="charter", slug="iesgrev")) + self.assertTrue(charter in agenda_data(date_str)["sections"]["4.2.2"]["docs"]) + + #for n, s in agenda_data(date_str)["sections"].iteritems(): + # print n, s.get("docs") if "docs" in s else s["title"] + + def test_feed(self): + url = "/feed/iesg-agenda/" + + r = self.client.get(url) self.assertEquals(r.status_code, 200) - # check that it moved below the right header in the DOM - d_header_pos = r.content.find("IESG telechat %s" % d.strftime("%Y-%m-%d")) - draft_pos = r.content.find(draft.filename) - self.assertTrue(d_header_pos < draft_pos) + for d in self.telechat_docs.values(): + self.assertTrue(d.name in r.content) + self.assertTrue(d.title in r.content) - draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6") - self.assertEquals(draft.idinternal.telechat_date, d) - self.assertTrue(not draft.idinternal.returning_item) - self.assertEquals(draft.idinternal.comments().count(), comments_before + 1) - self.assertTrue("Telechat" in draft.idinternal.comments()[0].comment_text) + def test_agenda_json(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda_json")) + self.assertEquals(r.status_code, 200) -class RescheduleOnAgendaTestCaseREDESIGN(TestCase): - perma_fixtures = ['names'] + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + self.assertTrue(json.loads(r.content)) + + def test_agenda(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_txt(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda_txt")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_scribe_template(self): + r = self.client.get(urlreverse("ietf.iesg.views.agenda_scribe_template")) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + continue # scribe template doesn't contain chartering info + + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_moderator_package(self): + url = urlreverse("ietf.iesg.views.agenda_moderator_package") + login_testing_unauthorized(self, "secretary", url) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_package(self): + url = urlreverse("ietf.iesg.views.agenda_package") + login_testing_unauthorized(self, "secretary", url) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + if d.type_id == "charter": + self.assertTrue(d.group.name in r.content, "%s not in response" % k) + self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k) + else: + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_documents_txt(self): + url = urlreverse("ietf.iesg.views.agenda_documents_txt") + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + self.assertTrue(d.name in r.content, "%s not in response" % k) + + def test_agenda_documents(self): + url = urlreverse("ietf.iesg.views.agenda_documents") + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + for k, d in self.telechat_docs.iteritems(): + self.assertTrue(d.name in r.content, "%s not in response" % k) + self.assertTrue(d.title in r.content, "%s title not in response" % k) + + def test_agenda_telechat_docs(self): + d1 = self.telechat_docs["ietf_draft"] + d2 = self.telechat_docs["ise_draft"] + + d1_filename = "%s-%s.txt" % (d1.name, d1.rev) + d2_filename = "%s-%s.txt" % (d2.name, d2.rev) + + with open(os.path.join(self.draft_dir, d1_filename), "w") as f: + f.write("test content") + + url = urlreverse("ietf.iesg.views.telechat_docs_tarfile", kwargs=dict(date=get_agenda_date().isoformat())) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + + import tarfile, StringIO + + tar = tarfile.open(None, fileobj=StringIO.StringIO(r.content)) + names = tar.getnames() + self.assertTrue(d1_filename in names) + self.assertTrue(d2_filename not in names) + self.assertTrue("manifest.txt" in names) + + f = tar.extractfile(d1_filename) + self.assertEqual(f.read(), "test content") + + f = tar.extractfile("manifest.txt") + lines = list(f.readlines()) + self.assertTrue("Included" in [l for l in lines if d1_filename in l][0]) + self.assertTrue("Not found" in [l for l in lines if d2_filename in l][0]) + +class RescheduleOnAgendaTests(TestCase): def test_reschedule(self): - from ietf.utils.test_data import make_test_data - from ietf.person.models import Person - from ietf.doc.models import TelechatDocEvent - draft = make_test_data() # add to schedule @@ -94,10 +394,12 @@ class RescheduleOnAgendaTestCaseREDESIGN(TestCase): r = self.client.post(url, { '%s-telechat_date' % form_id: d.isoformat(), '%s-clear_returning_item' % form_id: "1" }) - self.assertEquals(r.status_code, 200) + self.assertEquals(r.status_code, 302) # check that it moved below the right header in the DOM on the # agenda docs page + r = self.client.get(url) + self.assertEquals(r.status_code, 200) d_header_pos = r.content.find("IESG telechat %s" % d.isoformat()) draft_pos = r.content.find(draft.name) self.assertTrue(d_header_pos < draft_pos) @@ -107,376 +409,7 @@ class RescheduleOnAgendaTestCaseREDESIGN(TestCase): self.assertTrue(not draft.latest_event(TelechatDocEvent, "scheduled_for_telechat").returning_item) self.assertEquals(draft.docevent_set.count(), events_before + 1) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - RescheduleOnAgendaTestCase = RescheduleOnAgendaTestCaseREDESIGN - -class ManageTelechatDatesTestCase(TestCase): - perma_fixtures = ['base', 'draft'] - - def test_set_dates(self): - dates = TelechatDates.objects.all()[0] - url = urlreverse('ietf.iesg.views.telechat_dates') - login_testing_unauthorized(self, "klm", url) - - # normal get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=date1]')), 1) - - # post - new_date = dates.date1 + timedelta(days=7) - - r = self.client.post(url, dict(date1=new_date.isoformat(), - date2=new_date.isoformat(), - date3=new_date.isoformat(), - date4=new_date.isoformat(), - )) - self.assertEquals(r.status_code, 200) - - dates = TelechatDates.objects.all()[0] - self.assertTrue(dates.date1 == new_date) - - def test_rollup_dates(self): - dates = TelechatDates.objects.all()[0] - url = urlreverse('ietf.iesg.views.telechat_dates') - login_testing_unauthorized(self, "klm", url) - - old_date2 = dates.date2 - new_date = dates.date4 + timedelta(days=14) - r = self.client.post(url, dict(rollup_dates="1")) - self.assertEquals(r.status_code, 200) - - dates = TelechatDates.objects.all()[0] - self.assertTrue(dates.date4 == new_date) - self.assertTrue(dates.date1 == old_date2) - -# class ManageTelechatDatesTestCaseREDESIGN(TestCase): -# perma_fixtures = ['names'] - -# def test_set_dates(self): -# from ietf.utils.test_data import make_test_data -# make_test_data() - -# dates = TelechatDates.objects.all()[0] -# url = urlreverse('ietf.iesg.views.telechat_dates') -# 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 input[name=date1]')), 1) - -# # post -# new_date = dates.date1 + timedelta(days=7) - -# r = self.client.post(url, dict(date1=new_date.isoformat(), -# date2=new_date.isoformat(), -# date3=new_date.isoformat(), -# date4=new_date.isoformat(), -# )) -# self.assertEquals(r.status_code, 200) - -# dates = TelechatDates.objects.all()[0] -# self.assertTrue(dates.date1 == new_date) - -# def test_rollup_dates(self): -# from ietf.utils.test_data import make_test_data -# make_test_data() - -# dates = TelechatDates.objects.all()[0] -# url = urlreverse('ietf.iesg.views.telechat_dates') -# login_testing_unauthorized(self, "secretary", url) - -# old_date2 = dates.date2 -# new_date = dates.date4 + timedelta(days=14) -# r = self.client.post(url, dict(rollup_dates="1")) -# self.assertEquals(r.status_code, 200) - -# dates = TelechatDates.objects.all()[0] -# self.assertTrue(dates.date4 == new_date) -# self.assertTrue(dates.date1 == old_date2) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - #ManageTelechatDatesTestCase = ManageTelechatDatesTestCaseREDESIGN - del ManageTelechatDatesTestCase - -class WorkingGroupActionsTestCase(TestCase): - perma_fixtures = ['base', 'wgactions'] - - def setUp(self): - super(self.__class__, self).setUp() - - curdir = os.path.dirname(os.path.abspath(__file__)) - self.evaldir = os.path.join(curdir, "testdir") - os.mkdir(self.evaldir) - - src = os.path.join(curdir, "fixtures", "sieve-charter.txt") - shutil.copy(src, self.evaldir) - - settings.IESG_WG_EVALUATION_DIR = self.evaldir - - def tearDown(self): - super(self.__class__, self).tearDown() - shutil.rmtree(self.evaldir) - - - def test_working_group_actions(self): - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "klm", url) - - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - for wga in WGAction.objects.all(): - self.assertTrue(wga.group_acronym.name in r.content) - - self.assertTrue('(sieve)' in r.content) - - def test_delete_wgaction(self): - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "klm", url) - - r = self.client.post(url, dict(delete="1")) - self.assertEquals(r.status_code, 302) - self.assertTrue(not WGAction.objects.filter(pk=wga.pk)) - - def test_edit_wgaction(self): - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "klm", 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=token_name]')), 1) - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - # change - dates = TelechatDates.objects.all()[0] - token_name = IESGLogin.active_iesg().exclude(first_name=wga.token_name)[0].first_name - old = wga.pk - r = self.client.post(url, dict(status_date=dates.date1.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates.date4.isoformat())) - self.assertEquals(r.status_code, 302) - - wga = WGAction.objects.get(pk=old) - self.assertEquals(wga.status_date, dates.date1) - self.assertEquals(wga.token_name, token_name) - self.assertEquals(wga.category, 23) - self.assertEquals(wga.note, "Testing.") - self.assertEquals(wga.telechat_date, dates.date4) - - def test_add_possible_wg(self): - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "klm", url) - - r = self.client.post(url, dict(add="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 302) - - add_url = r['Location'] - r = self.client.get(add_url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue('(sieve)' in r.content) - self.assertEquals(len(q('form select[name=token_name]')), 1) - self.assertEquals(q('form input[name=status_date]')[0].get("value"), "2010-05-07") - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - wgas_before = WGAction.objects.all().count() - dates = TelechatDates.objects.all()[0] - token_name = IESGLogin.active_iesg()[0].first_name - r = self.client.post(add_url, - dict(status_date=dates.date1.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates.date4.isoformat())) - self.assertEquals(r.status_code, 302) - self.assertEquals(wgas_before + 1, WGAction.objects.all().count()) - - def test_delete_possible_wg(self): - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "klm", url) - - r = self.client.post(url, dict(delete="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 200) - - self.assertTrue('(sieve)' not in r.content) - -class WorkingGroupActionsTestCaseREDESIGN(TestCase): - perma_fixtures = ['names'] - - def setUp(self): - super(self.__class__, self).setUp() - - curdir = os.path.dirname(os.path.abspath(__file__)) - self.evaldir = os.path.join(curdir, "tmp-testdir") - os.mkdir(self.evaldir) - - src = os.path.join(curdir, "fixtures", "sieve-charter.txt") - shutil.copy(src, self.evaldir) - - settings.IESG_WG_EVALUATION_DIR = self.evaldir - - def tearDown(self): - super(self.__class__, self).tearDown() - shutil.rmtree(self.evaldir) - - - def test_working_group_actions(self): - from ietf.utils.test_data import make_test_data - - make_test_data() - - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "secretary", url) - - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - for wga in WGAction.objects.all(): - self.assertTrue(wga.group_acronym.name in r.content) - - self.assertTrue('(sieve)' in r.content) - - def test_delete_wgaction(self): - from ietf.utils.test_data import make_test_data - - make_test_data() - - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - login_testing_unauthorized(self, "secretary", url) - - r = self.client.post(url, dict(delete="1")) - self.assertEquals(r.status_code, 302) - self.assertTrue(not WGAction.objects.filter(pk=wga.pk)) - - def test_edit_wgaction(self): - from ietf.utils.test_data import make_test_data - from ietf.person.models import Person - - make_test_data() - - wga = WGAction.objects.all()[0] - url = urlreverse('iesg_edit_working_group_action', kwargs=dict(wga_id=wga.pk)) - 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=token_name]')), 1) - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - # change - dates = TelechatDate.objects.active() - token_name = Person.objects.get(name="Ad No1").plain_name() - old = wga.pk - r = self.client.post(url, dict(status_date=dates[0].date.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates[3].date.isoformat())) - self.assertEquals(r.status_code, 302) - - wga = WGAction.objects.get(pk=old) - self.assertEquals(wga.status_date, dates[0].date) - self.assertEquals(wga.token_name, token_name) - self.assertEquals(wga.category, 23) - self.assertEquals(wga.note, "Testing.") - self.assertEquals(wga.telechat_date, dates[3].date) - - def test_add_possible_wg(self): - from ietf.utils.test_data import make_test_data - from ietf.person.models import Person - from ietf.group.models import Group - - make_test_data() - - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "secretary", url) - - r = self.client.post(url, dict(add="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 302) - - # now we got back a URL we can use for adding, but first make - # sure we got a proposed group with the acronym - group = Group.objects.create( - name="Sieve test test", - acronym="sieve", - state_id="proposed", - type_id="wg", - parent=None - ) - - add_url = r['Location'] - r = self.client.get(add_url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue('(sieve)' in r.content) - self.assertEquals(len(q('form select[name=token_name]')), 1) - self.assertEquals(q('form input[name=status_date]')[0].get("value"), "2010-05-07") - self.assertEquals(len(q('form select[name=telechat_date]')), 1) - - wgas_before = WGAction.objects.all().count() - dates = TelechatDate.objects.active() - token_name = Person.objects.get(name="Ad No1").plain_name() - r = self.client.post(add_url, - dict(status_date=dates[0].date.isoformat(), - token_name=token_name, - category="23", - note="Testing.", - telechat_date=dates[3].date.isoformat())) - self.assertEquals(r.status_code, 302) - self.assertEquals(wgas_before + 1, WGAction.objects.all().count()) - - def test_delete_possible_wg(self): - from ietf.utils.test_data import make_test_data - - make_test_data() - - url = urlreverse('iesg_working_group_actions') - login_testing_unauthorized(self, "secretary", url) - - r = self.client.post(url, dict(delete="1", - filename='sieve-charter.txt')) - self.assertEquals(r.status_code, 200) - - self.assertTrue('(sieve)' not in r.content) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - WorkingGroupActionsTestCase = WorkingGroupActionsTestCaseREDESIGN - - -class IesgUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) - def doCanonicalize(self, url, content): - if url.startswith("/feed/"): - return canonicalize_feed(content) - else: - return content - -#Tests added since database redesign that speak the new clases - -from ietf.doc.models import Document,TelechatDocEvent,State -from ietf.group.models import Person class DeferUndeferTestCase(TestCase): - - perma_fixtures = ['names'] - def helper_test_defer(self,name): doc = Document.objects.get(name=name) @@ -571,5 +504,24 @@ class DeferUndeferTestCase(TestCase): # when charters support being deferred, be sure to test them here def setUp(self): - from ietf.utils.test_data import make_test_data make_test_data() + +class IESGDiscussesTests(TestCase): + def test_feed(self): + draft = make_test_data() + draft.set_state(State.objects.get(type="draft-iesg", slug="iesg-eva")) + + pos = BallotPositionDocEvent() + pos.ballot = draft.latest_event(BallotDocEvent, type="created_ballot") + pos.pos_id = "discuss" + pos.type = "changed_ballot_position" + pos.doc = draft + pos.ad = pos.by = Person.objects.get(user__username="ad") + pos.save() + + r = self.client.get(urlreverse("ietf.iesg.views.discusses")) + self.assertEquals(r.status_code, 200) + + self.assertTrue(draft.name in r.content) + self.assertTrue(pos.ad.plain_name() in r.content) + diff --git a/ietf/iesg/testurl.list b/ietf/iesg/testurl.list deleted file mode 100644 index 1f7734032..000000000 --- a/ietf/iesg/testurl.list +++ /dev/null @@ -1,24 +0,0 @@ - -301 /iesg/telechat/ -301 /iesg/telechat/y/2007/ -301 /iesg/telechat/y/2007/apr/ -301 /iesg/telechat/354/ - -200 /iesg/agenda/ -200 /iesg/agenda/agenda.txt -200 /iesg/agenda/scribe_template.html -200 /iesg/agenda/documents.txt -200 /iesg/agenda/documents/ -200 /iesg/discusses/ - -302 /iesg/agenda/moderator_package.html -302 /iesg/agenda/agenda_package.txt -200 /iesg/_test/moderator_package.html -200 /iesg/_test/agenda_package.txt - -200 /iesg/ann/ind/ -200 /iesg/ann/new/ -# This takes ~ 300s: -#200 /iesg/ann/prev/ - -200 /feed/iesg-agenda/ diff --git a/ietf/iesg/urls.py b/ietf/iesg/urls.py index aa6532e13..9f0fc6c86 100644 --- a/ietf/iesg/urls.py +++ b/ietf/iesg/urls.py @@ -35,41 +35,23 @@ from django.conf.urls.defaults import patterns, url from django.conf import settings from ietf.iesg import views -from ietf.idtracker.models import BallotInfo - -queryset_ann = BallotInfo.objects.all() urlpatterns = patterns('', - (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }) -) + (r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }), + (r'^ann/(?:ind|new|prev)/$', 'django.views.generic.simple.redirect_to', { 'url': "/iesg/decisions/", 'permanent': True }), + (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), -urlpatterns += patterns('django.views.generic.list_detail', - (r'^ann/(?P\d+)/$', 'object_detail', { 'queryset': queryset_ann, 'template_name':"iesg/ballotinfo_detail.html" }), + (r'^decisions/(?:(?P[0-9]{4})/)?$', views.review_decisions), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?$', views.agenda), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda.txt$', views.agenda_txt), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda.json$', views.agenda_json), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?scribe_template.html$', views.agenda_scribe_template), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?moderator_package.html$', views.agenda_moderator_package), + (r'^agenda/(?:(?P\d{4}-\d{2}-\d{2})/)?agenda_package.txt$', views.agenda_package), + + (r'^agenda/documents.txt$', views.agenda_documents_txt), + (r'^agenda/documents/$', views.agenda_documents), + (r'^agenda/telechat-(?:(?P\d{4}-\d{2}-\d{2})-)?docs.tgz', views.telechat_docs_tarfile), + (r'^discusses/$', views.discusses), + (r'^milestones/$', views.milestones_needing_review), ) - -urlpatterns += patterns('', - (r'^ann/ind/$',views.inddocs), - (r'^ann/(?P[^/]+)/$',views.wgdocs), - (r'^agenda/$', views.agenda), - (r'^agenda/agenda.txt$', views.agenda_txt), - (r'^agenda/agenda.json$', views.agenda_json), - (r'^agenda/scribe_template.html$', views.agenda_scribe_template), - (r'^agenda/moderator_package.html$', views.agenda_moderator_package), - (r'^agenda/agenda_package.txt$', views.agenda_package), - (r'^agenda/documents.txt$', views.agenda_documents_txt), - (r'^agenda/documents/$', views.agenda_documents), - (r'^agenda/telechat-(?P\d+)-(?P\d+)-(?P\d+)-docs.tgz', views.telechat_docs_tarfile), - (r'^discusses/$', views.discusses), - (r'^milestones', views.milestones_needing_review), - (r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }), - url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"), - url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"), - url(r'^wgactions/(?P\d+)/$', views.edit_working_group_action, name="iesg_edit_working_group_action"), -) - -if settings.SERVER_MODE != 'production': - urlpatterns += patterns('', - (r'^agenda/(?P\d{4}-\d\d-\d\d)/$', views.agenda), - (r'^_test/moderator_package.html$', views.agenda_moderator_package_test), - (r'^_test/agenda_package.txt', views.agenda_package_test), - ) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index f534312d5..85796ab40 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -32,466 +32,265 @@ # (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 codecs, re, os, glob -import datetime -import tarfile +import codecs, re, os, glob, datetime +import tarfile, StringIO, time +import itertools -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 -from django.views.decorators.vary import vary_on_cookie from django.core.urlresolvers import reverse as urlreverse from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseRedirect from django.template import RequestContext, Context, loader -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.conf import settings from django.utils import simplejson as json +from django.db import models from django import forms -from ietf.iesg.models import TelechatDates, TelechatAgendaItem, WGAction -from ietf.idrfc.idrfc_wrapper import IdWrapper, RfcWrapper -from ietf.doc.utils import update_telechat -from ietf.ietfauth.decorators import group_required, role_required -from ietf.ietfauth.utils import has_role + +from ietf.iesg.models import TelechatDate, TelechatAgendaItem from ietf.ipr.models import IprDocAlias -from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent +from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent, IESG_BALLOT_ACTIVE_STATES from ietf.group.models import Group, GroupMilestone +from ietf.person.models import Person -def date_threshold(): - """Return the first day of the month that is 185 days ago.""" - ret = datetime.date.today() - datetime.timedelta(days=185) - ret = ret - datetime.timedelta(days=ret.day - 1) - return ret +from ietf.doc.utils import update_telechat, augment_events_with_revision +from ietf.ietfauth.utils import has_role, role_required, user_is_person +from ietf.iesg.agenda import * -def inddocs(request): - queryset_list_ind = [d for d in InternetDraft.objects.filter(stream__in=("IRTF","ISE"), docevent__type="iesg_approved").distinct() if d.latest_event(type__in=("iesg_disapproved", "iesg_approved")).type == "iesg_approved"] - queryset_list_ind.sort(key=lambda d: d.b_approve_date, reverse=True) +def review_decisions(request, year=None): + events = DocEvent.objects.filter(type__in=("iesg_disapproved", "iesg_approved")) - queryset_list_ind_dnp = [d for d in IDInternal.objects.filter(stream__in=("IRTF","ISE"), docevent__type="iesg_disapproved").distinct() if d.latest_event(type__in=("iesg_disapproved", "iesg_approved")).type == "iesg_disapproved"] - queryset_list_ind_dnp.sort(key=lambda d: d.dnp_date, reverse=True) + years = sorted((d.year for d in events.dates('time', 'year')), reverse=True) - return render_to_response('iesg/independent_doc.html', - dict(object_list=queryset_list_ind, - object_list_dnp=queryset_list_ind_dnp), - context_instance=RequestContext(request)) - - -def wgdocs(request,cat): - pass - -def wgdocsREDESIGN(request,cat): - is_recent = 0 - proto_actions = [] - doc_actions = [] - threshold = date_threshold() - - proto_levels = ["bcp", "ds", "ps", "std"] - doc_levels = ["exp", "inf"] - - if cat == 'new': - is_recent = 1 - - drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__gte=threshold, intended_std_level__in=proto_levels + doc_levels).exclude(stream__in=("ISE","IRTF")).distinct() - for d in drafts: - if d.b_approve_date and d.b_approve_date >= threshold: - if d.intended_std_level_id in proto_levels: - proto_actions.append(d) - elif d.intended_std_level_id in doc_levels: - doc_actions.append(d) - - elif cat == 'prev': - # proto - start_date = datetime.date(1997, 12, 1) - - drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__lt=threshold, docevent__time__gte=start_date, intended_std_level__in=proto_levels).exclude(stream__in=("ISE","IRTF")).distinct() - - for d in drafts: - if d.b_approve_date and start_date <= d.b_approve_date < threshold: - proto_actions.append(d) - - # doc - start_date = datetime.date(1998, 10, 15) - - drafts = InternetDraft.objects.filter(docevent__type="iesg_approved", docevent__time__lt=threshold, docevent__time__gte=start_date, intended_std_level__in=doc_levels).exclude(stream__in=("ISE","IRTF")).distinct() - - for d in drafts: - if d.b_approve_date and start_date <= d.b_approve_date < threshold: - doc_actions.append(d) + if year: + year = int(year) + events = events.filter(time__year=year) else: - raise Http404 + d = datetime.date.today() - datetime.timedelta(days=185) + d = datetime.date(d.year, d.month, 1) + events = events.filter(time__gte=d) - proto_actions.sort(key=lambda d: d.b_approve_date, reverse=True) - doc_actions.sort(key=lambda d: d.b_approve_date, reverse=True) - - return render_to_response('iesg/ietf_doc.html', - dict(object_list=proto_actions, - object_list_doc=doc_actions, - is_recent=is_recent, - title_prefix="Recent" if is_recent else "Previous"), + events = events.select_related("doc", "doc__intended_std_level").order_by("-time", "-id") + + #proto_levels = ["bcp", "ds", "ps", "std"] + #doc_levels = ["exp", "inf"] + + timeframe = u"%s" % year if year else u"the past 6 months" + + return render_to_response('iesg/review_decisions.html', + dict(events=events, + years=years, + year=year, + timeframe=timeframe), context_instance=RequestContext(request)) -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - wgdocs = wgdocsREDESIGN - +def agenda_json(request, date=None): + data = agenda_data(date) -def get_doc_section(id): - pass + res = { + "telechat-date": str(data["date"]), + "as-of": str(datetime.datetime.utcnow()), + "sections": {}, + } -def get_doc_sectionREDESIGN(doc): - if doc.type_id == 'draft': - if doc.intended_std_level_id in ["bcp", "ds", "ps", "std"]: - s = "2" - else: - s = "3" + for num, section in data["sections"].iteritems(): + s = res["sections"][num] = { + "title": section["title"], + } - g = doc.group_acronym() - if g and str(g) != 'none': - s = s + "1" - elif (s == "3") and doc.stream_id in ("ise","irtf"): - s = s + "3" - else: - s = s + "2" - if not doc.get_state_slug=="rfc" and doc.get_state_slug('draft-iesg') not in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"): - s = s + "3" - elif doc.returning_item(): - s = s + "2" - else: - s = s + "1" - elif doc.type_id == 'charter': - s = get_wg_section(doc.group) - elif doc.type_id == 'statchg': - protocol_action = False - for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')): - if relation.relationship.slug in ('tops','tois') or relation.target.document.std_level.slug in ('std','ds','ps'): - protocol_action = True - if protocol_action: - s="23" - else: - s="33" - if doc.get_state_slug() not in ("iesgeval", "defer", "appr-pr", "appr-pend", "appr-sent"): - s = s + "3" - elif doc.returning_item(): - s = s + "2" - else: - s = s + "1" - elif doc.type_id == 'conflrev': - if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'): - s = "343" - elif doc.returning_item(): - s = "342" - else: - s = "341" - - 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 in ['active','dormant']: - if charter_slug in ['extrev','iesgrev']: - s = '422' - else: - s = '421' - else: - if charter_slug in ['extrev','iesgrev']: - s = '412' - else: - s = '411' - return s - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - get_doc_section = get_doc_sectionREDESIGN - -def agenda_docs(date, next_agenda): - matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).select_related("stream").distinct() - - docmatches = [] - - for doc in matches: - if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: + if "docs" not in section: continue - e = doc.latest_event(type="started_iesg_process") - doc.balloting_started = e.time if e else datetime.datetime.min + docs = section["docs"] - if doc.type_id == "draft": - s = doc.get_state("draft-iana-review") - if s: # and s.slug in ("not-ok", "changed", "need-rev"): - doc.iana_review_state = str(s) + if "4" <= num < "5": + # charters + s["wgs"] = [] - if doc.get_state_slug("draft-iesg") == "lc": - e = doc.latest_event(LastCallDocEvent, type="sent_last_call") - if e: - doc.lastcall_expires = e.expires + for doc in docs: + wginfo = { + 'docname': doc.canonical_name(), + 'rev': doc.rev, + 'wgname': doc.group.name, + 'acronym': doc.group.acronym, + 'ad': doc.group.ad.name if doc.group.ad else None, + } - if doc.stream_id in ("ietf", "irtf", "iab"): - doc.consensus = "Unknown" - e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") - if e: - doc.consensus = "Yes" if e.consensus else "No" - elif doc.type_id=='conflrev': - doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document + # consider moving the charters to "docs" like the other documents + s['wgs'].append(wginfo) + else: + # other documents + s["docs"] = [] - docmatches.append(doc) + for doc in docs: + docinfo = { + 'docname':doc.canonical_name(), + 'title':doc.title, + 'ad':doc.ad.name if doc.ad else None, + } - # Be careful to keep this the same as what's used in agenda_documents - docmatches.sort(key=lambda d: d.balloting_started) - - 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)) - for k in range(1,4): - res['s34%d'%k]=[] - for id in docmatches: - section_key = "s"+get_doc_section(id) - if section_key not in res: - res[section_key] = [] - res[section_key].append({'obj':id}) - return res + if doc.note: + docinfo['note'] = doc.note + defer = doc.active_defer_event() + if defer: + docinfo['defer-by'] = defer.by.name + docinfo['defer-at'] = str(defer.time) + if doc.type_id == "draft": + docinfo['rev'] = doc.rev + docinfo['intended-std-level'] = str(doc.intended_std_level) + if doc.rfc_number(): + docinfo['rfc-number'] = doc.rfc_number() -def agenda_wg_actions(date): - 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).select_related("group").distinct() - charters = charters.filter(group__state__slug__in=["proposed","active"]) - for c in charters: - if c.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date: - continue + iana_state = doc.get_state("draft-iana-review") + if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"): + docinfo['iana-review-state'] = str(iana_state) - c.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (c.canonical_name(), c.rev) + if doc.get_state_slug("draft-iesg") == "lc": + e = doc.latest_event(LastCallDocEvent, type="sent_last_call") + if e: + docinfo['lastcall-expires'] = e.expires.strftime("%Y-%m-%d") - section_key = "s" + get_wg_section(c.group) - if section_key not in res: - res[section_key] = [] - # Cleanup - Older view code wants obj, newer wants doc. Older code should be moved forward - res[section_key].append({'obj': c.group, 'doc': c}) - return res + docinfo['consensus'] = None + e = doc.latest_event(ConsensusDocEvent, type="changed_consensus") + if e: + docinfo['consensus'] = e.consensus + elif doc.type_id == 'conflrev': + docinfo['rev'] = doc.rev + td = doc.relateddocument_set.get(relationship__slug='conflrev').target.document + docinfo['target-docname'] = td.canonical_name() + docinfo['target-title'] = td.title + docinfo['target-rev'] = td.rev + docinfo['intended-std-level'] = str(td.intended_std_level) + docinfo['stream'] = str(td.stream) + else: + # XXX check this -- is there nothing to set for + # all other documents here? + pass -def agenda_management_issues(date): - return TelechatAgendaItem.objects.filter(type=3).order_by('id') + s["docs"].append(docinfo) -def _agenda_json(request, date=None): - if not date: - date = TelechatDates.objects.all()[0].date1 - next_agenda = True - else: - y,m,d = date.split("-") - date = datetime.date(int(y), int(m), int(d)) - next_agenda = None + return HttpResponse(json.dumps(res, indent=2), mimetype='text/plain') - data = {'telechat-date':str(date), - 'as-of':str(datetime.datetime.utcnow()), - 'sections':{}} - data['sections']['1'] = {'title':"Administrivia"} - data['sections']['1.1'] = {'title':"Roll Call"} - data['sections']['1.2'] = {'title':"Bash the Agenda"} - data['sections']['1.3'] = {'title':"Approval of the Minutes of Past Telechats"} - data['sections']['1.4'] = {'title':"List of Remaining Action Items from Last Telechat"} - data['sections']['2'] = {'title':"Protocol Actions"} - data['sections']['2.1'] = {'title':"WG Submissions"} - data['sections']['2.1.1'] = {'title':"New Items", 'docs':[]} - data['sections']['2.1.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['2.2'] = {'title':"Individual Submissions"} - data['sections']['2.2.1'] = {'title':"New Items", 'docs':[]} - data['sections']['2.2.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['2.3'] = {'title':"Individual Submissions"} - data['sections']['2.3.1'] = {'title':"New Items", 'docs':[]} - data['sections']['2.3.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3'] = {'title':"Document Actions"} - data['sections']['3.1'] = {'title':"WG Submissions"} - data['sections']['3.1.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.1.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3.2'] = {'title':"Individual Submissions Via AD"} - data['sections']['3.2.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.2.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3.3'] = {'title':"Status Changes"} - data['sections']['3.3.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.3.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['3.4'] = {'title':"IRTF and Independent Submission Stream Documents"} - data['sections']['3.4.1'] = {'title':"New Items", 'docs':[]} - data['sections']['3.4.2'] = {'title':"Returning Items", 'docs':[]} - data['sections']['4'] = {'title':"Working Group Actions"} - data['sections']['4.1'] = {'title':"WG Creation"} - data['sections']['4.1.1'] = {'title':"Proposed for IETF Review", 'wgs':[]} - data['sections']['4.1.2'] = {'title':"Proposed for Approval", 'wgs':[]} - data['sections']['4.2'] = {'title':"WG Rechartering"} - data['sections']['4.2.1'] = {'title':"Under Evaluation for IETF Review", 'wgs':[]} - data['sections']['4.2.2'] = {'title':"Proposed for Approval", 'wgs':[]} - data['sections']['5'] = {'title':"IAB News We Can Use"} - data['sections']['6'] = {'title':"Management Issues"} - data['sections']['7'] = {'title':"Working Group News"} - - docs = agenda_docs(date, next_agenda) - for section in docs.keys(): - # in case the document is in a state that does not have an agenda section - if section != 's': - s = str(".".join(list(section)[1:])) - if s[0:1] == '4': - # ignore these; not sure why they are included by agenda_docs - pass - else: - if len(docs[section]) != 0: - # If needed, add a "For Action" section to agenda - if s[4:5] == '3': - data['sections'][s] = {'title':"For Action", 'docs':[]} - - for obj in docs[section]: - d = obj['obj'] - docinfo = {'docname':d.canonical_name(), - 'title':d.title, - 'ad':d.ad.name} - if d.note: - docinfo['note'] = d.note - defer = d.active_defer_event() - if defer: - docinfo['defer-by'] = defer.by.name - docinfo['defer-at'] = str(defer.time) - if d.type_id == "draft": - docinfo['rev'] = d.rev - docinfo['intended-std-level'] = str(d.intended_std_level) - if d.rfc_number(): - docinfo['rfc-number'] = d.rfc_number() - - iana_state = d.get_state("draft-iana-review") - if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"): - docinfo['iana-review-state'] = str(iana_state) - - if d.get_state_slug("draft-iesg") == "lc": - e = d.latest_event(LastCallDocEvent, type="sent_last_call") - if e: - docinfo['lastcall-expires'] = e.expires.strftime("%Y-%m-%d") - - docinfo['consensus'] = None - e = d.latest_event(ConsensusDocEvent, type="changed_consensus") - if e: - docinfo['consensus'] = e.consensus - elif d.type_id == 'conflrev': - docinfo['rev'] = d.rev - td = d.relateddocument_set.get(relationship__slug='conflrev').target.document - docinfo['target-docname'] = td.canonical_name() - docinfo['target-title'] = td.title - docinfo['target-rev'] = td.rev - docinfo['intended-std-level'] = str(td.intended_std_level) - docinfo['stream'] = str(td.stream) - else: - # XXX check this -- is there nothing to set for - # all other documents here? - pass - data['sections'][s]['docs'] += [docinfo, ] - - wgs = agenda_wg_actions(date) - for section in wgs.keys(): - # in case the charter is in a state that does not have an agenda section - if section != 's': - s = str(".".join(list(section)[1:])) - if s[0:1] != '4': - # ignore these; not sure why they are included by agenda_wg_actions - pass - else: - if len(wgs[section]) != 0: - for obj in wgs[section]: - wg = obj['obj'] - doc = obj['doc'] - wginfo = {'docname': doc.canonical_name(), - 'rev': doc.rev, - 'wgname': doc.group.name, - 'acronym': doc.group.acronym, - 'ad': doc.group.ad.name} - data['sections'][s]['wgs'] += [wginfo, ] - - mgmt = agenda_management_issues(date) - num = 0 - for m in mgmt: - num += 1 - data['sections']["6.%d" % num] = {'title':m.title} - - return data - -def _agenda_data(request, date=None): - if not date: - date = TelechatDates.objects.all()[0].date1 - next_agenda = True - else: - y,m,d = date.split("-") - date = datetime.date(int(y), int(m), int(d)) - next_agenda = None - #date = "2006-03-16" - docs = agenda_docs(date, next_agenda) - mgmt = agenda_management_issues(date) - wgs = agenda_wg_actions(date) - data = {'date':str(date), 'docs':docs,'mgmt':mgmt,'wgs':wgs} - for key, filename in {'action_items':settings.IESG_TASK_FILE, - 'roll_call':settings.IESG_ROLL_CALL_FILE, - 'minutes':settings.IESG_MINUTES_FILE}.items(): - try: - f = codecs.open(filename, 'r', 'utf-8', 'replace') - text = f.read().strip() - f.close() - data[key] = text - except IOError: - data[key] = "(Error reading "+key+")" - return data - -@vary_on_cookie def agenda(request, date=None): - data = _agenda_data(request, date) - data['private'] = 'private' in request.REQUEST - data['settings'] = settings - return render_to_response("iesg/agenda.html", data, context_instance=RequestContext(request)) + data = agenda_data(date) -def agenda_txt(request): - data = _agenda_data(request) - return render_to_response("iesg/agenda.txt", data, context_instance=RequestContext(request), mimetype="text/plain") + if has_role(request.user, ["Area Director", "IAB Chair", "Secretariat"]): + data["sections"]["1.1"]["title"] = data["sections"]["1.1"]["title"].replace("Roll Call", 'Roll Call') + data["sections"]["1.3"]["title"] = data["sections"]["1.3"]["title"].replace("Minutes", 'Minutes') -def agenda_json(request): - response = HttpResponse(mimetype='text/plain') - response.write(json.dumps(_agenda_json(request), indent=2)) - return response + return render_to_response("iesg/agenda.html", { + "date": data["date"], + "sections": sorted(data["sections"].iteritems()), + "settings": settings, + }, context_instance=RequestContext(request)) -def agenda_scribe_template(request): - date = TelechatDates.objects.all()[0].date1 - docs = agenda_docs(date, True) - return render_to_response('iesg/scribe_template.html', {'date':str(date), 'docs':docs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}, context_instance=RequestContext(request) ) +def agenda_txt(request, date=None): + data = agenda_data(date) + return render_to_response("iesg/agenda.txt", { + "date": data["date"], + "sections": sorted(data["sections"].iteritems()), + }, context_instance=RequestContext(request), mimetype="text/plain") -def _agenda_moderator_package(request): - data = _agenda_data(request) - data['ad_names'] = [str(x) for x in IESGLogin.active_iesg()] - data['ad_names'].sort(key=lambda x: x.split(' ')[-1]) - return render_to_response("iesg/moderator_package.html", data, context_instance=RequestContext(request)) +def agenda_scribe_template(request, date=None): + data = agenda_data(date) + sections = sorted((num, section) for num, section in data["sections"].iteritems() if "2" <= num < "4") + appendix_docs = [] + for num, section in sections: + if "docs" in section: + # why are we here including documents that have no discuss/comment? + appendix_docs.extend(section["docs"]) + return render_to_response("iesg/scribe_template.html", { + "date": data["date"], + "sections": sections, + "appendix_docs": appendix_docs, + }, context_instance=RequestContext(request) ) -@group_required('Area_Director','Secretariat') -def agenda_moderator_package(request): - return _agenda_moderator_package(request) +@role_required('Area Director', 'Secretariat') +def agenda_moderator_package(request, date=None): + """Output telechat agenda with one page per section, with each + document in its own section.""" + data = agenda_data(date) -def agenda_moderator_package_test(request): - if request.META['REMOTE_ADDR'] == "127.0.0.1": - return _agenda_moderator_package(request) - else: - return HttpResponseForbidden() + def leaf_section(num, section): + return not (num == "1" + or "2" <= num < "5" and "docs" not in section + or (num == "6" and "6.1" not in data["sections"])) -def _agenda_package(request): - data = _agenda_data(request) - return render_to_response("iesg/agenda_package.txt", data, context_instance=RequestContext(request), mimetype='text/plain') + # sort and prune non-leaf headlines + sections = sorted((num, section) for num, section in data["sections"].iteritems() + if leaf_section(num, section)) -@group_required('Area_Director','Secretariat') -def agenda_package(request): - return _agenda_package(request) + # add parents field to each section + for num, s in sections: + s["parents"] = [] + split = num.split(".") + + for i in xrange(num.count(".")): + parent_num = ".".join(split[:i + 1]) + parent = data["sections"].get(parent_num) + if parent: + s["parents"].append((parent_num, parent)) + + + # put each document in its own section + flattened_sections = [] + for num, s in sections: + if "2" <= num < "5" and "docs" in s and s["docs"]: + for i, d in enumerate(s["docs"], start=1): + flattened_sections.append((num, { + "title": s["title"] + " (%s of %s)" % (i, len(s["docs"])), + "doc": d, + "parents": s["parents"], + })) + else: + flattened_sections.append((num, s)) + + # add ads + data["sections"]["7"]["ads"] = sorted(Person.objects.filter(role__name="ad", role__group__state="active"), + key=lambda p: p.name_parts()[3]) + + return render_to_response("iesg/moderator_package.html", { + "date": data["date"], + "sections": flattened_sections, + }, context_instance=RequestContext(request)) + +@role_required('Area Director', 'Secretariat') +def agenda_package(request, date=None): + data = agenda_data(date) + return render_to_response("iesg/agenda_package.txt", { + "date": data["date"], + "sections": sorted(data["sections"].iteritems()), + "roll_call": data["sections"]["1.1"]["text"], + "minutes": data["sections"]["1.3"]["text"], + "management_items": [(num, section) for num, section in data["sections"].iteritems() if "6" < num < "7"], + }, context_instance=RequestContext(request), mimetype='text/plain') -def agenda_package_test(request): - if request.META['REMOTE_ADDR'] == "127.0.0.1": - return _agenda_package(request) - else: - return HttpResponseForbidden() def agenda_documents_txt(request): - dates = TelechatDates.objects.all()[0].dates() + dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4]) + docs = [] - for date in dates: - from ietf.doc.models import TelechatDocEvent - for d in Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct(): - if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date: - docs.append(d) - t = loader.get_template('iesg/agenda_documents.txt') - c = Context({'docs':docs,'special_stream_list':['ise','irtf']}) - return HttpResponse(t.render(c), mimetype='text/plain') + for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).distinct(): + date = d.telechat_date() + if date in dates: + d.computed_telechat_date = date + docs.append(d) + docs.sort(key=lambda d: d.computed_telechat_date) + + # output table + rows = [] + rows.append("# Fields: telechat date, filename (draft-foo-bar or rfc1234), intended status, rfc editor submission flag (0=no, 1=yes), area acronym, AD name, version") + for d in docs: + row = ( + d.computed_telechat_date.isoformat(), + d.name, + unicode(d.intended_std_level), + "1" if d.stream_id in ("ise", "irtf") else "0", + unicode(d.area_acronym()).lower(), + d.ad.plain_name() if d.ad else "None Assigned", + d.rev, + ) + rows.append("\t".join(row)) + return HttpResponse(u"\n".join(rows), mimetype='text/plain') class RescheduleForm(forms.Form): telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False) @@ -513,7 +312,7 @@ class RescheduleForm(forms.Form): self.fields['telechat_date'].choices = choices -def handle_reschedule_form(request, doc, dates): +def handle_reschedule_form(request, doc, dates, status): initial = dict(telechat_date=doc.telechat_date()) formargs = dict(telechat_dates=dates, @@ -522,12 +321,13 @@ def handle_reschedule_form(request, doc, dates): if request.method == 'POST': form = RescheduleForm(request.POST, **formargs) if form.is_valid(): - login = request.user.get_profile() - update_telechat(request, doc, login, + update_telechat(request, doc, request.user.get_profile(), form.cleaned_data['telechat_date'], False if form.cleaned_data['clear_returning_item'] else None) doc.time = datetime.datetime.now() doc.save() + + status["changed"] = True else: form = RescheduleForm(**formargs) @@ -535,100 +335,119 @@ def handle_reschedule_form(request, doc, dates): return form def agenda_documents(request): - dates = TelechatDates.objects.all()[0].dates() - from ietf.doc.models import TelechatDocEvent - docs = [] - for d in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).distinct(): - if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date in dates: - docs.append(d) + dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4]) - e = d.latest_event(type="started_iesg_process") - d.balloting_started = e.time if e else datetime.datetime.min - docs.sort(key=lambda d: d.balloting_started) - for i in docs: - i.reschedule_form = handle_reschedule_form(request, i, dates) + docs_by_date = dict((d, []) for d in dates) + for doc in Document.objects.filter(docevent__telechatdocevent__telechat_date__in=dates).select_related("stream", "group").distinct(): + d = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date + if d in docs_by_date: + docs_by_date[d].append(doc) - # some may have been taken off the schedule by the reschedule form - docs = [d for d in docs if d.telechat_date()] + reschedule_status = { "changed": False } + + for i in itertools.chain(*docs_by_date.values()): + i.reschedule_form = handle_reschedule_form(request, i, dates, reschedule_status) + + if reschedule_status["changed"]: + # if any were changed, redirect so the browser history is preserved + return redirect("ietf.iesg.views.agenda_documents") telechats = [] for date in dates: - matches = filter(lambda x: x.telechat_date() == date, docs) - res = {} - for i in matches: - section_key = "s" + get_doc_section(i) - if section_key not in res: - res[section_key] = [] - if i.type_id=='draft': - if i.get_state_slug()!="rfc": - i.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(i.name) - else: - i.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(i.rfc_number()) - i.iprCount = len(i.ipr()) - res[section_key].append(i) - telechats.append({'date':date, 'docs':res}) - return direct_to_template(request, 'iesg/agenda_documents_redesign.html', {'telechats':telechats, 'hide_telechat_date':True}) + sections = agenda_sections() + fill_in_agenda_docs(date, sections, docs_by_date[d]) + + for doc in docs_by_date[d]: + if doc.type_id=='draft': + if doc.get_state_slug() != "rfc": + doc.iprUrl = "/ipr/search?option=document_search&id_document_tag=" + str(doc.name) + else: + doc.iprUrl = "/ipr/search?option=rfc_search&rfc_search=" + str(doc.rfc_number()) + doc.iprCount = len(doc.ipr()) + + telechats.append({ + "date":date, + "sections": sorted((num, section) for num, section in sections.iteritems() + if "2" <= num < "5") + }) + return direct_to_template(request, 'iesg/agenda_documents.html', { 'telechats':telechats }) + +def telechat_docs_tarfile(request, date): + date = get_agenda_date(date) + + docs = [] + for d in Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct(): + if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date: + docs.append(d) -def telechat_docs_tarfile(request,year,month,day): - from tempfile import mkstemp - date=datetime.date(int(year),int(month),int(day)) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import TelechatDocEvent - docs = [] - for d in IDInternal.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct(): - if d.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date == date: - docs.append(d) - else: - docs= IDInternal.objects.filter(telechat_date=date, primary_flag=1, agenda=1) response = HttpResponse(mimetype='application/octet-stream') - response['Content-Disposition'] = 'attachment; filename=telechat-%s-%s-%s-docs.tgz'%(year, month, day) - tarstream = tarfile.open('','w:gz',response) - mfh, mfn = mkstemp() - manifest = open(mfn, "w") + response['Content-Disposition'] = 'attachment; filename=telechat-%s-docs.tgz' % date.isoformat() + + tarstream = tarfile.open('', 'w:gz', response) + + manifest = StringIO.StringIO() + for doc in docs: - doc_path = os.path.join(settings.INTERNET_DRAFT_PATH, doc.draft.filename+"-"+doc.draft.revision_display()+".txt") + doc_path = os.path.join(doc.get_file_path(), doc.name + "-" + doc.rev + ".txt") if os.path.exists(doc_path): try: - tarstream.add(doc_path, str(doc.draft.filename+"-"+doc.draft.revision_display()+".txt")) - manifest.write("Included: "+doc_path+"\n") - except Exception, e: - manifest.write(("Failed (%s): "%e)+doc_path+"\n") + tarstream.add(doc_path, str(doc.name + "-" + doc.rev + ".txt")) + manifest.write("Included: %s\n" % doc_path) + except Exception as e: + manifest.write("Failed (%s): %s\n" % (e, doc_path)) else: - manifest.write("Not found: "+doc_path+"\n") - manifest.close() - tarstream.add(mfn, "manifest.txt") + manifest.write("Not found: %s\n" % doc_path) + + manifest.seek(0) + t = tarfile.TarInfo(name="manifest.txt") + t.size = len(manifest.buf) + t.mtime = time.time() + tarstream.addfile(t, manifest) + tarstream.close() - os.unlink(mfn) + return response def discusses(request): - res = [] + possible_docs = Document.objects.filter(models.Q(states__type="draft-iesg", + states__slug__in=IESG_BALLOT_ACTIVE_STATES) | + models.Q(states__type="charter", + states__slug__in=("intrev", "iesgrev")) | + models.Q(states__type__in=("statchg", "conflrev"), + states__slug__in=("iesgeval", "defer")), + docevent__ballotpositiondocevent__pos__blocking=True) + possible_docs = possible_docs.select_related("stream", "group", "ad").distinct() - for d in IDInternal.objects.filter(states__type="draft-iesg", states__slug__in=("pub-req", "ad-eval", "review-e", "lc-req", "lc", "writeupw", "goaheadw", "iesg-eva", "defer", "watching"), docevent__ballotpositiondocevent__pos="discuss").distinct(): - found = False - for p in d.positions.all(): - if p.discuss: - found = True - break - - if not found: + docs = [] + for doc in possible_docs: + ballot = doc.active_ballot() + if not ballot: continue - if d.rfc_flag: - doc = RfcWrapper(d) - else: - doc = IdWrapper(draft=d) + blocking_positions = [p for p in ballot.all_positions() if p.pos.blocking] - if doc.in_ietf_process() and doc.ietf_process.has_active_iesg_ballot(): - res.append(doc) + if not blocking_positions: + continue - return direct_to_template(request, 'iesg/discusses.html', {'docs':res}) + augment_events_with_revision(doc, blocking_positions) + + doc.by_me = bool([p for p in blocking_positions if user_is_person(request.user, p.ad)]) + doc.for_me = user_is_person(request.user, doc.ad) + doc.milestones = doc.groupmilestone_set.filter(state="active").order_by("time").select_related("group") + doc.blocking_positions = blocking_positions + + docs.append(doc) + + # latest first + docs.sort(key=lambda d: min(p.time for p in d.blocking_positions), reverse=True) + + return direct_to_template(request, 'iesg/discusses.html', { 'docs': docs }) @role_required('Area Director', 'Secretariat') def milestones_needing_review(request): # collect milestones, grouped on AD and group ads = {} - for m in GroupMilestone.objects.filter(state="review").exclude(group__state="concluded", group__ad=None).distinct().select_related("group", "group__ad"): + for m in GroupMilestone.objects.filter(state="review").exclude(group__state="concluded").exclude(group__ad=None).distinct().select_related("group", "group__ad"): groups = ads.setdefault(m.group.ad, {}) milestones = groups.setdefault(m.group, []) milestones.append(m) @@ -645,162 +464,3 @@ def milestones_needing_review(request): ), context_instance=RequestContext(request)) -def parse_wg_action_file(path): - f = open(path, 'rU') - - line = f.readline() - while line and not line.strip(): - line = f.readline() - - # name - m = re.search(r'([^\(]*) \(', line) - if not m: - return None - name = m.group(1) - - # acronym - m = re.search(r'\((\w+)\)', line) - if not m: - return None - acronym = m.group(1) - - # date - line = f.readline() - m = re.search(r'(\d{4})-(\d{2})-(\d{2})', line) - while line and not m: - line = f.readline() - m = re.search(r'(\d{4})-(\d{2})-(\d{2})', line) - - last_updated = None - if m: - try: - last_updated = datetime.date(int(m.group(1)), int(m.group(2)), int(m.group(3))) - except: - pass - - # token - line = f.readline() - while line and not 'area director' in line.lower(): - line = f.readline() - - line = f.readline() - line = f.readline() - m = re.search(r'\s*(\w+)\s*', line) - token = "" - if m: - token = m.group(1) - - return dict(filename=os.path.basename(path), name=name, acronym=acronym, - status_date=last_updated, token=token) - -def get_possible_wg_actions(): - res = [] - charters = glob.glob(os.path.join(settings.IESG_WG_EVALUATION_DIR, '*-charter.txt')) - for path in charters: - d = parse_wg_action_file(path) - if d: - if not d['status_date']: - d['status_date'] = datetime.date(1900,1,1) - res.append(d) - - res.sort(key=lambda x: x['status_date']) - - return res - - -@group_required('Area_Director', 'Secretariat') -def working_group_actions(request): - current_items = WGAction.objects.order_by('status_date').select_related() - - if request.method == 'POST' and has_role(request.user, 'Secretariat'): - filename = request.POST.get('filename') - if filename and filename in os.listdir(settings.IESG_WG_EVALUATION_DIR): - if 'delete' in request.POST: - os.unlink(os.path.join(settings.IESG_WG_EVALUATION_DIR, filename)) - if 'add' in request.POST: - d = parse_wg_action_file(os.path.join(settings.IESG_WG_EVALUATION_DIR, filename)) - qstr = "?" + "&".join("%s=%s" % t for t in d.iteritems()) - return HttpResponseRedirect(urlreverse('iesg_add_working_group_action') + qstr) - - - skip = [c.group_acronym.acronym for c in current_items] - possible_items = filter(lambda x: x['acronym'] not in skip, - get_possible_wg_actions()) - - return render_to_response("iesg/working_group_actions.html", - dict(current_items=current_items, - possible_items=possible_items), - context_instance=RequestContext(request)) - -class EditWGActionForm(forms.ModelForm): - token_name = forms.ChoiceField(required=True) - telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False) - - class Meta: - model = WGAction - fields = ['status_date', 'token_name', 'category', 'note'] - - def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - - # token name choices - self.fields['token_name'].choices = [("", "(None)")] + [(p.plain_name(), p.plain_name()) for p in IESGLogin.active_iesg().order_by('first_name')] - - # telechat choices - dates = TelechatDates.objects.all()[0].dates() - init = kwargs['initial']['telechat_date'] - if init and init not in dates: - dates.insert(0, init) - - choices = [("", "(not on agenda)")] - for d in dates: - choices.append((d, d.strftime("%Y-%m-%d"))) - - self.fields['telechat_date'].choices = choices - - -@group_required('Secretariat') -def edit_working_group_action(request, wga_id): - if wga_id != None: - wga = get_object_or_404(WGAction, pk=wga_id) - else: - wga = WGAction() - try: - wga.group_acronym = Acronym.objects.get(acronym=request.GET.get('acronym')) - except Acronym.DoesNotExist: - pass - - wga.token_name = request.GET.get('token') - try: - d = datetime.datetime.strptime(request.GET.get('status_date'), '%Y-%m-%d').date() - except: - d = datetime.date.today() - wga.status_date = d - wga.telechat_date = TelechatDates.objects.all()[0].date1 - wga.agenda = True - - initial = dict(telechat_date=wga.telechat_date if wga.agenda else None) - - if request.method == 'POST': - if "delete" in request.POST: - wga.delete() - return HttpResponseRedirect(urlreverse('iesg_working_group_actions')) - - form = EditWGActionForm(request.POST, instance=wga, initial=initial) - if form.is_valid(): - form.save(commit=False) - wga.agenda = bool(form.cleaned_data['telechat_date']) - if wga.category in (11, 21): - wga.agenda = False - if wga.agenda: - wga.telechat_date = form.cleaned_data['telechat_date'] - wga.save() - return HttpResponseRedirect(urlreverse('iesg_working_group_actions')) - else: - form = EditWGActionForm(instance=wga, initial=initial) - - - return render_to_response("iesg/edit_working_group_action.html", - dict(wga=wga, - form=form), - context_instance=RequestContext(request)) diff --git a/ietf/ietfworkflows/.gitignore b/ietf/ietfworkflows/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/__init__.py b/ietf/ietfworkflows/__init__.py deleted file mode 100644 index e8d53c9a3..000000000 --- a/ietf/ietfworkflows/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# coding: latin-1 - -from types import ModuleType -import urls, models, views, forms, accounts - -# These people will be sent a stack trace if there's an uncaught exception in -# code any of the modules imported above: -DEBUG_EMAILS = [ - ('Emilio A. Sánchez', 'esanchez@yaco.es'), -] - -for k in locals().keys(): - m = locals()[k] - if isinstance(m, ModuleType): - if hasattr(m, "DEBUG_EMAILS"): - DEBUG_EMAILS += list(getattr(m, "DEBUG_EMAILS")) - setattr(m, "DEBUG_EMAILS", DEBUG_EMAILS) - diff --git a/ietf/ietfworkflows/accounts.py b/ietf/ietfworkflows/accounts.py deleted file mode 100644 index c687bda85..000000000 --- a/ietf/ietfworkflows/accounts.py +++ /dev/null @@ -1,132 +0,0 @@ -from django.conf import settings - -from django.db.models import Q - -from ietf.ietfworkflows.streams import get_streamed_draft -from ietf.group.models import Role - - -def get_person_for_user(user): - try: - return user.get_profile().person() - except: - return None - - -def is_secretariat(user): - if not user or not user.is_authenticated(): - return False - return bool(user.groups.filter(name='Secretariat')) - - -def is_wgchair(person): - return bool(person.wgchair_set.all()) - -def is_wgchairREDESIGN(person): - return bool(Role.objects.filter(name="chair", group__type="wg", group__state="active", person=person)) - -def is_rgchairREDESIGN(person): - return bool(Role.objects.filter(name="chair", group__type="rg", group__state="active", person=person)) - -def is_wgdelegate(person): - return bool(person.wgdelegate_set.all()) - -def is_wgdelegateREDESIGN(person): - return bool(Role.objects.filter(name="delegate", group__type="wg", group__state="active", person=person)) - -def is_rgdelegateREDESIGN(person): - return bool(Role.objects.filter(name="delegate", group__type="rg", group__state="active", person=person)) - -def is_delegate_of_stream(user, stream): - if is_secretariat(user): - return True - person = get_person_for_user(user) - return stream.check_delegate(person) - -def is_delegate_of_streamREDESIGN(user, stream): - if is_secretariat(user): - return True - return user.is_authenticated() and bool(Role.objects.filter(group__acronym=stream.slug, name="delegate", person__user=user)) - - -def is_chair_of_stream(user, stream): - if is_secretariat(user): - return True - person = get_person_for_user(user) - return stream.check_chair(person) - -def is_chair_of_streamREDESIGN(user, stream): - if is_secretariat(user): - return True - if isinstance(user, basestring): - return False - return user.is_authenticated() and bool(Role.objects.filter(group__acronym=stream.slug, name="chair", person__user=user)) - -def is_authorized_in_draft_stream(user, draft): - if is_secretariat(user): - return True - person = get_person_for_user(user) - if not person: - return False - streamed = get_streamed_draft(draft) - if not streamed or not streamed.stream: - return False - # Check if the person is chair of the stream - if is_chair_of_stream(user, streamed.stream): - return True - # Check if the person is delegate of the stream - if is_delegate_of_stream(user, streamed.stream): - return True - # Check if the person is chair of the related group - chairs = streamed.stream.get_chairs_for_document(draft) - if chairs and person in [i.person for i in chairs]: - return True - # Check if the person is authorized by a delegate system - delegates = streamed.stream.get_delegates_for_document(draft) - return bool(person in delegates) - -def is_authorized_in_draft_streamREDESIGN(user, draft): - if is_secretariat(user): - return True - - from ietf.doc.models import Document - - if not super(Document, draft).stream: - return False - - # must be a chair or delegate of the stream group (or draft group) - group_req = Q(group__acronym=super(Document, draft).stream.slug) - if draft.group and super(Document, draft).stream.slug in ["ietf", "irtf"]: - group_req |= Q(group=draft.group) - - return user.is_authenticated() and bool(Role.objects.filter(name__in=("chair", "secr", "delegate"), person__user=user).filter(group_req)) - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.accounts import is_secretariat, get_person_for_user - is_wgdelegate = is_wgdelegateREDESIGN - is_wgchair = is_wgchairREDESIGN - is_rgdelegate = is_rgdelegateREDESIGN - is_rgchair = is_rgchairREDESIGN - is_chair_of_stream = is_chair_of_streamREDESIGN - is_delegate_of_stream = is_delegate_of_streamREDESIGN - is_authorized_in_draft_stream = is_authorized_in_draft_streamREDESIGN - - -def can_edit_state(user, draft): - return (is_secretariat(user) or - is_authorized_in_draft_stream(user, draft)) - - -def can_edit_stream(user, draft): - return is_secretariat(user) - -def can_adopt(user, draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES and (not draft.stream_id or draft.stream_id in ["ietf", "irtf"]) and draft.group.type_id == "individ": - person = get_person_for_user(user) - if not person: - return False - return is_wgchair(person) or is_rgchair(person) or is_wgdelegate(person) or is_rgdelegate() or is_secretariat(user) - else: - return is_secretariat(user) - diff --git a/ietf/ietfworkflows/constants.py b/ietf/ietfworkflows/constants.py deleted file mode 100644 index d29a1164f..000000000 --- a/ietf/ietfworkflows/constants.py +++ /dev/null @@ -1,13 +0,0 @@ -# Required states -CALL_FOR_ADOPTION = 'Call For Adoption By WG Issued' -WG_DOCUMENT = 'WG Document' -SUBMITTED_TO_IESG = 'Submitted to IESG for Publication' - -REQUIRED_STATES = ( - CALL_FOR_ADOPTION, - WG_DOCUMENT, - SUBMITTED_TO_IESG, - ) - -# IETF Stream -IETF_STREAM = 'IETF' diff --git a/ietf/ietfworkflows/fixtures/.gitignore b/ietf/ietfworkflows/fixtures/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/fixtures/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/fixtures/initial_data.xml b/ietf/ietfworkflows/fixtures/initial_data.xml deleted file mode 100644 index 51e06c12f..000000000 --- a/ietf/ietfworkflows/fixtures/initial_data.xml +++ /dev/null @@ -1,686 +0,0 @@ - - - - Default WG Workflow - 11 - - - IAB Workflow - - - - IRTF Workflow - - - - ISE Workflow - - - - Active IAB Document - 2 - - - - Active RG Document - 3 - - - - Adopted by a WG - 1 - - - - Adopted for WG Info Only - 1 - - - - Approved by IAB, To Be Sent to RFC Editor - 2 - - - - Awaiting IRSG Reviews - 3 - - - - Call For Adoption By WG Issued - 1 - - - - Candidate IAB Document - 2 - - - - Candidate RG Document - 3 - - - - Community Review - 2 - - - - Dead IAB Document - 2 - - - - Dead IRTF Document - 3 - - - - Dead WG Document - 1 - - - - Document on Hold Based On IESG Request - 3 - - - - Document on Hold Based On IESG Request - 4 - - - - Finding Reviewers - 4 - - - - IAB Review - 2 - - - - In IESG Review - 3 - - - - In IESG Review - 4 - - - - In IRSG Poll - 3 - - - - In ISE Review - 4 - - - - In RG Last Call - 3 - - - - In WG Last Call - 1 - - - - No Longer In Independent Submission Stream - 4 - - - - Parked IAB Document - 2 - - - - Parked RG Document - 3 - - - - Parked WG Document - 1 - - - - Response to Review Needed - 4 - - - - Sent to a Different Organization for Publication - 2 - - - - Sent to the RFC Editor - 2 - - - - Sent to the RFC Editor - 4 - - - - Sent to the RFC Editor - 3 - - - - Submission Received - 4 - - - - Submitted to IESG for Publication - 1 - - - - Waiting for Document Shepherd - 3 - - - - Waiting for IRTF Chair - 3 - - - - Waiting for WG Chair Go-Ahead - 1 - - - - WG Consensus: Waiting for Write-Up - 1 - - - - WG Document - 1 - - - - Wait for go-ahead - 1 - 18 - - - - - Reach consensus - 1 - 19 - - - - - Adopt - 1 - 12 - - - - - Adopt for WG info only - 1 - 13 - - - - - Develop - 1 - 14 - - - - - Park - 1 - 15 - - - - - Die - 1 - 16 - - - - - Submit to IESG - 1 - 20 - - - - - Raise last call - 1 - 17 - - - - - Revised I-D Needed - 3 - - - - Shepherd Needed - 3 - - - - Waiting for Dependency on Other Document - 3 - - - - Revised I-D Needed - 2 - - - - Document Shepherd Followup - 2 - - - - Editor Needed - 3 - - - - Awaiting Reviews - 2 - - - - Waiting for Partner Feedback - 2 - - - - Other - see Comment Log - 1 - - - - Editor Needed - 2 - - - - Doc Shepherd Follow-Up Underway - 1 - - - - Revised I-D Needed - Issue raised by IESG - 1 - - - - Awaiting Expert Review/Resolution of Issues Raised - 1 - - - - Awaiting External Review/Resolution of Issues Raised - 1 - - - - Awaiting Merge with Other Document - 1 - - - - Author or Editor Needed - 1 - - - - Waiting for Referenced Document - 1 - - - - Revised I-D Needed - Issue raised by AD - 1 - - - - Revised I-D Needed - Issue raised by WGLC - 1 - - - - Waiting for Referencing Document - 1 - - - - IESG Review Completed - 3 - - - - Waiting for Dependency on Other Document - 4 - - - - Awaiting Reviews - 4 - - - - Revised I-D Needed - 4 - - - - IESG Review Completed - 4 - - - - - - - - - - - - - - - - - - - - IETF - group.ietfwg - chairs - 1 - - - IAB - - - 2 - - - IRTF - - - 3 - - - ISE - - - 4 - - - 11 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.1" target="_blank">4.2.1. Call for Adoption by WG Issued</a> - - - The "Call for Adoption by WG Issued" state should be used to indicate - when an I-D is being considered for adoption by an IETF WG. An I-D - that is in this state is actively being considered for adoption and - has not yet achieved consensus, preference, or selection in the WG. - - This state may be used to describe an I-D that someone has asked a WG - to consider for adoption, if the WG Chair has agreed with the - request. This state may also be used to identify an I-D that a WG - Chair asked an author to write specifically for consideration as a - candidate WG item [WGDTSPEC], and/or an I-D that is listed as a - 'candidate draft' in the WG's charter. - - Under normal conditions, it should not be possible for an I-D to be - in the "Call for Adoption by WG Issued" state in more than one - working group at the same time. This said, it is not uncommon for - authors to "shop" their I-Ds to more than one WG at a time, with the - hope of getting their documents adopted somewhere. - - After this state is implemented in the Datatracker, an I-D that is in - the "Call for Adoption by WG Issued" state will not be able to be - "shopped" to any other WG without the consent of the WG Chairs and - the responsible ADs impacted by the shopping. - - Note that Figure 1 includes an arc leading from this state to outside - of the WG state machine. This illustrates that some I-Ds that are - considered do not get adopted as WG drafts. An I-D that is not - adopted as a WG draft will transition out of the WG state machine and - revert back to having no stream-specific state; however, the status - change history log of the I-D will record that the I-D was previously - in the "Call for Adoption by WG Issued" state. - - 1 - - - 12 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.2" target="_blank">4.2.2. Adopted by a WG</a> - - - The "Adopted by a WG" state describes an individual submission I-D - that an IETF WG has agreed to adopt as one of its WG drafts. - - WG Chairs who use this state will be able to clearly indicate when - their WGs adopt individual submission I-Ds. This will facilitate the - Datatracker's ability to correctly capture "Replaces" information for - WG drafts and correct "Replaced by" information for individual - submission I-Ds that have been replaced by WG drafts. - - This state is needed because the Datatracker uses the filename of an - I-D as a key to search its database for status information about the - I-D, and because the filename of a WG I-D is supposed to be different - from the filename of an individual submission I-D. - The filename of an individual submission I-D will typically be - formatted as 'draft-author-wgname-topic-nn'. - - The filename of a WG document is supposed to be formatted as 'draft- - ietf-wgname-topic-nn'. - - An individual I-D that is adopted by a WG may take weeks or months to - be resubmitted by the author as a new (version-00) WG draft. If the - "Adopted by a WG" state is not used, the Datatracker has no way to - determine that an I-D has been adopted until a new version of the I-D - is submitted to the WG by the author and until the I-D is approved - for posting by a WG Chair. - - 2 - - - 13 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.3" target="_blank">4.2.3. Adopted for WG Info Only</a> - - - The "Adopted for WG Info Only" state describes a document that - contains useful information for the WG that adopted it, but the - document is not intended to be published as an RFC. The WG will not - actively develop the contents of the I-D or progress it for - publication as an RFC. The only purpose of the I-D is to provide - information for internal use by the WG. - - 3 - - - 14 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.4" target="_blank">4.2.4. WG Document</a> - - - The "WG Document" state describes an I-D that has been adopted by an - IETF WG and is being actively developed. - - A WG Chair may transition an I-D into the "WG Document" state at any - time as long as the I-D is not being considered or developed in any - other WG. - - Alternatively, WG Chairs may rely upon new functionality to be added - to the Datatracker to automatically move version-00 drafts into the - "WG Document" state as described in Section 4.1. - - Under normal conditions, it should not be possible for an I-D to be - in the "WG Document" state in more than one WG at a time. This said, - I-Ds may be transferred from one WG to another with the consent of - the WG Chairs and the responsible ADs. - - - 4 - - - 15 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.5" target="_blank">4.2.5. Parked WG Document</a> - - - A "Parked WG Document" is an I-D that has lost its author or editor, - is waiting for another document to be written or for a review to be - completed, or cannot be progressed by the working group for some - other reason. - - Some of the annotation tags described in Section 4.3 may be used in - conjunction with this state to indicate why an I-D has been parked, - and/or what may need to happen for the I-D to be un-parked. - - Parking a WG draft will not prevent it from expiring; however, this - state can be used to indicate why the I-D has stopped progressing in - the WG. - - A "Parked WG Document" that is not expired may be transferred from - one WG to another with the consent of the WG Chairs and the - responsible ADs. - - - 5 - - - 16 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.6" target="_blank">4.2.6. Dead WG Document</a> - - - A "Dead WG Document" is an I-D that has been abandoned. Note that - 'Dead' is not always a final state for a WG I-D. If consensus is - subsequently achieved, a "Dead WG Document" may be resurrected. A - "Dead WG Document" that is not resurrected will eventually expire. - - Note that an I-D that is declared to be "Dead" in one WG and that is - not expired may be transferred to a non-dead state in another WG with - the consent of the WG Chairs and the responsible ADs. - - - 6 - - - 17 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.7" target="_blank">4.2.7. In WG Last Call</a> - - - A document "In WG Last Call" is an I-D for which a WG Last Call - (WGLC) has been issued and is in progress. - - Note that conducting a WGLC is an optional part of the IETF WG - process, per Section 7.4 of RFC 2418 [RFC2418]. - - If a WG Chair decides to conduct a WGLC on an I-D, the "In WG Last - Call" state can be used to track the progress of the WGLC. The Chair - may configure the Datatracker to send a WGLC message to one or more - mailing lists when the Chair moves the I-D into this state. The WG - Chair may also be able to select a different set of mailing lists for - a different document undergoing a WGLC; some documents may deserve - coordination with other WGs. - - A WG I-D in this state should remain "In WG Last Call" until the WG - Chair moves it to another state. The WG Chair may configure the - Datatracker to send an e-mail after a specified period of time to - remind or 'nudge' the Chair to conclude the WGLC and to determine the - next state for the document. - - It is possible for one WGLC to lead into another WGLC for the same - document. For example, an I-D that completed a WGLC as an - "Informational" document may need another WGLC if a decision is taken - to convert the I-D into a Standards Track document. - - - 7 - - - 18 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.8" target="_blank">4.2.8. Waiting for WG Chair Go-Ahead</a> - - - A WG Chair may wish to place an I-D that receives a lot of comments - during a WGLC into the "Waiting for WG Chair Go-Ahead" state. This - state describes an I-D that has undergone a WGLC; however, the Chair - is not yet ready to call consensus on the document. - - If comments from the WGLC need to be responded to, or a revision to - the I-D is needed, the Chair may place an I-D into this state until - all of the WGLC comments are adequately addressed and the (possibly - revised) document is in the I-D repository. - - - 8 - - - 19 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.9" target="_blank">4.2.9. WG Consensus: Waiting for Writeup</a> - - - A document in the "WG Consensus: Waiting for Writeup" state has - essentially completed its development within the working group, and - is nearly ready to be sent to the IESG for publication. The last - thing to be done is the preparation of a protocol writeup by a - Document Shepherd. The IESG requires that a document shepherd - writeup be completed before publication of the I-D is requested. The - IETF document shepherding process and the role of a WG Document - Shepherd is described in RFC 4858 [RFC4858] - - A WG Chair may call consensus on an I-D without a formal WGLC and - transition an I-D that was in the "WG Document" state directly into - this state. - - The name of this state includes the words "Waiting for Writeup" - because a good document shepherd writeup takes time to prepare. - - - 9 - - - 20 - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.10" target="_blank">4.2.10. Submitted to IESG for Publication</a> - - - This state describes a WG document that has been submitted to the - IESG for publication and that has not been sent back to the working - group for revision. - - An I-D in this state may be under review by the IESG, it may have - been approved and be in the RFC Editor's queue, or it may have been - published as an RFC. Other possibilities exist too. The document - may be "Dead" (in the IESG state machine) or in a "Do Not Publish" - state. - - - 10 - - diff --git a/ietf/ietfworkflows/forms.py b/ietf/ietfworkflows/forms.py deleted file mode 100644 index 6532f144b..000000000 --- a/ietf/ietfworkflows/forms.py +++ /dev/null @@ -1,363 +0,0 @@ -import datetime - -from django.conf import settings -from django import forms -from django.template.loader import render_to_string -from workflows.models import State -from workflows.utils import set_workflow_for_object - -from ietf.idtracker.models import PersonOrOrgInfo, IETFWG, InternetDraft -from ietf.wgchairs.accounts import get_person_for_user -from ietf.ietfworkflows.models import Stream, StreamDelegate -from ietf.ietfworkflows.utils import (get_workflow_for_draft, get_workflow_for_wg, - get_state_for_draft, get_state_by_name, - update_state, FOLLOWUP_TAG, - get_annotation_tags_for_draft, - update_tags, update_stream) -from ietf.ietfworkflows.accounts import is_secretariat -from ietf.ietfworkflows.streams import (get_stream_from_draft, get_streamed_draft, - get_stream_by_name, set_stream_for_draft) -from ietf.ietfworkflows.constants import CALL_FOR_ADOPTION, IETF_STREAM -from ietf.doc.utils import get_tags_for_stream_id -from ietf.doc.models import save_document_in_history, DocEvent, Document -from ietf.name.models import DocTagName, StreamName, RoleName -from ietf.group.models import Group, GroupStateTransitions, Role -from ietf.group.utils import save_group_in_history -from ietf.person.models import Person, Email - -class StreamDraftForm(forms.Form): - - can_cancel = False - template = None - - def __init__(self, *args, **kwargs): - self.draft = kwargs.pop('draft', None) - self.user = kwargs.pop('user', None) - self.person = get_person_for_user(self.user) - self.workflow = get_workflow_for_draft(self.draft) - self.message = {} - super(StreamDraftForm, self).__init__(*args, **kwargs) - - def get_message(self): - return self.message - - def set_message(self, msg_type, msg_value): - self.message = {'type': msg_type, - 'value': msg_value, - } - - def __unicode__(self): - return render_to_string(self.template, {'form': self}) - - -class NoWorkflowStateForm(StreamDraftForm): - comment = forms.CharField(widget=forms.Textarea, required=False) - weeks = forms.IntegerField(required=False) - group = forms.ChoiceField(required=False) - - template = 'ietfworkflows/noworkflow_state_form.html' - - def __init__(self, *args, **kwargs): - super(NoWorkflowStateForm, self).__init__(*args, **kwargs) - self.groups = None - if is_secretariat(self.user): - groups = IETFWG.objects.all().order_by('group_acronym__acronym') - else: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - groups = IETFWG.objects.filter(type__in=["wg", "rg"], state="active", role__name__in=("chair", "secr", "delegate"), role__person__user=self.user).order_by('acronym').distinct() - else: - groups = set([i.group_acronym for i in self.person.wgchair_set.all()]).union(set([i.wg for i in self.person.wgdelegate_set.all()])) - groups = list(groups) - groups.sort(lambda x, y: cmp(x.group_acronym.acronym, y.group_acronym.acronym)) - self.groups = groups - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - self.fields['group'].choices = [(i.pk, '%s - %s' % (i.acronym, i.name)) for i in self.groups] - else: - self.fields['group'].choices = [(i.pk, '%s - %s' % (i.group_acronym.acronym, i.group_acronym.name)) for i in self.groups] - - def save(self): - comment = self.cleaned_data.get('comment').strip() - weeks = self.cleaned_data.get('weeks') - group = IETFWG.objects.get(pk=self.cleaned_data.get('group')) - estimated_date = None - if weeks: - now = datetime.date.today() - estimated_date = now + datetime.timedelta(weeks=weeks) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # do changes on real Document object instead of proxy to avoid trouble - doc = Document.objects.get(pk=self.draft.pk) - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - - if group.type.slug == "rg": - new_stream = StreamName.objects.get(slug="irtf") - else: - new_stream = StreamName.objects.get(slug="ietf") - - if doc.stream != new_stream: - e = DocEvent(type="changed_stream") - e.time = doc.time - e.by = self.user.get_profile() - e.doc = doc - e.desc = u"Changed to %s" % new_stream.name - if doc.stream: - e.desc += u" from %s" % doc.stream.name - e.save() - doc.stream = new_stream - - if doc.group.pk != group.pk: - e = DocEvent(type="changed_group") - e.time = doc.time - e.by = self.user.get_profile() - e.doc = doc - e.desc = u"Changed group to %s (%s)" % (group.name, group.acronym.upper()) - if doc.group.type_id != "individ": - e.desc += " from %s (%s)" % (doc.group.name, doc.group.acronym) - e.save() - doc.group_id = group.pk - - doc.save() - self.draft = InternetDraft.objects.get(pk=doc.pk) # make sure proxy object is updated - else: - workflow = get_workflow_for_wg(wg) - set_workflow_for_object(self.draft, workflow) - stream = get_stream_by_name(IETF_STREAM) - streamed = get_streamed_draft(self.draft) - if not streamed: - set_stream_for_draft(self.draft, stream) - streamed = get_streamed_draft(self.draft) - streamed.stream = stream - streamed.group = wg - streamed.save() - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import State - if self.draft.stream_id == "irtf": - to_state = State.objects.get(used=True, slug="active", type="draft-stream-irtf") - else: - to_state = State.objects.get(used=True, slug="c-adopt", type="draft-stream-%s" % self.draft.stream_id) - else: - to_state = get_state_by_name(CALL_FOR_ADOPTION) - update_state(self.request, self.draft, - comment=comment, - person=self.person, - to_state=to_state, - added_tags=[], - removed_tags=[], - estimated_date=estimated_date) - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if comment: - e = DocEvent(type="added_comment") - e.time = self.draft.time - e.by = self.person - e.doc_id = self.draft.pk - e.desc = comment - e.save() - -class DraftTagsStateForm(StreamDraftForm): - - new_state = forms.ChoiceField(label='State') - weeks = forms.IntegerField(label='Expected weeks in state',required=False) - comment = forms.CharField(widget=forms.Textarea, required=False) - tags = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False) - - template = 'ietfworkflows/state_form.html' - - def __init__(self, *args, **kwargs): - super(DraftTagsStateForm, self).__init__(*args, **kwargs) - self.state = get_state_for_draft(self.draft) - self.fields['new_state'].choices = self.get_states() - self.fields['new_state'].initial = self.state.pk if self.state else None - if self.draft.stream_id == 'ietf': - self.fields['new_state'].help_text = "Only select 'Submitted to IESG for Publication' to correct errors. Use the document's main page to request publication." - if self.is_bound: - for key, value in self.data.items(): - if key.startswith('transition_'): - new_state = self.get_new_state(key) - if new_state: - self.data = self.data.copy() - self.data.update({'new_state': new_state.id}) - if key.startswith('new_state_'): # hack to get value from submit buttons - self.data = self.data.copy() - self.data['new_state'] = key.replace('new_state_', '') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - possible_tags = get_tags_for_stream_id(self.draft.stream_id) - if self.draft.stream_id == "ietf" and self.draft.group: - unused_tags = self.draft.group.unused_tags.values_list("slug", flat=True) - possible_tags = [t for t in possible_tags if t not in unused_tags] - self.available_tags = DocTagName.objects.filter(slug__in=possible_tags) - self.tags = self.draft.tags.filter(slug__in=possible_tags) - else: - self.available_tags = self.workflow.get_tags() - self.tags = [i.annotation_tag for i in get_annotation_tags_for_draft(self.draft)] - - self.fields['tags'].choices = [(i.pk, i.name) for i in self.available_tags] - self.fields['tags'].initial = [i.pk for i in self.tags] - - def get_new_state(self, key): - transition_id = key.replace('transition_', '') - transition = self.get_transitions().filter(id=transition_id) - if transition: - return transition[0].destination - return None - - def get_transitions(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return [] - return self.state.transitions.filter(workflow=self.workflow) - - def get_next_states(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if not self.draft.stream_id: - return [] - - from ietf.doc.models import State - state_type = "draft-stream-%s" % self.draft.stream_id - s = self.draft.get_state(state_type) - next_states = [] - if s: - next_states = s.next_states.all() - - if self.draft.stream_id == "ietf" and self.draft.group: - transitions = self.draft.group.groupstatetransitions_set.filter(state=s) - if transitions: - next_states = transitions[0].next_states.all() - else: - # return the initial state - states = State.objects.filter(used=True, type=state_type).order_by('order') - if states: - next_states = states[:1] - - unused = [] - if self.draft.group: - unused = self.draft.group.unused_states.values_list("pk", flat=True) - return [n for n in next_states if n.pk not in unused] - - return [] - - - def get_states(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if not self.draft.stream_id: - return [] - - from ietf.doc.models import State - states = State.objects.filter(used=True, type="draft-stream-%s" % self.draft.stream_id) - if self.draft.stream_id == "ietf" and self.draft.group: - unused_states = self.draft.group.unused_states.values_list("pk", flat=True) - states = [s for s in states if s.pk not in unused_states] - return [(i.pk, i.name) for i in states] - - return [(i.pk, i.name) for i in self.workflow.get_states()] - - def save_tags(self,send_email=True): - comment = self.cleaned_data.get('comment') - new_tags = self.cleaned_data.get('tags') - - set_tags = [tag for tag in self.available_tags if str(tag.pk) in new_tags and tag not in self.tags] - reset_tags = [tag for tag in self.available_tags if str(tag.pk) not in new_tags and tag in self.tags] - followup = bool([tag for tag in set_tags if tag.name == FOLLOWUP_TAG]) - extra_notify = [] - if followup: - try: - shepherd = self.draft.shepherd - if shepherd: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - extra_notify = [shepherd.formatted_email()] - else: - extra_notify = ['%s <%s>' % shepherd.email()] - except PersonOrOrgInfo.DoesNotExist: - pass - if not set_tags and not reset_tags: - return - update_tags(self.request, self.draft, - comment=comment, - person=self.person, - set_tags=set_tags, - reset_tags=reset_tags, - extra_notify=extra_notify, - send_email=send_email) - - def save_state(self): - comment = self.cleaned_data.get('comment') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.models import State - state = State.objects.get(pk=self.cleaned_data.get('new_state')) - - old_state = self.draft.get_state("draft-stream-%s" % self.draft.stream_id) - if state==old_state: - self.save_tags() - return - - self.save_tags(False) - new_tags = self.cleaned_data.get('tags') - - set_tags = [tag for tag in self.available_tags if str(tag.pk) in new_tags and tag not in self.tags] - reset_tags = [tag for tag in self.available_tags if str(tag.pk) not in new_tags and tag in self.tags] - - weeks = self.cleaned_data.get('weeks') - estimated_date = None - if weeks: - now = datetime.date.today() - estimated_date = now + datetime.timedelta(weeks=weeks) - - update_state(self.request, self.draft, - comment=comment, - person=self.person, - to_state=state, - estimated_date=estimated_date, - added_tags=set_tags, - removed_tags=reset_tags) - - def save(self): - self.save_state() - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - comment = self.cleaned_data.get('comment').strip() - if comment: - e = DocEvent(type="added_comment") - e.time = datetime.datetime.now() - e.by = self.person - e.doc_id = self.draft.pk - e.desc = comment - e.save() - - -class StreamDelegatesForm(forms.Form): - email = forms.EmailField() - - def __init__(self, *args, **kwargs): - self.stream = kwargs.pop('stream') - super(StreamDelegatesForm, self).__init__(*args, **kwargs) - - def get_person(self, email): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - persons = Person.objects.filter(email__address=email).distinct() - else: - persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).distinct() - if not persons: - return None - return persons[0] - - def clean_email(self): - email = self.cleaned_data.get('email') - self.person = self.get_person(email) - if not self.person: - raise forms.ValidationError('There is no user with this email in the system') - return email - - def save(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - stream_group = Group.objects.get(acronym=self.stream.slug) - save_group_in_history(stream_group) - Role.objects.get_or_create(person=self.person, - group=stream_group, - name=RoleName.objects.get(slug="delegate"), - email=Email.objects.get(address=self.cleaned_data.get('email'))) - return - - StreamDelegate.objects.get_or_create( - person=self.person, - stream=self.stream) diff --git a/ietf/ietfworkflows/migrations/.gitignore b/ietf/ietfworkflows/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/migrations/0001_initial.py b/ietf/ietfworkflows/migrations/0001_initial.py deleted file mode 100644 index 7d9f29e4f..000000000 --- a/ietf/ietfworkflows/migrations/0001_initial.py +++ /dev/null @@ -1,148 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'WGWorkflow' - db.create_table('ietfworkflows_wgworkflow', ( - ('workflow_ptr', orm['ietfworkflows.WGWorkflow:workflow_ptr']), - )) - db.send_create_signal('ietfworkflows', ['WGWorkflow']) - - # Adding model 'ObjectWorkflowHistoryEntry' - db.create_table('ietfworkflows_objectworkflowhistoryentry', ( - ('id', orm['ietfworkflows.ObjectWorkflowHistoryEntry:id']), - ('content_type', orm['ietfworkflows.ObjectWorkflowHistoryEntry:content_type']), - ('content_id', orm['ietfworkflows.ObjectWorkflowHistoryEntry:content_id']), - ('from_state', orm['ietfworkflows.ObjectWorkflowHistoryEntry:from_state']), - ('to_state', orm['ietfworkflows.ObjectWorkflowHistoryEntry:to_state']), - ('transition_date', orm['ietfworkflows.ObjectWorkflowHistoryEntry:transition_date']), - ('comment', orm['ietfworkflows.ObjectWorkflowHistoryEntry:comment']), - )) - db.send_create_signal('ietfworkflows', ['ObjectWorkflowHistoryEntry']) - - # Adding model 'ObjectAnnotationTagHistoryEntry' - db.create_table('ietfworkflows_objectannotationtaghistoryentry', ( - ('id', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:id']), - ('content_type', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:content_type']), - ('content_id', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:content_id']), - ('setted', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:setted']), - ('unsetted', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:unsetted']), - ('change_date', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:change_date']), - ('comment', orm['ietfworkflows.ObjectAnnotationTagHistoryEntry:comment']), - )) - db.send_create_signal('ietfworkflows', ['ObjectAnnotationTagHistoryEntry']) - - # Adding model 'AnnotationTag' - db.create_table('ietfworkflows_annotationtag', ( - ('id', orm['ietfworkflows.AnnotationTag:id']), - ('name', orm['ietfworkflows.AnnotationTag:name']), - ('workflow', orm['ietfworkflows.AnnotationTag:workflow']), - ('permission', orm['ietfworkflows.AnnotationTag:permission']), - )) - db.send_create_signal('ietfworkflows', ['AnnotationTag']) - - # Adding model 'AnnotationTagObjectRelation' - db.create_table('ietfworkflows_annotationtagobjectrelation', ( - ('id', orm['ietfworkflows.AnnotationTagObjectRelation:id']), - ('content_type', orm['ietfworkflows.AnnotationTagObjectRelation:content_type']), - ('content_id', orm['ietfworkflows.AnnotationTagObjectRelation:content_id']), - ('annotation_tag', orm['ietfworkflows.AnnotationTagObjectRelation:annotation_tag']), - )) - db.send_create_signal('ietfworkflows', ['AnnotationTagObjectRelation']) - - - - def backwards(self, orm): - - # Deleting model 'WGWorkflow' - db.delete_table('ietfworkflows_wgworkflow') - - # Deleting model 'ObjectWorkflowHistoryEntry' - db.delete_table('ietfworkflows_objectworkflowhistoryentry') - - # Deleting model 'ObjectAnnotationTagHistoryEntry' - db.delete_table('ietfworkflows_objectannotationtaghistoryentry') - - # Deleting model 'AnnotationTag' - db.delete_table('ietfworkflows_annotationtag') - - # Deleting model 'AnnotationTagObjectRelation' - db.delete_table('ietfworkflows_annotationtagobjectrelation') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.wgworkflow': { - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py b/ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py deleted file mode 100644 index 7545559b7..000000000 --- a/ietf/ietfworkflows/migrations/0002_add_selected_states_and_tags.py +++ /dev/null @@ -1,107 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding ManyToManyField 'WGWorkflow.selected_tags' - db.create_table('ietfworkflows_wgworkflow_selected_tags', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('wgworkflow', models.ForeignKey(orm.WGWorkflow, null=False)), - ('annotationtag', models.ForeignKey(orm.AnnotationTag, null=False)) - )) - - # Adding ManyToManyField 'WGWorkflow.selected_states' - db.create_table('ietfworkflows_wgworkflow_selected_states', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('wgworkflow', models.ForeignKey(orm.WGWorkflow, null=False)), - ('state', models.ForeignKey(orm['workflows.State'], null=False)) - )) - - - - def backwards(self, orm): - - # Dropping ManyToManyField 'WGWorkflow.selected_tags' - db.delete_table('ietfworkflows_wgworkflow_selected_tags') - - # Dropping ManyToManyField 'WGWorkflow.selected_states' - db.delete_table('ietfworkflows_wgworkflow_selected_states') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']"}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0003_add_person_to_history.py b/ietf/ietfworkflows/migrations/0003_add_person_to_history.py deleted file mode 100644 index 85f634656..000000000 --- a/ietf/ietfworkflows/migrations/0003_add_person_to_history.py +++ /dev/null @@ -1,119 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding field 'ObjectWorkflowHistoryEntry.person' - db.add_column('ietfworkflows_objectworkflowhistoryentry', 'person', orm['ietfworkflows.objectworkflowhistoryentry:person']) - - # Adding field 'ObjectAnnotationTagHistoryEntry.person' - db.add_column('ietfworkflows_objectannotationtaghistoryentry', 'person', orm['ietfworkflows.objectannotationtaghistoryentry:person']) - - - - def backwards(self, orm): - - # Deleting field 'ObjectWorkflowHistoryEntry.person' - db.delete_column('ietfworkflows_objectworkflowhistoryentry', 'person_id') - - # Deleting field 'ObjectAnnotationTagHistoryEntry.person' - db.delete_column('ietfworkflows_objectannotationtaghistoryentry', 'person_id') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'symmetrical': 'False'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'symmetrical': 'False'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0004_add_object_state_dates.py b/ietf/ietfworkflows/migrations/0004_add_object_state_dates.py deleted file mode 100644 index c71d2a08e..000000000 --- a/ietf/ietfworkflows/migrations/0004_add_object_state_dates.py +++ /dev/null @@ -1,132 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StateObjectRelationMetadata' - db.create_table('ietfworkflows_stateobjectrelationmetadata', ( - ('id', orm['ietfworkflows.stateobjectrelationmetadata:id']), - ('relation', orm['ietfworkflows.stateobjectrelationmetadata:relation']), - ('from_date', orm['ietfworkflows.stateobjectrelationmetadata:from_date']), - ('estimated_date', orm['ietfworkflows.stateobjectrelationmetadata:estimated_date']), - )) - db.send_create_signal('ietfworkflows', ['StateObjectRelationMetadata']) - - - - def backwards(self, orm): - - # Deleting model 'StateObjectRelationMetadata' - db.delete_table('ietfworkflows_stateobjectrelationmetadata') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'symmetrical': 'False'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'symmetrical': 'False'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0005_add_streams.py b/ietf/ietfworkflows/migrations/0005_add_streams.py deleted file mode 100644 index 1e3de388c..000000000 --- a/ietf/ietfworkflows/migrations/0005_add_streams.py +++ /dev/null @@ -1,209 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StreamedID' - db.create_table('ietfworkflows_streamedid', ( - ('id', orm['ietfworkflows.streamedid:id']), - ('draft', orm['ietfworkflows.streamedid:draft']), - ('stream', orm['ietfworkflows.streamedid:stream']), - )) - db.send_create_signal('ietfworkflows', ['StreamedID']) - - # Adding model 'Stream' - db.create_table('ietfworkflows_stream', ( - ('id', orm['ietfworkflows.stream:id']), - ('name', orm['ietfworkflows.stream:name']), - ('with_groups', orm['ietfworkflows.stream:with_groups']), - ('group_model', orm['ietfworkflows.stream:group_model']), - ('group_chair_model', orm['ietfworkflows.stream:group_chair_model']), - ('workflow', orm['ietfworkflows.stream:workflow']), - )) - db.send_create_signal('ietfworkflows', ['Stream']) - - - - def backwards(self, orm): - - # Deleting model 'StreamedID' - db.delete_table('ietfworkflows_streamedid') - - # Deleting model 'Stream' - db.delete_table('ietfworkflows_stream') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py b/ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py deleted file mode 100644 index 9460f0ac6..000000000 --- a/ietf/ietfworkflows/migrations/0006_add_group_to_streamed_id.py +++ /dev/null @@ -1,198 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding field 'StreamedID.content_type' - db.add_column('ietfworkflows_streamedid', 'content_type', orm['ietfworkflows.streamedid:content_type']) - - # Adding field 'StreamedID.content_id' - db.add_column('ietfworkflows_streamedid', 'content_id', orm['ietfworkflows.streamedid:content_id']) - - - - def backwards(self, orm): - - # Deleting field 'StreamedID.content_type' - db.delete_column('ietfworkflows_streamedid', 'content_type_id') - - # Deleting field 'StreamedID.content_id' - db.delete_column('ietfworkflows_streamedid', 'content_id') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0007_do_stream_optional.py b/ietf/ietfworkflows/migrations/0007_do_stream_optional.py deleted file mode 100644 index bcb758aab..000000000 --- a/ietf/ietfworkflows/migrations/0007_do_stream_optional.py +++ /dev/null @@ -1,194 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Changing field 'StreamedID.stream' - # (to signature: django.db.models.fields.related.ForeignKey(to=orm['ietfworkflows.Stream'], null=True, blank=True)) - db.alter_column('ietfworkflows_streamedid', 'stream_id', orm['ietfworkflows.streamedid:stream']) - - - - def backwards(self, orm): - - # Changing field 'StreamedID.stream' - # (to signature: django.db.models.fields.related.ForeignKey(to=orm['ietfworkflows.Stream'])) - db.alter_column('ietfworkflows_streamedid', 'stream_id', orm['ietfworkflows.streamedid:stream']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'change_date': ('django.db.models.fields.DateTimeField', [], {}), - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transition_date': ('django.db.models.fields.DateTimeField', [], {}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0008_refactor_history_entries.py b/ietf/ietfworkflows/migrations/0008_refactor_history_entries.py deleted file mode 100644 index 40bc6f36d..000000000 --- a/ietf/ietfworkflows/migrations/0008_refactor_history_entries.py +++ /dev/null @@ -1,267 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Deleting model 'objectworkflowhistoryentry' - db.delete_table('ietfworkflows_objectworkflowhistoryentry') - - # Deleting model 'objectannotationtaghistoryentry' - db.delete_table('ietfworkflows_objectannotationtaghistoryentry') - - # Adding model 'ObjectAnnotationTagHistoryEntry' - db.create_table('ietfworkflows_objectannotationtaghistoryentry', ( - ('objecthistoryentry_ptr', orm['ietfworkflows.objectannotationtaghistoryentry:objecthistoryentry_ptr']), - ('setted', orm['ietfworkflows.objectannotationtaghistoryentry:setted']), - ('unsetted', orm['ietfworkflows.objectannotationtaghistoryentry:unsetted']), - )) - db.send_create_signal('ietfworkflows', ['ObjectAnnotationTagHistoryEntry']) - - # Adding model 'ObjectHistoryEntry' - db.create_table('ietfworkflows_objecthistoryentry', ( - ('id', orm['ietfworkflows.objecthistoryentry:id']), - ('content_type', orm['ietfworkflows.objecthistoryentry:content_type']), - ('content_id', orm['ietfworkflows.objecthistoryentry:content_id']), - ('date', orm['ietfworkflows.objecthistoryentry:date']), - ('comment', orm['ietfworkflows.objecthistoryentry:comment']), - ('person', orm['ietfworkflows.objecthistoryentry:person']), - )) - db.send_create_signal('ietfworkflows', ['ObjectHistoryEntry']) - - # Adding model 'ObjectStreamHistoryEntry' - db.create_table('ietfworkflows_objectstreamhistoryentry', ( - ('objecthistoryentry_ptr', orm['ietfworkflows.objectstreamhistoryentry:objecthistoryentry_ptr']), - ('from_stream', orm['ietfworkflows.objectstreamhistoryentry:from_stream']), - ('to_stream', orm['ietfworkflows.objectstreamhistoryentry:to_stream']), - )) - db.send_create_signal('ietfworkflows', ['ObjectStreamHistoryEntry']) - - # Adding model 'ObjectWorkflowHistoryEntry' - db.create_table('ietfworkflows_objectworkflowhistoryentry', ( - ('objecthistoryentry_ptr', orm['ietfworkflows.objectworkflowhistoryentry:objecthistoryentry_ptr']), - ('from_state', orm['ietfworkflows.objectworkflowhistoryentry:from_state']), - ('to_state', orm['ietfworkflows.objectworkflowhistoryentry:to_state']), - )) - db.send_create_signal('ietfworkflows', ['ObjectWorkflowHistoryEntry']) - - - def backwards(self, orm): - - # Deleting model 'ObjectAnnotationTagHistoryEntry' - db.delete_table('ietfworkflows_objectannotationtaghistoryentry') - - # Deleting model 'ObjectHistoryEntry' - db.delete_table('ietfworkflows_objecthistoryentry') - - # Deleting model 'ObjectStreamHistoryEntry' - db.delete_table('ietfworkflows_objectstreamhistoryentry') - - # Deleting model 'ObjectWorkflowHistoryEntry' - db.delete_table('ietfworkflows_objectworkflowhistoryentry') - - # Adding model 'objectworkflowhistoryentry' - db.create_table('ietfworkflows_objectworkflowhistoryentry', ( - ('comment', orm['ietfworkflows.objectworkflowhistoryentry:comment']), - ('from_state', orm['ietfworkflows.objectworkflowhistoryentry:from_state']), - ('to_state', orm['ietfworkflows.objectworkflowhistoryentry:to_state']), - ('content_type', orm['ietfworkflows.objectworkflowhistoryentry:content_type']), - ('person', orm['ietfworkflows.objectworkflowhistoryentry:person']), - ('content_id', orm['ietfworkflows.objectworkflowhistoryentry:content_id']), - ('id', orm['ietfworkflows.objectworkflowhistoryentry:id']), - ('transition_date', orm['ietfworkflows.objectworkflowhistoryentry:transition_date']), - )) - db.send_create_signal('ietfworkflows', ['objectworkflowhistoryentry']) - - # Adding model 'objectannotationtaghistoryentry' - db.create_table('ietfworkflows_objectannotationtaghistoryentry', ( - ('comment', orm['ietfworkflows.objectannotationtaghistoryentry:comment']), - ('person', orm['ietfworkflows.objectannotationtaghistoryentry:person']), - ('unsetted', orm['ietfworkflows.objectannotationtaghistoryentry:unsetted']), - ('content_type', orm['ietfworkflows.objectannotationtaghistoryentry:content_type']), - ('change_date', orm['ietfworkflows.objectannotationtaghistoryentry:change_date']), - ('setted', orm['ietfworkflows.objectannotationtaghistoryentry:setted']), - ('content_id', orm['ietfworkflows.objectannotationtaghistoryentry:content_id']), - ('id', orm['ietfworkflows.objectannotationtaghistoryentry:id']), - )) - db.send_create_signal('ietfworkflows', ['objectannotationtaghistoryentry']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py b/ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py deleted file mode 100644 index b2c19c982..000000000 --- a/ietf/ietfworkflows/migrations/0009_allow_null_in_from_date.py +++ /dev/null @@ -1,197 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Changing field 'StateObjectRelationMetadata.from_date' - # (to signature: django.db.models.fields.DateTimeField(null=True, blank=True)) - db.alter_column('ietfworkflows_stateobjectrelationmetadata', 'from_date', orm['ietfworkflows.stateobjectrelationmetadata:from_date']) - - - - def backwards(self, orm): - - # Changing field 'StateObjectRelationMetadata.from_date' - # (to signature: django.db.models.fields.DateTimeField()) - db.alter_column('ietfworkflows_stateobjectrelationmetadata', 'from_date', orm['ietfworkflows.stateobjectrelationmetadata:from_date']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0010_add_state_definitions.py b/ietf/ietfworkflows/migrations/0010_add_state_definitions.py deleted file mode 100644 index 503943c56..000000000 --- a/ietf/ietfworkflows/migrations/0010_add_state_definitions.py +++ /dev/null @@ -1,207 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StateDescription' - db.create_table('ietfworkflows_statedescription', ( - ('id', orm['ietfworkflows.statedescription:id']), - ('state', orm['ietfworkflows.statedescription:state']), - ('definition', orm['ietfworkflows.statedescription:definition']), - ('order', orm['ietfworkflows.statedescription:order']), - )) - db.send_create_signal('ietfworkflows', ['StateDescription']) - - - - def backwards(self, orm): - - # Deleting model 'StateDescription' - db.delete_table('ietfworkflows_statedescription') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'streamed_id'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py b/ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py deleted file mode 100644 index 5705f7ef8..000000000 --- a/ietf/ietfworkflows/migrations/0011_remove_group_from_stream.py +++ /dev/null @@ -1,205 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Deleting field 'StreamedID.content_type' - db.delete_column('ietfworkflows_streamedid', 'content_type_id') - - # Deleting field 'StreamedID.content_id' - db.delete_column('ietfworkflows_streamedid', 'content_id') - - - - def backwards(self, orm): - - # Adding field 'StreamedID.content_type' - db.add_column('ietfworkflows_streamedid', 'content_type', orm['ietfworkflows.streamedid:content_type']) - - # Adding field 'StreamedID.content_id' - db.add_column('ietfworkflows_streamedid', 'content_id', orm['ietfworkflows.streamedid:content_id']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'group_chair_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'group_model': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'with_groups': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py b/ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py deleted file mode 100644 index e5a61fcbd..000000000 --- a/ietf/ietfworkflows/migrations/0012_refactor_stream_group_descrition.py +++ /dev/null @@ -1,222 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding field 'Stream.group_chair_attribute' - db.add_column('ietfworkflows_stream', 'group_chair_attribute', orm['ietfworkflows.stream:group_chair_attribute']) - - # Adding field 'Stream.document_group_attribute' - db.add_column('ietfworkflows_stream', 'document_group_attribute', orm['ietfworkflows.stream:document_group_attribute']) - - # Deleting field 'Stream.group_chair_model' - db.delete_column('ietfworkflows_stream', 'group_chair_model') - - # Deleting field 'Stream.with_groups' - db.delete_column('ietfworkflows_stream', 'with_groups') - - # Deleting field 'Stream.group_model' - db.delete_column('ietfworkflows_stream', 'group_model') - - - - def backwards(self, orm): - - # Deleting field 'Stream.group_chair_attribute' - db.delete_column('ietfworkflows_stream', 'group_chair_attribute') - - # Deleting field 'Stream.document_group_attribute' - db.delete_column('ietfworkflows_stream', 'document_group_attribute') - - # Adding field 'Stream.group_chair_model' - db.add_column('ietfworkflows_stream', 'group_chair_model', orm['ietfworkflows.stream:group_chair_model']) - - # Adding field 'Stream.with_groups' - db.add_column('ietfworkflows_stream', 'with_groups', orm['ietfworkflows.stream:with_groups']) - - # Adding field 'Stream.group_model' - db.add_column('ietfworkflows_stream', 'group_model', orm['ietfworkflows.stream:group_model']) - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'document_group_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'group_chair_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0013_add_stream_delegates.py b/ietf/ietfworkflows/migrations/0013_add_stream_delegates.py deleted file mode 100644 index 2647539a7..000000000 --- a/ietf/ietfworkflows/migrations/0013_add_stream_delegates.py +++ /dev/null @@ -1,208 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'StreamDelegate' - db.create_table('ietfworkflows_streamdelegate', ( - ('id', orm['ietfworkflows.streamdelegate:id']), - ('stream', orm['ietfworkflows.streamdelegate:stream']), - ('person', orm['ietfworkflows.streamdelegate:person']), - )) - db.send_create_signal('ietfworkflows', ['StreamDelegate']) - - - - def backwards(self, orm): - - # Deleting model 'StreamDelegate' - db.delete_table('ietfworkflows_streamdelegate') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'document_group_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'group_chair_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - } - } - - complete_apps = ['ietfworkflows'] diff --git a/ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py b/ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py deleted file mode 100644 index a6ef0232e..000000000 --- a/ietf/ietfworkflows/migrations/0014_change_alt_streams_states.py +++ /dev/null @@ -1,234 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.ietfworkflows.models import * - -class Migration: - - no_dry_run = True - - def forwards(self, orm): - # Remove all 'Published RFC' status - for state in orm['workflows.state'].objects.filter(name='Published RFC'): - state.delete() - - - def backwards(self, orm): - "Write your backwards migration here" - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.PersonOrOrgInfo']"], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.annotationtag': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'annotation_tags'", 'to': "orm['workflows.Workflow']"}) - }, - 'ietfworkflows.annotationtagobjectrelation': { - 'annotation_tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.AnnotationTag']"}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'annotation_tags'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'ietfworkflows.objectannotationtaghistoryentry': { - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'setted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'unsetted': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objecthistoryentry': { - 'comment': ('django.db.models.fields.TextField', [], {}), - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_history'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}) - }, - 'ietfworkflows.objectstreamhistoryentry': { - 'from_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_stream': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.objectworkflowhistoryentry': { - 'from_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'objecthistoryentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['ietfworkflows.ObjectHistoryEntry']", 'unique': 'True', 'primary_key': 'True'}), - 'to_state': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'ietfworkflows.statedescription': { - 'definition': ('django.db.models.fields.TextField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'order': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'ietfworkflows.stateobjectrelationmetadata': { - 'estimated_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'from_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'relation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.StateObjectRelation']"}) - }, - 'ietfworkflows.stream': { - 'document_group_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'group_chair_attribute': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.WGWorkflow']"}) - }, - 'ietfworkflows.streamdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']"}) - }, - 'ietfworkflows.streamedid': { - 'draft': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.InternetDraft']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'stream': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['ietfworkflows.Stream']", 'null': 'True', 'blank': 'True'}) - }, - 'ietfworkflows.wgworkflow': { - 'selected_states': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.State']", 'null': 'True', 'blank': 'True'}), - 'selected_tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['ietfworkflows.AnnotationTag']", 'null': 'True', 'blank': 'True'}), - 'workflow_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['workflows.Workflow']", 'unique': 'True', 'primary_key': 'True'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'permissions.role': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateinheritanceblock': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.statepermissionrelation': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - }, - 'workflows.workflowmodelrelation': { - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wmrs'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wors'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowpermissionrelation': { - 'Meta': {'unique_together': "(('workflow', 'permission'),)"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': "orm['permissions.Permission']"}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"}) - } - } - - complete_apps = ['ietfworkflows', 'workflows'] diff --git a/ietf/ietfworkflows/migrations/__init__.py b/ietf/ietfworkflows/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/ietfworkflows/models.py b/ietf/ietfworkflows/models.py deleted file mode 100644 index 5a455a6b3..000000000 --- a/ietf/ietfworkflows/models.py +++ /dev/null @@ -1,268 +0,0 @@ -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.db import models -from django.utils.translation import ugettext_lazy as _ -from django.conf import settings - -from ietf.idtracker.models import PersonOrOrgInfo, InternetDraft, Role, IRTF -from ietf.utils.admin import admin_link -from workflows.models import Workflow, State, StateObjectRelation -from permissions.models import Permission - - -class ObjectHistoryEntry(models.Model): - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="workflow_history", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - date = models.DateTimeField(_('Date'), auto_now_add=True) - comment = models.TextField(_('Comment')) - person = models.ForeignKey(PersonOrOrgInfo) - - class Meta: - ordering = ('-date', ) - - def get_real_instance(self): - if hasattr(self, '_real_instance'): - return self._real_instance - for i in ('objectworkflowhistoryentry', 'objectannotationtaghistoryentry', 'objectstreamhistoryentry'): - try: - real_instance = getattr(self, i, None) - if real_instance: - self._real_instance = real_instance - return real_instance - except models.ObjectDoesNotExist: - continue - self._real_instance = self - return self - - -class ObjectWorkflowHistoryEntry(ObjectHistoryEntry): - from_state = models.CharField(_('From state'), max_length=100) - to_state = models.CharField(_('To state'), max_length=100) - - def describe_change(self): - html = '

' - html += 'Changed state %s to %s' % (self.from_state, self.to_state) - html += '

' - return html - - -class ObjectAnnotationTagHistoryEntry(ObjectHistoryEntry): - setted = models.TextField(_('Setted tags'), blank=True, null=True) - unsetted = models.TextField(_('Unsetted tags'), blank=True, null=True) - - def describe_change(self): - html = '' - if self.setted: - html += '

' - html += 'Annotation tags set: ' - html += self.setted - html += '

' - if self.unsetted: - html += '

' - html += 'Annotation tags reset: ' - html += self.unsetted - html += '

' - return html - - -class ObjectStreamHistoryEntry(ObjectHistoryEntry): - from_stream = models.TextField(_('From stream'), blank=True, null=True) - to_stream = models.TextField(_('To stream'), blank=True, null=True) - - def describe_change(self): - html = '

' - html += 'Changed doc from stream %s to %s' % (self.from_stream, self.to_stream) - html += '

' - return html - - -class StateDescription(models.Model): - state = models.ForeignKey(State) - definition = models.TextField() - order = models.PositiveIntegerField() - - class Meta: - ordering = ('order', ) - - def __unicode__(self): - return unicode(self.state) - - -class AnnotationTag(models.Model): - name = models.CharField(_(u"Name"), max_length=100) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="annotation_tags") - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"), blank=True, null=True) - - class Meta: - ordering = ('name', ) - - def __unicode__(self): - return self.name - - -class AnnotationTagObjectRelation(models.Model): - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="annotation_tags", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - annotation_tag = models.ForeignKey(AnnotationTag, verbose_name=_(u"Annotation tag")) - - -class StateObjectRelationMetadata(models.Model): - relation = models.ForeignKey(StateObjectRelation) - from_date = models.DateTimeField(_('Initial date'), blank=True, null=True) - estimated_date = models.DateTimeField(_('Estimated date'), blank=True, null=True) - - -class WGWorkflow(Workflow): - selected_states = models.ManyToManyField(State, blank=True, null=True) - selected_tags = models.ManyToManyField(AnnotationTag, blank=True, null=True) - - class Meta: - verbose_name = 'IETF Workflow' - verbose_name_plural = 'IETF Workflows' - - def get_tags(self): - tags = self.annotation_tags.all() - if tags.count(): - return tags - else: - return self.selected_tags.all() - - def get_states(self): - states = self.states.all() - if states.count(): - return states - else: - return self.selected_states.all() - - -class Stream(models.Model): - name = models.CharField(_(u"Name"), max_length=100) - document_group_attribute = models.CharField(_(u'Document group attribute'), max_length=255, blank=True, null=True) - group_chair_attribute = models.CharField(_(u'Group chair attribute'), max_length=255, blank=True, null=True) - workflow = models.ForeignKey(WGWorkflow) - - def __unicode__(self): - return u'%s stream' % self.name - workflow_link = admin_link('workflow') - - def _irtf_group(self, document): - filename = document.filename.split('-') - if len(filename) > 2 and filename[0] == 'draft' and filename[1] == 'irtf': - try: - return IRTF.objects.get(acronym=filename[2]) - except IRTF.DoesNotExist: - return None - return None - - def _irtf_chairs_for_document(self, document): - group = self._irtf_group(document) - if not group: - return [] - chairs = [i.person for i in group.chairs()] - chairs.append(Role.objects.get(pk=Role.IRTF_CHAIR).person) - return chairs - - def _ietf_delegates_for_document(self, document): - group = self.get_group_for_document(document) - if not group: - return False - return [i.person for i in group.wgdelegate_set.all()] - - def get_group_for_document(self, document): - if hasattr(self, '_%s_group' % self.name.lower()): - return getattr(self, '_%s_group' % self.name.lower())(document) - - if not self.document_group_attribute: - return None - attr = None - obj = document - for attr_name in self.document_group_attribute.split('.'): - attr = getattr(obj, attr_name, None) - if not attr: - return None - if callable(attr): - attr = attr() - obj = attr - return attr - - def get_chairs_for_document(self, document): - if hasattr(self, '_%s_chairs_for_document' % self.name.lower()): - return getattr(self, '_%s_chairs_for_document' % self.name.lower())(document) - - group = self.get_group_for_document(document) - if not group or not self.group_chair_attribute: - return [] - attr = None - obj = group - for attr_name in self.group_chair_attribute.split('.'): - attr = getattr(obj, attr_name, None) - if not attr: - return None - if callable(attr): - attr = attr() - obj = attr - return attr - - def get_delegates_for_document(self, document): - delegates = [] - if hasattr(self, '_%s_delegates_for_document' % self.name.lower()): - delegates = getattr(self, '_%s_delegates_for_document' % self.name.lower())(document) - delegates += [i.person for i in self.streamdelegate_set.all()] - return delegates - - def _ise_chairs_for_document(self, document): - return self._ise_stream_chairs() - - def _ise_stream_chairs(self): - chairs = [] - try: - chairs.append(Role.objects.get(role_name='ISE').person) - except Role.DoesNotExist: - pass - return chairs - - def get_chairs(self): - chairs = [] - if hasattr(self, '_%s_stream_chairs' % self.name.lower()): - chairs += list(getattr(self, '_%s_stream_chairs' % self.name.lower())()) - - role_key = getattr(Role, '%s_CHAIR' % self.name.upper(), None) - if role_key: - try: - chairs.append(Role.objects.get(pk=role_key).person) - except Role.DoesNotExist: - pass - return list(set(chairs)) - - def get_delegates(self): - delegates = [] - if hasattr(self, '_%s_stream_delegates' % self.name.lower()): - delegates += list(getattr(self, '_%s_stream_delegates' % self.name.lower())()) - delegates += [i.person for i in StreamDelegate.objects.filter(stream=self)] - return list(set(delegates)) - - def check_chair(self, person): - return person in self.get_chairs() - - def check_delegate(self, person): - return person in self.get_delegates() - - -class StreamedID(models.Model): - draft = models.OneToOneField(InternetDraft) - stream = models.ForeignKey(Stream, blank=True, null=True) - - def get_group(self): - return self.stream.get_group_for_document(self.draft) - - -class StreamDelegate(models.Model): - stream = models.ForeignKey(Stream) - person = models.ForeignKey(PersonOrOrgInfo) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.name.proxy import StreamProxy as Stream diff --git a/ietf/ietfworkflows/streams.py b/ietf/ietfworkflows/streams.py deleted file mode 100644 index 9540a2825..000000000 --- a/ietf/ietfworkflows/streams.py +++ /dev/null @@ -1,102 +0,0 @@ -from django.db import models -from django.conf import settings -from ietf.idtracker.models import InternetDraft - -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper -from ietf.ietfworkflows.models import StreamedID, Stream - - -def get_streamed_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - class Dummy: pass - o = Dummy() - o.draft = draft - o.stream = super(InternetDraft, draft).stream - o.group = draft.group - o.get_group = lambda: draft.group - return o - - if not draft: - return None - try: - return draft.streamedid - except StreamedID.DoesNotExist: - return None - - -def get_stream_from_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - s = super(InternetDraft, draft).stream - if s: - s.with_groups = s.slug in ["ietf", "irtf"] - return s - - streamedid = get_streamed_draft(draft) - if streamedid: - return streamedid.stream - return False - - -def get_stream_by_name(stream_name): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - - try: - return Stream.objects.get(name=stream_name) - except Stream.DoesNotExist: - return None - - -def get_stream_from_id(stream_id): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - - try: - return Stream.objects.get(id=stream_id) - except Stream.DoesNotExist: - return None - - -def _set_stream_automatically(draft, stream): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - (streamed, created) = StreamedID.objects.get_or_create(draft=draft) - if created: - streamed.stream = stream - streamed.save() - return - - -def get_stream_from_wrapper(idrfc_wrapper): - idwrapper = None - if isinstance(idrfc_wrapper, IdRfcWrapper): - idwrapper = idrfc_wrapper.id - elif isinstance(idrfc_wrapper, IdWrapper): - idwrapper = idrfc_wrapper - if not idwrapper: - return None - draft = idwrapper._draft - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return super(InternetDraft, draft).stream - - stream = get_stream_from_draft(draft) - if stream == False: - stream_id = idwrapper.stream_id() - stream = get_stream_from_id(stream_id) - _set_stream_automatically(draft, stream) - return stream - else: - return stream - return None - - -def set_stream_for_draft(draft, stream): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - raise NotImplementedError - - (streamed, created) = StreamedID.objects.get_or_create(draft=draft) - if streamed.stream != stream: - streamed.stream = stream - streamed.group = None - streamed.save() - return streamed.stream diff --git a/ietf/ietfworkflows/templatetags/.gitignore b/ietf/ietfworkflows/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/ietfworkflows/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/ietfworkflows/templatetags/__init__.py b/ietf/ietfworkflows/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/ietfworkflows/templatetags/ietf_streams.py b/ietf/ietfworkflows/templatetags/ietf_streams.py deleted file mode 100644 index f9e69803f..000000000 --- a/ietf/ietfworkflows/templatetags/ietf_streams.py +++ /dev/null @@ -1,114 +0,0 @@ -from django import template -from django.conf import settings -from django.core.urlresolvers import reverse as urlreverse - -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper -from ietf.ietfworkflows.utils import (get_workflow_for_draft, - get_state_for_draft) -from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document, - can_manage_writeup_of_a_document) -from ietf.ietfworkflows.streams import get_stream_from_wrapper -from ietf.ietfworkflows.models import Stream -from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, - is_chair_of_stream, can_adopt) - -register = template.Library() - - -@register.inclusion_tag('ietfworkflows/stream_state.html', takes_context=True) -def stream_state(context, doc): - data = {} - stream = get_stream_from_wrapper(doc) - data.update({'stream': stream}) - if not stream: - return data - - idwrapper = None - if isinstance(doc, IdRfcWrapper): - idwrapper = doc.id - elif isinstance(doc, IdWrapper): - idwrapper = doc - if not idwrapper: - return data - - draft = getattr(idwrapper, '_draft', None) - if not draft: - return data - - workflow = get_workflow_for_draft(draft) - state = get_state_for_draft(draft) - - data.update({'workflow': workflow, - 'draft': draft, - 'state': state, - 'milestones': draft.groupmilestone_set.filter(state="active") - }) - - return data - - -@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True) -def workflow_history_entry(context, entry): - real_entry = entry.get_real_instance() - return {'entry': real_entry, - 'entry_class': real_entry.__class__.__name__.lower()} - - -@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True) -def edit_actions(context, wrapper): - request = context.get('request', None) - user = request and request.user - if not user: - return {} - idwrapper = None - if isinstance(wrapper, IdRfcWrapper): - idwrapper = wrapper.id - elif isinstance(wrapper, IdWrapper): - idwrapper = wrapper - if not idwrapper: - return None - doc = wrapper - draft = wrapper._draft - - actions = [] - if can_adopt(user, draft): - actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name)))) - - if can_edit_state(user, draft): - actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name)))) - if draft.stream_id in ("iab", "ise", "irtf"): - actions.append(("Request publication", urlreverse('doc_request_publication', kwargs=dict(name=doc.draft_name)))) - - return dict(actions=actions) - - -class StreamListNode(template.Node): - - def __init__(self, user, var_name): - self.user = user - self.var_name = var_name - - def render(self, context): - user = self.user.resolve(context) - streams = [] - for i in Stream.objects.all(): - if "Legacy" in i.name: - continue - if is_chair_of_stream(user, i): - streams.append(i) - context.update({self.var_name: streams}) - return '' - - -@register.tag -def get_user_managed_streams(parser, token): - firstbits = token.contents.split(None, 2) - if len(firstbits) != 3: - raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments") - user = parser.compile_filter(firstbits[1]) - lastbits_reversed = firstbits[2][::-1].split(None, 2) - if lastbits_reversed[1][::-1] != 'as': - raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must" - " be 'as'") - var_name = lastbits_reversed[0][::-1] - return StreamListNode(user, var_name) diff --git a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py b/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py deleted file mode 100644 index 978e99e74..000000000 --- a/ietf/ietfworkflows/templatetags/ietf_streams_redesign.py +++ /dev/null @@ -1,104 +0,0 @@ -from django import template -from django.conf import settings -from django.core.urlresolvers import reverse as urlreverse - -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper, IdWrapper -from ietf.ietfworkflows.utils import (get_workflow_for_draft, - get_state_for_draft) -from ietf.wgchairs.accounts import (can_manage_shepherd_of_a_document, - can_manage_writeup_of_a_document) -from ietf.ietfworkflows.streams import get_stream_from_wrapper -from ietf.ietfworkflows.models import Stream -from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, - is_chair_of_stream, can_adopt) - -register = template.Library() - - -@register.inclusion_tag('ietfworkflows/stream_state_redesign.html', takes_context=True) -def stream_state(context, doc): - data = {} - stream = doc.stream - data.update({'stream': stream}) - if not stream: - return data - - if doc.type.slug != 'draft': - return data - - state = get_state_for_draft(doc) - - data.update({ - 'state': state, - 'doc': doc, - }) - - return data - - -@register.inclusion_tag('ietfworkflows/workflow_history_entry.html', takes_context=True) -def workflow_history_entry(context, entry): - real_entry = entry.get_real_instance() - return {'entry': real_entry, - 'entry_class': real_entry.__class__.__name__.lower()} - - -@register.inclusion_tag('ietfworkflows/edit_actions.html', takes_context=True) -def edit_actions(context, wrapper): - request = context.get('request', None) - user = request and request.user - if not user: - return {} - idwrapper = None - if isinstance(wrapper, IdRfcWrapper): - idwrapper = wrapper.id - elif isinstance(wrapper, IdWrapper): - idwrapper = wrapper - if not idwrapper: - return None - doc = wrapper - draft = wrapper._draft - - actions = [] - if can_adopt(user, draft): - actions.append(("Adopt in WG", urlreverse('edit_adopt', kwargs=dict(name=doc.draft_name)))) - - if can_edit_state(user, draft): - actions.append(("Change stream state", urlreverse('edit_state', kwargs=dict(name=doc.draft_name)))) - - if can_edit_stream(user, draft): - actions.append(("Change stream", urlreverse('edit_stream', kwargs=dict(name=doc.draft_name)))) - - return dict(actions=actions) - - -class StreamListNode(template.Node): - - def __init__(self, user, var_name): - self.user = user - self.var_name = var_name - - def render(self, context): - user = self.user.resolve(context) - streams = [] - for i in Stream.objects.all(): - if "Legacy" in i.name: - continue - if is_chair_of_stream(user, i): - streams.append(i) - context.update({self.var_name: streams}) - return '' - - -@register.tag -def get_user_managed_streams(parser, token): - firstbits = token.contents.split(None, 2) - if len(firstbits) != 3: - raise template.TemplateSyntaxError("'get_user_managed_streams' tag takes three arguments") - user = parser.compile_filter(firstbits[1]) - lastbits_reversed = firstbits[2][::-1].split(None, 2) - if lastbits_reversed[1][::-1] != 'as': - raise template.TemplateSyntaxError("next-to-last argument to 'get_user_managed_stream' tag must" - " be 'as'") - var_name = lastbits_reversed[0][::-1] - return StreamListNode(user, var_name) diff --git a/ietf/ietfworkflows/tests.py b/ietf/ietfworkflows/tests.py deleted file mode 100644 index 522afc907..000000000 --- a/ietf/ietfworkflows/tests.py +++ /dev/null @@ -1,189 +0,0 @@ -import datetime, os, shutil - -from django.conf import settings -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse as urlreverse -from StringIO import StringIO -from pyquery import PyQuery - -from ietf.utils.test_utils import login_testing_unauthorized -from ietf.utils.test_data import make_test_data -from ietf.utils.mail import outbox -from ietf.utils import TestCase - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.person.models import Person, Email - from ietf.group.models import Group, Role - from ietf.doc.models import Document, State - from ietf.doc.utils import * - from ietf.name.models import DocTagName - -class EditStreamInfoTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - - def test_adopt_document(self): - draft = make_test_data() - draft.stream = None - draft.group = Group.objects.get(type="individ") - draft.save() - draft.unset_state("draft-stream-ietf") - - url = urlreverse('edit_adopt', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "marschairman", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[type=submit][value*=adopt]')), 1) - self.assertEquals(len(q('form select[name="group"] option')), 1) # we can only select "mars" - - # adopt in mars WG - mailbox_before = len(outbox) - events_before = draft.docevent_set.count() - r = self.client.post(url, - dict(comment="some comment", - group=Group.objects.get(acronym="mars").pk, - weeks="10")) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.group.acronym, "mars") - self.assertEquals(draft.stream_id, "ietf") - self.assertEquals(draft.docevent_set.count() - events_before, 4) - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) - self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) - self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - - def test_set_tags(self): - draft = make_test_data() - draft.tags = DocTagName.objects.filter(slug="w-expert") - draft.group.unused_tags.add("w-refdoc") - - url = urlreverse('edit_state', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "marschairman", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - # make sure the unused tags are hidden - unused = draft.group.unused_tags.values_list("slug", flat=True) - for t in q("input[name=tags]"): - self.assertTrue(t.attrib["value"] not in unused) - - # set tags - mailbox_before = len(outbox) - events_before = draft.docevent_set.count() - r = self.client.post(url, - dict(comment="some comment", - weeks="10", - tags=["need-aut", "sheph-u"], - only_tags="1", - # unused but needed for validation - new_state=draft.get_state("draft-stream-%s" % draft.stream_id).pk, - )) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.tags.count(), 2) - self.assertEquals(draft.tags.filter(slug="w-expert").count(), 0) - self.assertEquals(draft.tags.filter(slug="need-aut").count(), 1) - self.assertEquals(draft.tags.filter(slug="sheph-u").count(), 1) - self.assertEquals(draft.docevent_set.count() - events_before, 2) - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("tags changed" in outbox[-1]["Subject"].lower()) - self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) - self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - self.assertTrue("plain@example.com" in unicode(outbox[-1])) - - def test_set_state(self): - draft = make_test_data() - - url = urlreverse('edit_state', kwargs=dict(name=draft.name)) - login_testing_unauthorized(self, "marschairman", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - # make sure the unused states are hidden - unused = draft.group.unused_states.values_list("pk", flat=True) - for t in q("select[name=new_state]").find("option[name=tags]"): - self.assertTrue(t.attrib["value"] not in unused) - self.assertEquals(len(q('select[name=new_state]')), 1) - - old_state = draft.get_state("draft-stream-%s" % draft.stream_id ) - new_state = State.objects.get(used=True, type="draft-stream-%s" % draft.stream_id, slug="parked") - self.assertTrue(old_state!=new_state) - mailbox_before = len(outbox) - events_before = draft.docevent_set.count() - - # First make sure cancel doesn't change anything - r = self.client.post(url, - dict(comment="some comment", - weeks="10", - tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))], - new_state=new_state.pk, - cancel="1", - )) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), old_state) - - # Set new state - r = self.client.post(url, - dict(comment="some comment", - weeks="10", - tags=[x.pk for x in draft.tags.filter(slug__in=get_tags_for_stream_id(draft.stream_id))], - new_state=new_state.pk, - )) - self.assertEquals(r.status_code, 302) - - draft = Document.objects.get(pk=draft.pk) - self.assertEquals(draft.get_state("draft-stream-%s" % draft.stream_id), new_state) - self.assertEquals(draft.docevent_set.count() - events_before, 2) - reminder = DocReminder.objects.filter(event__doc=draft, type="stream-s") - self.assertEquals(len(reminder), 1) - due = datetime.datetime.now() + datetime.timedelta(weeks=10) - self.assertTrue(due - datetime.timedelta(days=1) <= reminder[0].due <= due + datetime.timedelta(days=1)) - self.assertEquals(len(outbox), mailbox_before + 1) - self.assertTrue("state changed" in outbox[-1]["Subject"].lower()) - self.assertTrue("wgchairman@ietf.org" in unicode(outbox[-1])) - self.assertTrue("wgdelegate@ietf.org" in unicode(outbox[-1])) - - def test_manage_stream_delegates(self): - make_test_data() - - url = urlreverse('stream_delegates', kwargs=dict(stream_name="IETF")) - login_testing_unauthorized(self, "secretary", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('input[type=submit][value*=Add]')), 1) - - delegate = Email.objects.get(address="plain@example.com") - - # add delegate - r = self.client.post(url, - dict(email=delegate.address)) - self.assertEquals(r.status_code, 200) - - self.assertEquals(Role.objects.filter(group__acronym="ietf", name="delegate", person__email__address=delegate.address).count(), 1) - - # remove delegate again - r = self.client.post(url, - dict(remove_delegate=[delegate.person.pk], - delete="1")) - self.assertEquals(r.status_code, 200) - - self.assertEquals(Role.objects.filter(group__acronym="ietf", name="delegate", person__email__address=delegate.address).count(), 0) - -if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - # the above tests only work with the new schema - del EditStreamInfoTestCase diff --git a/ietf/ietfworkflows/urls.py b/ietf/ietfworkflows/urls.py deleted file mode 100644 index af7a885d4..000000000 --- a/ietf/ietfworkflows/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright The IETF Trust 2008, All Rights Reserved - -from django.conf.urls.defaults import patterns, url -from django.views.generic.simple import redirect_to - -urlpatterns = patterns('ietf.ietfworkflows.views', - url(r'^(?P[^/]+)/history/$', 'stream_history', name='stream_history'), - url(r'^(?P[^/]+)/edit/adopt/$', 'edit_adopt', name='edit_adopt'), - # FIXME: the name edit_state is far too generic - url(r'^(?P[^/]+)/edit/state/$', 'edit_state', name='edit_state'), - url(r'^(?P[^/]+)/edit/stream/$', redirect_to, { 'url': '/doc/%(name)s/edit/info/'}) , - url(r'^delegates/(?P[^/]+)/$', 'stream_delegates', name='stream_delegates'), -) diff --git a/ietf/ietfworkflows/utils.py b/ietf/ietfworkflows/utils.py deleted file mode 100644 index 137abb765..000000000 --- a/ietf/ietfworkflows/utils.py +++ /dev/null @@ -1,471 +0,0 @@ -import copy -import datetime - -from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.core.mail import EmailMessage -from django.template.loader import render_to_string -from django.template.defaultfilters import pluralize - -from workflows.models import State, StateObjectRelation -from workflows.utils import (get_workflow_for_object, set_workflow_for_object, - get_state, set_state) - -from ietf.ietfworkflows.streams import (get_streamed_draft, get_stream_from_draft, - set_stream_for_draft) -from ietf.ietfworkflows.models import (WGWorkflow, AnnotationTagObjectRelation, - AnnotationTag, ObjectAnnotationTagHistoryEntry, - ObjectHistoryEntry, StateObjectRelationMetadata, - ObjectWorkflowHistoryEntry, ObjectStreamHistoryEntry) -from ietf.idtracker.models import InternetDraft -from ietf.utils.mail import send_mail -from ietf.doc.models import Document, DocEvent, save_document_in_history, DocReminder, DocReminderTypeName -from ietf.group.models import Role - -WAITING_WRITEUP = 'WG Consensus: Waiting for Write-Up' -FOLLOWUP_TAG = 'Doc Shepherd Follow-up Underway' - - -def get_default_workflow_for_wg(): - try: - workflow = WGWorkflow.objects.get(name='Default WG Workflow') - return workflow - except WGWorkflow.DoesNotExist: - return None - - -def clone_transition(transition): - new = copy.copy(transition) - new.pk = None - new.save() - - # Reference original initial states - for state in transition.states.all(): - new.states.add(state) - return new - - -def clone_workflow(workflow, name): - new = WGWorkflow.objects.create(name=name, initial_state=workflow.initial_state) - - # Reference default states - for state in workflow.states.all(): - new.selected_states.add(state) - - # Reference default annotation tags - for tag in workflow.annotation_tags.all(): - new.selected_tags.add(tag) - - # Reference cloned transitions - for transition in workflow.transitions.all(): - new.transitions.add(clone_transition(transition)) - return new - - -def get_workflow_for_wg(wg, default=None): - workflow = get_workflow_for_object(wg) - try: - workflow = workflow and workflow.wgworkflow - except WGWorkflow.DoesNotExist: - workflow = None - if not workflow: - if default: - workflow = default - else: - workflow = get_default_workflow_for_wg() - if not workflow: - return None - workflow = clone_workflow(workflow, name='%s workflow' % wg) - set_workflow_for_object(wg, workflow) - return workflow - - -def get_workflow_for_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return True if get_streamed_draft(draft) else None - - workflow = get_workflow_for_object(draft) - try: - workflow = workflow and workflow.wgworkflow - except WGWorkflow.DoesNotExist: - workflow = None - if not workflow: - streamed_draft = get_streamed_draft(draft) - if not streamed_draft or not streamed_draft.stream: - return None - stream = streamed_draft.stream - if stream.document_group_attribute: - group = streamed_draft.get_group() - if not group: - return None - else: - workflow = get_workflow_for_wg(group, streamed_draft.stream.workflow) - else: - workflow = stream.workflow - set_workflow_for_object(draft, workflow) - return workflow - - -def get_workflow_history_for_draft(draft, entry_type=None): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.doc.proxy import ObjectHistoryEntryProxy - return ObjectHistoryEntryProxy.objects.filter(doc=draft).order_by('-time', '-id').select_related('by') - - ctype = ContentType.objects.get_for_model(draft) - filter_param = {'content_type': ctype, - 'content_id': draft.pk} - if entry_type: - filter_param.update({'%s__isnull' % entry_type: False}) - history = ObjectHistoryEntry.objects.filter(**filter_param).\ - select_related('objectworkflowhistoryentry', 'objectannotationtaghistoryentry', - 'objectstreamhistoryentry') - return history - - -def get_annotation_tags_for_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.name.proxy import AnnotationTagObjectRelationProxy - from ietf.doc.utils import get_tags_for_stream_id - return AnnotationTagObjectRelationProxy.objects.filter(document=draft.pk, slug__in=get_tags_for_stream_id(draft.stream_id)) - - ctype = ContentType.objects.get_for_model(draft) - tags = AnnotationTagObjectRelation.objects.filter(content_type=ctype, content_id=draft.pk) - return tags - - -def get_state_for_draft(draft): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return draft.get_state("draft-stream-%s" % draft.stream_id) - return get_state(draft) - - -def get_state_by_name(state_name): - try: - return State.objects.get(used=True, name=state_name) - except State.DoesNotExist: - return None - - -def get_annotation_tag_by_name(tag_name): - try: - return AnnotationTag.objects.get(name=tag_name) - except AnnotationTag.DoesNotExist: - return None - - -def set_tag(obj, tag): - ctype = ContentType.objects.get_for_model(obj) - (relation, created) = AnnotationTagObjectRelation.objects.get_or_create( - content_type=ctype, - content_id=obj.pk, - annotation_tag=tag) - return relation - - -def set_tag_by_name(obj, tag_name): - try: - tag = AnnotationTag.objects.get(name=tag_name) - return set_tag(obj, tag) - except AnnotationTag.DoesNotExist: - return None - - -def reset_tag(obj, tag): - ctype = ContentType.objects.get_for_model(obj) - try: - tag_relation = AnnotationTagObjectRelation.objects.get( - content_type=ctype, - content_id=obj.pk, - annotation_tag=tag) - tag_relation.delete() - return True - except AnnotationTagObjectRelation.DoesNotExist: - return False - - -def reset_tag_by_name(obj, tag_name): - try: - tag = AnnotationTag.objects.get(name=tag_name) - return reset_tag(obj, tag) - except AnnotationTag.DoesNotExist: - return False - - -def set_state_for_draft(draft, state, estimated_date=None): - workflow = get_workflow_for_draft(draft) - if state in workflow.get_states(): - set_state(draft, state) - relation = StateObjectRelation.objects.get( - content_type=ContentType.objects.get_for_model(draft), - content_id=draft.pk) - metadata = StateObjectRelationMetadata.objects.get_or_create(relation=relation)[0] - metadata.from_date = datetime.date.today() - metadata.to_date = estimated_date - metadata.save() - return state - return False - - -def notify_entry(entry, template, extra_notify=[]): - doc = entry.content - wg = doc.group.ietfwg - mail_list = set(['%s <%s>' % i.person.email() for i in wg.wgchair_set.all() if i.person.email()]) - mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in wg.wgdelegate_set.all() if i.person.email()]) - mail_list = mail_list.union(['%s <%s>' % i.person.email() for i in doc.authors.all() if i.person.email()]) - mail_list = mail_list.union(extra_notify) - mail_list = list(mail_list) - - subject = 'Annotation tags have changed for draft %s' % doc - body = render_to_string(template, {'doc': doc, - 'entry': entry, - }) - mail = EmailMessage(subject=subject, - body=body, - to=mail_list, - from_email=settings.DEFAULT_FROM_EMAIL) - # Only send emails if we are not debug mode - if not settings.DEBUG: - mail.send() - return mail - - -def notify_tag_entry(entry, extra_notify=[]): - return notify_entry(entry, 'ietfworkflows/annotation_tags_updated_mail.txt', extra_notify) - - -def notify_state_entry(entry, extra_notify=[]): - return notify_entry(entry, 'ietfworkflows/state_updated_mail.txt', extra_notify) - - -def notify_stream_entry(entry, extra_notify=[]): - return notify_entry(entry, 'ietfworkflows/stream_updated_mail.txt', extra_notify) - -def get_notification_receivers(doc, extra_notify): - persons = set() - res = [] - for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")): - res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address)) - persons.add(r.person) - - for email in doc.authors.all(): - if email.person not in persons: - res.append(email.formatted_email()) - persons.add(email.person) - - for x in extra_notify: - if not x in res: - res.append(x) - - return res - -def get_pubreq_receivers(doc, extra_notify): - res = [u'"IESG Secretary" ', ] - - for r in Role.objects.filter(person=doc.group.ad,name__slug='ad'): - res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address)) - - for x in extra_notify: - if not x in res: - res.append(x) - - return res - -def get_pubreq_cc_receivers(doc): - res = [] - - for r in Role.objects.filter(group=doc.group, name__in=("chair", "delegate")): - res.append(u'"%s" <%s>' % (r.person.plain_name(), r.email.address)) - - return res - -def update_tags(request, obj, comment, person, set_tags=[], reset_tags=[], extra_notify=[], send_email=True): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - doc = Document.objects.get(pk=obj.pk) - save_document_in_history(doc) - - obj.tags.remove(*reset_tags) - obj.tags.add(*set_tags) - - doc.time = datetime.datetime.now() - - e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc) - l = [] - if set_tags: - l.append(u"Annotation tag%s %s set." % (pluralize(set_tags), ", ".join(x.name for x in set_tags))) - if reset_tags: - l.append(u"Annotation tag%s %s cleared." % (pluralize(reset_tags), ", ".join(x.name for x in reset_tags))) - e.desc = " ".join(l) - e.save() - - if send_email: - receivers = get_notification_receivers(doc, extra_notify) - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"Annotations tags changed for draft %s" % doc.name, - 'ietfworkflows/annotation_tags_updated_mail.txt', - dict(doc=doc, - entry=dict(setted=", ".join(x.name for x in set_tags), - unsetted=", ".join(x.name for x in reset_tags), - change_date=doc.time, - person=person, - comment=comment))) - return - - ctype = ContentType.objects.get_for_model(obj) - setted = [] - resetted = [] - for tag in set_tags: - if isinstance(tag, basestring): - if set_tag_by_name(obj, tag): - setted.append(tag) - else: - if set_tag(obj, tag): - setted.append(tag.name) - for tag in reset_tags: - if isinstance(tag, basestring): - if reset_tag_by_name(obj, tag): - resetted.append(tag) - else: - if reset_tag(obj, tag): - resetted.append(tag.name) - entry = ObjectAnnotationTagHistoryEntry.objects.create( - content_type=ctype, - content_id=obj.pk, - setted=','.join(setted), - unsetted=','.join(resetted), - date=datetime.datetime.now(), - comment=comment, - person=person) - notify_tag_entry(entry, extra_notify) - - -def update_state(request, doc, comment, person, to_state, added_tags, removed_tags, estimated_date=None, extra_notify=[]): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - doc = Document.objects.get(pk=doc.pk) - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - from_state = doc.get_state("draft-stream-%s" % doc.stream_id) - doc.set_state(to_state) - - e = DocEvent(type="changed_document", time=doc.time, by=person, doc=doc) - e.desc = u"%s changed to %s from %s" % (to_state.type.label, to_state, from_state) - e.save() - - # reminder - reminder_type = DocReminderTypeName.objects.get(slug="stream-s") - try: - reminder = DocReminder.objects.get(event__doc=doc, type=reminder_type, - active=True) - except DocReminder.DoesNotExist: - reminder = None - - if estimated_date: - if not reminder: - reminder = DocReminder(type=reminder_type) - - reminder.event = e - reminder.due = estimated_date - reminder.active = True - reminder.save() - elif reminder: - reminder.active = False - reminder.save() - - set_tags=", ".join(x.name for x in added_tags) - reset_tags=", ".join(x.name for x in removed_tags) - receivers = get_notification_receivers(doc, extra_notify) - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"State changed for draft %s" % doc.name, - 'ietfworkflows/state_updated_mail.txt', - dict(doc=doc, - entry=dict(from_state=from_state, - to_state=to_state, - transition_date=doc.time, - person=person, - comment=comment, - set_tags=set_tags, - reset_tags=reset_tags))) - - if (to_state.slug=='sub-pub'): - receivers = get_pubreq_receivers(doc, extra_notify) - cc_receivers = get_pubreq_cc_receivers(doc) - - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"Publication has been requested for draft %s" % doc.name, - 'ietfworkflows/state_updated_mail.txt', - dict(doc=doc, - entry=dict(from_state=from_state, - to_state=to_state, - transition_date=doc.time, - person=person, - comment=comment)), cc=cc_receivers) - - return - - ctype = ContentType.objects.get_for_model(doc) - from_state = get_state_for_draft(doc) - to_state = set_state_for_draft(doc, to_state, estimated_date) - if not to_state: - return False - entry = ObjectWorkflowHistoryEntry.objects.create( - content_type=ctype, - content_id=doc.pk, - from_state=from_state and from_state.name or '', - to_state=to_state and to_state.name or '', - date=datetime.datetime.now(), - comment=comment, - person=person) - notify_state_entry(entry, extra_notify) - - -def update_stream(request, doc, comment, person, to_stream, extra_notify=[]): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - doc = Document.objects.get(pk=doc.pk) - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - from_stream = doc.stream - doc.stream = to_stream - doc.save() - - e = DocEvent(type="changed_stream", time=doc.time, by=person, doc=doc) - e.desc = u"Stream changed to %s" % to_stream.name - if from_stream: - e.desc += u"from %s" % from_stream.name - e.save() - - receivers = get_notification_receivers(doc, extra_notify) - send_mail(request, receivers, settings.DEFAULT_FROM_EMAIL, - u"Stream changed for draft %s" % doc.name, - 'ietfworkflows/stream_updated_mail.txt', - dict(doc=doc, - entry=dict(from_stream=from_stream, - to_stream=to_stream, - transition_date=doc.time, - person=person, - comment=comment))) - return - - ctype = ContentType.objects.get_for_model(doc) - from_stream = get_stream_from_draft(doc) - to_stream = set_stream_for_draft(doc, to_stream) - entry = ObjectStreamHistoryEntry.objects.create( - content_type=ctype, - content_id=doc.pk, - from_stream=from_stream and from_stream.name or '', - to_stream=to_stream and to_stream.name or '', - date=datetime.datetime.now(), - comment=comment, - person=person) - notify_stream_entry(entry, extra_notify) - - -def get_full_info_for_draft(draft): - return dict( - streamed=get_streamed_draft(draft), - stream=get_stream_from_draft(draft), - workflow=get_workflow_for_draft(draft), - tags=[i.annotation_tag for i in get_annotation_tags_for_draft(draft)], - state=get_state_for_draft(draft), - shepherd=draft.shepherd if draft.shepherd_id else None, - ) diff --git a/ietf/ietfworkflows/views.py b/ietf/ietfworkflows/views.py deleted file mode 100644 index 75ad58700..000000000 --- a/ietf/ietfworkflows/views.py +++ /dev/null @@ -1,150 +0,0 @@ -from django.http import HttpResponseRedirect, HttpResponseForbidden -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext -from django.conf import settings -from django.core.urlresolvers import reverse as urlreverse - -from ietf.idtracker.models import InternetDraft -from ietf.ietfworkflows.models import Stream, StreamDelegate -from ietf.ietfworkflows.forms import (DraftTagsStateForm, - NoWorkflowStateForm, - StreamDelegatesForm) -from ietf.ietfworkflows.streams import (get_stream_from_draft, - get_streamed_draft) -from ietf.ietfworkflows.utils import (get_workflow_history_for_draft, - get_workflow_for_draft, - get_annotation_tags_for_draft, - get_state_for_draft) -from ietf.ietfworkflows.accounts import (can_edit_state, can_edit_stream, - is_chair_of_stream, can_adopt) -from ietf.doc.utils import get_tags_for_stream_id -from ietf.name.models import DocTagName -from ietf.group.utils import save_group_in_history -from ietf.group.models import Group, Role - - -REDUCED_HISTORY_LEN = 20 - - -def stream_history(request, name): - draft = get_object_or_404(InternetDraft, filename=name) - streamed = get_streamed_draft(draft) - stream = get_stream_from_draft(draft) - workflow = get_workflow_for_draft(draft) - tags = [] - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - used = list(draft.tags.all()) - tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id(draft.stream_id)) - for t in tags: - t.setted = t.slug in used - else: - if workflow: - tags_setted = [i.annotation_tag.pk for i in get_annotation_tags_for_draft(draft)] - for tag in workflow.get_tags(): - tag.setted = tag.pk in tags_setted - tags.append(tag) - state = get_state_for_draft(draft) - history = get_workflow_history_for_draft(draft) - show_more = False - if len(history) > REDUCED_HISTORY_LEN: - show_more = True - - return render_to_response('ietfworkflows/stream_history.html', - {'stream': stream, - 'streamed': streamed, - 'draft': draft, - 'tags': tags, - 'state': state, - 'workflow': workflow, - 'show_more': show_more, - 'history': history[:REDUCED_HISTORY_LEN], - }, - context_instance=RequestContext(request)) - - -def _edit_draft_stream(request, draft, form_class=DraftTagsStateForm): - user = request.user - workflow = get_workflow_for_draft(draft) - if not workflow and form_class == DraftTagsStateForm: - form_class = NoWorkflowStateForm - if request.method == 'POST': - form = form_class(user=user, draft=draft, data=request.POST) - form.request = request - if request.POST.get("cancel",""): - return HttpResponseRedirect(draft.get_absolute_url()) - if form.is_valid(): - form.save() - - # This behavior surprises folks. Let's try running awhile without it. - #if form_class == NoWorkflowStateForm and settings.USE_DB_REDESIGN_PROXY_CLASSES: - # return HttpResponseRedirect(urlreverse('ietf.ietfworkflows.views.edit_state', kwargs={ 'name': draft.filename } )) - - return HttpResponseRedirect(draft.get_absolute_url()) - else: - form = form_class(user=user, draft=draft) - form.request = request - state = get_state_for_draft(draft) - stream = get_stream_from_draft(draft) - history = get_workflow_history_for_draft(draft, 'objectworkflowhistoryentry') - tags = get_annotation_tags_for_draft(draft) - milestones = draft.groupmilestone_set.all() - return render_to_response('ietfworkflows/state_edit.html', - {'draft': draft, - 'state': state, - 'stream': stream, - 'workflow': workflow, - 'history': history, - 'tags': tags, - 'form': form, - 'milestones': milestones, - }, - context_instance=RequestContext(request)) - -# these three views are reusing the same view really, which apart from -# being somewhat obscure means that there are subtle bugs (like the -# title being wrong) - would probably be better to switch to a model -# where each part is edited on its own, we come from an overview page -# anyway, so there's not a big win in putting in a common -# overview/edit page here -def edit_adopt(request, name): - draft = get_object_or_404(InternetDraft, filename=name) - if not can_adopt(request.user, draft): - return HttpResponseForbidden("You don't have permission to access this view") - return _edit_draft_stream(request, draft, NoWorkflowStateForm) - -def edit_state(request, name): - draft = get_object_or_404(InternetDraft, filename=name, stream__isnull=False) - if not can_edit_state(request.user, draft, ): - return HttpResponseForbidden("You don't have permission to access this view") - return _edit_draft_stream(request, draft, DraftTagsStateForm) - - - -def stream_delegates(request, stream_name): - stream = get_object_or_404(Stream, name=stream_name) - if not is_chair_of_stream(request.user, stream): - return HttpResponseForbidden('You have no permission to access this view') - chairs = stream.get_chairs() - form = StreamDelegatesForm(stream=stream) - if request.method == 'POST': - if request.POST.get('delete', False): - pk_list = request.POST.getlist('remove_delegate') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - stream_group = Group.objects.get(acronym=stream.slug) - save_group_in_history(stream_group) - Role.objects.filter(person__in=pk_list, group=stream_group, name="delegate").delete() - else: - StreamDelegate.objects.filter(stream=stream, person__pk__in=pk_list).delete() - else: - form = StreamDelegatesForm(request.POST, stream=stream) - if form.is_valid(): - form.save() - form = StreamDelegatesForm(stream=stream) - delegates = stream.get_delegates() - return render_to_response('ietfworkflows/stream_delegates.html', - {'stream': stream, - 'chairs': chairs, - 'delegates': delegates, - 'form': form, - }, - context_instance=RequestContext(request)) diff --git a/ietf/liaisons/tests.py b/ietf/liaisons/tests.py index c5d3c9f91..bd7da0995 100644 --- a/ietf/liaisons/tests.py +++ b/ietf/liaisons/tests.py @@ -91,10 +91,7 @@ def make_liaison_models(): return l -class LiaisonManagementTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class LiaisonManagementTests(TestCase): def setUp(self): self.liaison_dir = os.path.abspath("tmp-liaison-dir") try: diff --git a/ietf/meeting/tests/view.py b/ietf/meeting/tests/view.py index 000c5efe8..07f2b491b 100644 --- a/ietf/meeting/tests/view.py +++ b/ietf/meeting/tests/view.py @@ -13,12 +13,12 @@ from ietf.meeting.views import edit_agenda class ViewTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = [ 'names.xml', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName - 'meeting83.json', - 'constraint83.json', - 'workinggroups.json', - 'groupgroup.json', - 'person.json', 'users.json' ] + perma_fixtures = [ 'names', # ietf/names/fixtures/names.xml for MeetingTypeName, and TimeSlotTypeName + 'meeting83', + 'constraint83', + 'workinggroups', + 'groupgroup', + 'person', 'users' ] def test_nameOfClueWg(self): clue_session = Session.objects.get(pk=2194) diff --git a/ietf/name/fixtures/names.json b/ietf/name/fixtures/names.json new file mode 100644 index 000000000..fe8b99f58 --- /dev/null +++ b/ietf/name/fixtures/names.json @@ -0,0 +1,3756 @@ +[ + { + "pk": "yes", + "model": "name.ballotpositionname", + "fields": { + "order": 1, + "used": true, + "name": "Yes", + "blocking": false, + "desc": "" + } + }, + { + "pk": "noobj", + "model": "name.ballotpositionname", + "fields": { + "order": 2, + "used": true, + "name": "No Objection", + "blocking": false, + "desc": "" + } + }, + { + "pk": "discuss", + "model": "name.ballotpositionname", + "fields": { + "order": 3, + "used": true, + "name": "Discuss", + "blocking": true, + "desc": "" + } + }, + { + "pk": "block", + "model": "name.ballotpositionname", + "fields": { + "order": 3, + "used": true, + "name": "Block", + "blocking": true, + "desc": "" + } + }, + { + "pk": "abstain", + "model": "name.ballotpositionname", + "fields": { + "order": 4, + "used": true, + "name": "Abstain", + "blocking": false, + "desc": "" + } + }, + { + "pk": "recuse", + "model": "name.ballotpositionname", + "fields": { + "order": 5, + "used": true, + "name": "Recuse", + "blocking": false, + "desc": "" + } + }, + { + "pk": "norecord", + "model": "name.ballotpositionname", + "fields": { + "order": 6, + "used": true, + "name": "No Record", + "blocking": false, + "desc": "" + } + }, + { + "pk": "conflict", + "model": "name.constraintname", + "fields": { + "order": 0, + "penalty": 0, + "used": true, + "name": "Conflicts with", + "desc": "" + } + }, + { + "pk": "conflic2", + "model": "name.constraintname", + "fields": { + "order": 0, + "penalty": 0, + "used": true, + "name": "Conflicts with (secondary)", + "desc": "" + } + }, + { + "pk": "conflic3", + "model": "name.constraintname", + "fields": { + "order": 0, + "penalty": 0, + "used": true, + "name": "Conflicts with (tertiary)", + "desc": "" + } + }, + { + "pk": "rst", + "model": "name.dbtemplatetypename", + "fields": { + "order": 0, + "used": true, + "name": "reStructuredText", + "desc": "" + } + }, + { + "pk": "plain", + "model": "name.dbtemplatetypename", + "fields": { + "order": 0, + "used": true, + "name": "Plain", + "desc": "" + } + }, + { + "pk": "django", + "model": "name.dbtemplatetypename", + "fields": { + "order": 0, + "used": true, + "name": "Django", + "desc": "" + } + }, + { + "pk": "obs", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Obsoleted by", + "used": true, + "name": "Obsoletes", + "desc": "" + } + }, + { + "pk": "updates", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Updated by", + "used": true, + "name": "Updates", + "desc": "" + } + }, + { + "pk": "replaces", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Replaced by", + "used": true, + "name": "Replaces", + "desc": "" + } + }, + { + "pk": "conflrev", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Conflict reviewed by", + "used": true, + "name": "conflict reviews", + "desc": "" + } + }, + { + "pk": "refnorm", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "fixme", + "used": true, + "name": "Normative Reference", + "desc": "Normative Reference" + } + }, + { + "pk": "refold", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "fixme", + "used": true, + "name": "Reference", + "desc": "A reference found in a document which does not have split normative/informative reference sections." + } + }, + { + "pk": "refinfo", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "fixme", + "used": true, + "name": "Informative Reference", + "desc": "Informative Reference" + } + }, + { + "pk": "tops", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Proposed Standard by", + "used": true, + "name": "Moves to Proposed Standard", + "desc": "" + } + }, + { + "pk": "tois", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Internet Standard by", + "used": true, + "name": "Moves to Internet Standard", + "desc": "" + } + }, + { + "pk": "tohist", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Historic by", + "used": true, + "name": "Moves to Historic", + "desc": "" + } + }, + { + "pk": "toinf", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Informational by", + "used": true, + "name": "Moves to Informational", + "desc": "" + } + }, + { + "pk": "tobcp", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to BCP by", + "used": true, + "name": "Moves to BCP", + "desc": "" + } + }, + { + "pk": "toexp", + "model": "name.docrelationshipname", + "fields": { + "order": 0, + "revname": "Moved to Experimental by", + "used": true, + "name": "Moves to Experimental", + "desc": "" + } + }, + { + "pk": "refunk", + "model": "name.docrelationshipname", + "fields": { + "order": 3, + "revname": "fixme", + "used": true, + "name": "Possible Reference", + "desc": "Reference of unknown type, likely found in the text of the document." + } + }, + { + "pk": "stream-s", + "model": "name.docremindertypename", + "fields": { + "order": 0, + "used": true, + "name": "Stream state should change", + "desc": "" + } + }, + { + "pk": "iana-crd", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "IANA coordination", + "desc": "RFC-Editor/IANA Registration Coordination" + } + }, + { + "pk": "ref", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Holding for references", + "desc": "Holding for normative reference" + } + }, + { + "pk": "missref", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Missing references", + "desc": "Awaiting missing normative reference" + } + }, + { + "pk": "errata", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Has errata", + "desc": "" + } + }, + { + "pk": "rfc-rev", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Review by RFC Editor", + "desc": "" + } + }, + { + "pk": "via-rfc", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Via RFC Editor", + "desc": "" + } + }, + { + "pk": "app-min", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Approved in minutes", + "desc": "" + } + }, + { + "pk": "need-sh", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Shepherd Needed", + "desc": "" + } + }, + { + "pk": "w-dep", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Waiting for Dependency on Other Document", + "desc": "" + } + }, + { + "pk": "iesg-com", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "IESG Review Completed", + "desc": "" + } + }, + { + "pk": "iana", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "IANA", + "desc": "The document has IANA actions that are not yet completed." + } + }, + { + "pk": "rev-wg", + "model": "name.doctagname", + "fields": { + "order": 0, + "used": true, + "name": "Revised I-D Needed - Issue raised by WG", + "desc": "" + } + }, + { + "pk": "point", + "model": "name.doctagname", + "fields": { + "order": 1, + "used": true, + "name": "Point Raised - writeup needed", + "desc": "IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the \"Point Raised - Writeup Needed\" state until *ALL* IESG comments that have been raised have been documented." + } + }, + { + "pk": "w-expert", + "model": "name.doctagname", + "fields": { + "order": 1, + "used": true, + "name": "Awaiting Expert Review/Resolution of Issues Raised", + "desc": "" + } + }, + { + "pk": "need-ed", + "model": "name.doctagname", + "fields": { + "order": 1, + "used": true, + "name": "Editor Needed", + "desc": "" + } + }, + { + "pk": "ad-f-up", + "model": "name.doctagname", + "fields": { + "order": 2, + "used": true, + "name": "AD Followup", + "desc": "A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include:\n\n- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG.\n\n- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently.\n\n- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes." + } + }, + { + "pk": "w-extern", + "model": "name.doctagname", + "fields": { + "order": 2, + "used": true, + "name": "Awaiting External Review/Resolution of Issues Raised", + "desc": "" + } + }, + { + "pk": "w-part", + "model": "name.doctagname", + "fields": { + "order": 2, + "used": true, + "name": "Waiting for Partner Feedback", + "desc": "" + } + }, + { + "pk": "extpty", + "model": "name.doctagname", + "fields": { + "order": 3, + "used": true, + "name": "External Party", + "desc": "The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the \"note\" field for more details on who has the action." + } + }, + { + "pk": "w-merge", + "model": "name.doctagname", + "fields": { + "order": 3, + "used": true, + "name": "Awaiting Merge with Other Document", + "desc": "" + } + }, + { + "pk": "w-review", + "model": "name.doctagname", + "fields": { + "order": 3, + "used": true, + "name": "Awaiting Reviews", + "desc": "" + } + }, + { + "pk": "need-aut", + "model": "name.doctagname", + "fields": { + "order": 4, + "used": true, + "name": "Author or Editor Needed", + "desc": "" + } + }, + { + "pk": "sh-f-up", + "model": "name.doctagname", + "fields": { + "order": 4, + "used": true, + "name": "Document Shepherd Followup", + "desc": "" + } + }, + { + "pk": "need-rev", + "model": "name.doctagname", + "fields": { + "order": 5, + "used": true, + "name": "Revised I-D Needed", + "desc": "An updated I-D is needed to address the issues that have been raised." + } + }, + { + "pk": "w-refdoc", + "model": "name.doctagname", + "fields": { + "order": 5, + "used": true, + "name": "Waiting for Referenced Document", + "desc": "" + } + }, + { + "pk": "w-refing", + "model": "name.doctagname", + "fields": { + "order": 6, + "used": true, + "name": "Waiting for Referencing Document", + "desc": "" + } + }, + { + "pk": "rev-wglc", + "model": "name.doctagname", + "fields": { + "order": 7, + "used": true, + "name": "Revised I-D Needed - Issue raised by WGLC", + "desc": "" + } + }, + { + "pk": "rev-ad", + "model": "name.doctagname", + "fields": { + "order": 8, + "used": true, + "name": "Revised I-D Needed - Issue raised by AD", + "desc": "" + } + }, + { + "pk": "rev-iesg", + "model": "name.doctagname", + "fields": { + "order": 9, + "used": true, + "name": "Revised I-D Needed - Issue raised by IESG", + "desc": "" + } + }, + { + "pk": "sheph-u", + "model": "name.doctagname", + "fields": { + "order": 10, + "used": true, + "name": "Doc Shepherd Follow-up Underway", + "desc": "" + } + }, + { + "pk": "other", + "model": "name.doctagname", + "fields": { + "order": 11, + "used": true, + "name": "Other - see Comment Log", + "desc": "" + } + }, + { + "pk": "charter", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Charter", + "desc": "" + } + }, + { + "pk": "agenda", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Agenda", + "desc": "" + } + }, + { + "pk": "minutes", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Minutes", + "desc": "" + } + }, + { + "pk": "slides", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Slides", + "desc": "" + } + }, + { + "pk": "draft", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Draft", + "desc": "" + } + }, + { + "pk": "liai-att", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Liaison Attachment", + "desc": "" + } + }, + { + "pk": "conflrev", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Conflict Review", + "desc": "" + } + }, + { + "pk": "statchg", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": true, + "name": "Status Change", + "desc": "" + } + }, + { + "pk": "shepwrit", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": false, + "name": "Shepherd's writeup", + "desc": "" + } + }, + { + "pk": "liaison", + "model": "name.doctypename", + "fields": { + "order": 0, + "used": false, + "name": "Liaison", + "desc": "" + } + }, + { + "pk": "uploaded", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 1, + "next_states": [ + "auth", + "aut-appr", + "grp-appr", + "manual", + "cancel" + ], + "used": true, + "name": "Uploaded", + "desc": "" + } + }, + { + "pk": "auth", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 2, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Submitter Authentication", + "desc": "" + } + }, + { + "pk": "aut-appr", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 3, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Approval from Previous Version Authors'", + "desc": "" + } + }, + { + "pk": "grp-appr", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 4, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Initial Version Approval", + "desc": "" + } + }, + { + "pk": "manual", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 5, + "next_states": [ + "cancel", + "posted" + ], + "used": true, + "name": "Awaiting Manual Post", + "desc": "" + } + }, + { + "pk": "cancel", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 6, + "next_states": [], + "used": true, + "name": "Cancelled", + "desc": "" + } + }, + { + "pk": "posted", + "model": "name.draftsubmissionstatename", + "fields": { + "order": 7, + "next_states": [], + "used": true, + "name": "Posted", + "desc": "" + } + }, + { + "pk": "comment", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Comment", + "desc": "" + } + }, + { + "pk": "questio", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Questionnaire response", + "desc": "" + } + }, + { + "pk": "nomina", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Nomination", + "desc": "" + } + }, + { + "pk": "junk", + "model": "name.feedbacktype", + "fields": { + "order": 0, + "used": true, + "name": "Junk", + "desc": "" + } + }, + { + "pk": "active", + "model": "name.groupmilestonestatename", + "fields": { + "order": 1, + "used": true, + "name": "Active", + "desc": "" + } + }, + { + "pk": "deleted", + "model": "name.groupmilestonestatename", + "fields": { + "order": 2, + "used": true, + "name": "Deleted", + "desc": "" + } + }, + { + "pk": "review", + "model": "name.groupmilestonestatename", + "fields": { + "order": 3, + "used": true, + "name": "For review", + "desc": "" + } + }, + { + "pk": "charter", + "model": "name.groupmilestonestatename", + "fields": { + "order": 4, + "used": true, + "name": "Chartering/rechartering", + "desc": "" + } + }, + { + "pk": "bof", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "BOF", + "desc": "" + } + }, + { + "pk": "proposed", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Proposed", + "desc": "" + } + }, + { + "pk": "active", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Active", + "desc": "" + } + }, + { + "pk": "dormant", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Dormant", + "desc": "" + } + }, + { + "pk": "conclude", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Concluded", + "desc": "" + } + }, + { + "pk": "unknown", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Unknown", + "desc": "" + } + }, + { + "pk": "abandon", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "Abandonded", + "desc": "Formation of the group (most likely a BoF or Proposed WG) was abandoned" + } + }, + { + "pk": "bof-conc", + "model": "name.groupstatename", + "fields": { + "order": 0, + "used": true, + "name": "BOF Concluded", + "desc": "" + } + }, + { + "pk": "ietf", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "IETF", + "desc": "" + } + }, + { + "pk": "area", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Area", + "desc": "" + } + }, + { + "pk": "ag", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "AG", + "desc": "Area group" + } + }, + { + "pk": "wg", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "WG", + "desc": "Working group" + } + }, + { + "pk": "rg", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "RG", + "desc": "Research group" + } + }, + { + "pk": "team", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Team", + "desc": "" + } + }, + { + "pk": "individ", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Individual", + "desc": "" + } + }, + { + "pk": "sdo", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "SDO", + "desc": "Standards organization" + } + }, + { + "pk": "irtf", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "IRTF", + "desc": "" + } + }, + { + "pk": "rfcedtyp", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "RFC Editor", + "desc": "" + } + }, + { + "pk": "nomcom", + "model": "name.grouptypename", + "fields": { + "order": 0, + "used": true, + "name": "Nomcom", + "desc": "An IETF/IAB Nominating Committee. Use 'SDO' for external nominating committees." + } + }, + { + "pk": "ps", + "model": "name.intendedstdlevelname", + "fields": { + "order": 1, + "used": true, + "name": "Proposed Standard", + "desc": "" + } + }, + { + "pk": "ds", + "model": "name.intendedstdlevelname", + "fields": { + "order": 2, + "used": false, + "name": "Draft Standard", + "desc": "" + } + }, + { + "pk": "std", + "model": "name.intendedstdlevelname", + "fields": { + "order": 3, + "used": true, + "name": "Internet Standard", + "desc": "" + } + }, + { + "pk": "bcp", + "model": "name.intendedstdlevelname", + "fields": { + "order": 4, + "used": true, + "name": "Best Current Practice", + "desc": "" + } + }, + { + "pk": "inf", + "model": "name.intendedstdlevelname", + "fields": { + "order": 5, + "used": true, + "name": "Informational", + "desc": "" + } + }, + { + "pk": "exp", + "model": "name.intendedstdlevelname", + "fields": { + "order": 6, + "used": true, + "name": "Experimental", + "desc": "" + } + }, + { + "pk": "hist", + "model": "name.intendedstdlevelname", + "fields": { + "order": 7, + "used": true, + "name": "Historic", + "desc": "" + } + }, + { + "pk": "action", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 1, + "used": true, + "name": "For action", + "desc": "" + } + }, + { + "pk": "comment", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 2, + "used": true, + "name": "For comment", + "desc": "" + } + }, + { + "pk": "info", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 3, + "used": true, + "name": "For information", + "desc": "" + } + }, + { + "pk": "response", + "model": "name.liaisonstatementpurposename", + "fields": { + "order": 4, + "used": true, + "name": "In response", + "desc": "" + } + }, + { + "pk": "ietf", + "model": "name.meetingtypename", + "fields": { + "order": 0, + "used": true, + "name": "IETF", + "desc": "" + } + }, + { + "pk": "interim", + "model": "name.meetingtypename", + "fields": { + "order": 0, + "used": true, + "name": "Interim", + "desc": "" + } + }, + { + "pk": "pending", + "model": "name.nomineepositionstate", + "fields": { + "order": 0, + "used": true, + "name": "Nominated, pending response", + "desc": "" + } + }, + { + "pk": "accepted", + "model": "name.nomineepositionstate", + "fields": { + "order": 0, + "used": true, + "name": "Accepted", + "desc": "" + } + }, + { + "pk": "declined", + "model": "name.nomineepositionstate", + "fields": { + "order": 0, + "used": true, + "name": "Declined", + "desc": "" + } + }, + { + "pk": "ad", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Area Director", + "desc": "" + } + }, + { + "pk": "pre-ad", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Incoming Area Director", + "desc": "" + } + }, + { + "pk": "chair", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Chair", + "desc": "" + } + }, + { + "pk": "editor", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Editor", + "desc": "" + } + }, + { + "pk": "secr", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Secretary", + "desc": "" + } + }, + { + "pk": "techadv", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Tech Advisor", + "desc": "" + } + }, + { + "pk": "execdir", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Executive Director", + "desc": "" + } + }, + { + "pk": "admdir", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Administrative Director", + "desc": "" + } + }, + { + "pk": "liaiman", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Liaison Manager", + "desc": "" + } + }, + { + "pk": "auth", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Authorized Individual", + "desc": "" + } + }, + { + "pk": "delegate", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Delegate", + "desc": "" + } + }, + { + "pk": "atlarge", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "At Large Member", + "desc": "" + } + }, + { + "pk": "member", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Member", + "desc": "Regular group member in a group that has explicit membership, such as the NomCom" + } + }, + { + "pk": "liaison", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Liaison Member", + "desc": "Liaison group member in a group that has explicit membership, such as the NomCom" + } + }, + { + "pk": "advisor", + "model": "name.rolename", + "fields": { + "order": 0, + "used": true, + "name": "Advisor", + "desc": "Advisor in a group that has explicit membership, such as the NomCom" + } + }, + { + "pk": "schedw", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Waiting for Scheduling", + "desc": "" + } + }, + { + "pk": "apprw", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Waiting for Approval", + "desc": "" + } + }, + { + "pk": "appr", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Approved", + "desc": "" + } + }, + { + "pk": "sched", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Scheduled", + "desc": "" + } + }, + { + "pk": "canceled", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Canceled", + "desc": "" + } + }, + { + "pk": "disappr", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Disapproved", + "desc": "" + } + }, + { + "pk": "notmeet", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Not meeting", + "desc": "" + } + }, + { + "pk": "deleted", + "model": "name.sessionstatusname", + "fields": { + "order": 0, + "used": true, + "name": "Deleted", + "desc": "" + } + }, + { + "pk": "std", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Internet Standard", + "desc": "" + } + }, + { + "pk": "ds", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": false, + "name": "Draft Standard", + "desc": "" + } + }, + { + "pk": "ps", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Proposed Standard", + "desc": "" + } + }, + { + "pk": "inf", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Informational", + "desc": "" + } + }, + { + "pk": "exp", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Experimental", + "desc": "" + } + }, + { + "pk": "bcp", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Best Current Practice", + "desc": "" + } + }, + { + "pk": "hist", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Historic", + "desc": "" + } + }, + { + "pk": "unkn", + "model": "name.stdlevelname", + "fields": { + "order": 0, + "used": true, + "name": "Unknown", + "desc": "" + } + }, + { + "pk": "ietf", + "model": "name.streamname", + "fields": { + "order": 1, + "used": true, + "name": "IETF", + "desc": "IETF stream" + } + }, + { + "pk": "ise", + "model": "name.streamname", + "fields": { + "order": 2, + "used": true, + "name": "ISE", + "desc": "Independent Submission Editor stream" + } + }, + { + "pk": "irtf", + "model": "name.streamname", + "fields": { + "order": 3, + "used": true, + "name": "IRTF", + "desc": "Independent Submission Editor stream" + } + }, + { + "pk": "iab", + "model": "name.streamname", + "fields": { + "order": 4, + "used": true, + "name": "IAB", + "desc": "IAB stream" + } + }, + { + "pk": "legacy", + "model": "name.streamname", + "fields": { + "order": 5, + "used": true, + "name": "Legacy", + "desc": "Legacy stream" + } + }, + { + "pk": "other", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Other", + "desc": "" + } + }, + { + "pk": "session", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Session", + "desc": "" + } + }, + { + "pk": "break", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Break", + "desc": "" + } + }, + { + "pk": "reg", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Registration", + "desc": "" + } + }, + { + "pk": "plenary", + "model": "name.timeslottypename", + "fields": { + "order": 0, + "used": true, + "name": "Plenary", + "desc": "" + } + }, + { + "pk": "draft", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "draft-iesg", + "model": "doc.statetype", + "fields": { + "label": "IESG state" + } + }, + { + "pk": "draft-iana", + "model": "doc.statetype", + "fields": { + "label": "IANA state" + } + }, + { + "pk": "draft-rfceditor", + "model": "doc.statetype", + "fields": { + "label": "RFC Editor state" + } + }, + { + "pk": "draft-stream-ietf", + "model": "doc.statetype", + "fields": { + "label": "IETF WG state" + } + }, + { + "pk": "draft-stream-irtf", + "model": "doc.statetype", + "fields": { + "label": "IRTF state" + } + }, + { + "pk": "draft-stream-ise", + "model": "doc.statetype", + "fields": { + "label": "ISE state" + } + }, + { + "pk": "draft-stream-iab", + "model": "doc.statetype", + "fields": { + "label": "IAB state" + } + }, + { + "pk": "slides", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "minutes", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "agenda", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "liai-att", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "charter", + "model": "doc.statetype", + "fields": { + "label": "State" + } + }, + { + "pk": "conflrev", + "model": "doc.statetype", + "fields": { + "label": "Conflict Review State" + } + }, + { + "pk": "draft-iana-action", + "model": "doc.statetype", + "fields": { + "label": "IANA Action state" + } + }, + { + "pk": "draft-iana-review", + "model": "doc.statetype", + "fields": { + "label": "IANA Review state" + } + }, + { + "pk": "statchg", + "model": "doc.statetype", + "fields": { + "label": "RFC Status Change" + } + }, + { + "pk": 81, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "agenda", + "order": 1, + "desc": "" + } + }, + { + "pk": 82, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "agenda", + "order": 2, + "desc": "" + } + }, + { + "pk": 83, + "model": "doc.state", + "fields": { + "used": true, + "name": "Not currently under review", + "next_states": [], + "slug": "notrev", + "type": "charter", + "order": 0, + "desc": "The proposed charter is not being considered at this time. A proposed charter will remain in this state until an AD moves it to Informal IESG review." + } + }, + { + "pk": 84, + "model": "doc.state", + "fields": { + "used": true, + "name": "Informal IESG review", + "next_states": [], + "slug": "infrev", + "type": "charter", + "order": 0, + "desc": "This is the initial state when an AD proposes a new charter. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned." + } + }, + { + "pk": 85, + "model": "doc.state", + "fields": { + "used": true, + "name": "Internal review", + "next_states": [], + "slug": "intrev", + "type": "charter", + "order": 0, + "desc": "The IESG and IAB are reviewing the early draft of the charter; this is the initial IESG and IAB review. The usual next state is External review if the idea is adopted, or Informal IESG review if the IESG decides the idea needs more work, or Not currently under review is the idea is abandoned" + } + }, + { + "pk": 86, + "model": "doc.state", + "fields": { + "used": true, + "name": "External review", + "next_states": [], + "slug": "extrev", + "type": "charter", + "order": 0, + "desc": "The IETF community and possibly other standards development organizations (SDOs) are reviewing the proposed charter. The usual next state is IESG review, although it might move to Not currently under review is the idea is abandoned during the external review." + } + }, + { + "pk": 87, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG review", + "next_states": [], + "slug": "iesgrev", + "type": "charter", + "order": 0, + "desc": "The IESG is reviewing the discussion from the external review of the proposed charter. The usual next state is Approved, or Not currently under review if the idea is abandoned." + } + }, + { + "pk": 88, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved", + "next_states": [], + "slug": "approved", + "type": "charter", + "order": 0, + "desc": "The charter is approved by the IESG." + } + }, + { + "pk": 90, + "model": "doc.state", + "fields": { + "used": true, + "name": "Needs Shepherd", + "next_states": [ + 91, + 98, + 99 + ], + "slug": "needshep", + "type": "conflrev", + "order": 1, + "desc": "A conflict review has been requested, but a shepherding AD has not yet been assigned" + } + }, + { + "pk": 91, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD Review", + "next_states": [ + 92, + 98, + 99 + ], + "slug": "adrev", + "type": "conflrev", + "order": 2, + "desc": "The sponsoring AD is reviewing the document and preparing a proposed response" + } + }, + { + "pk": 92, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation", + "next_states": [ + 93, + 94, + 95, + 98, + 99 + ], + "slug": "iesgeval", + "type": "conflrev", + "order": 3, + "desc": "The IESG is considering the proposed conflict review response" + } + }, + { + "pk": 93, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation - Defer", + "next_states": [ + 92, + 94, + 95, + 98, + 99 + ], + "slug": "defer", + "type": "conflrev", + "order": 4, + "desc": "The evaluation of the proposed conflict review response has been deferred to the next telechat" + } + }, + { + "pk": 100, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved Request to Not Publish - point raised", + "next_states": [ + 94 + ], + "slug": "appr-reqnopub-pr", + "type": "conflrev", + "order": 5, + "desc": "The IESG has approved the conflict review response (a request to not publish), but a point has been raised that should be cleared before moving to announcement to be sent" + } + }, + { + "pk": 101, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved No Problem - point raised", + "next_states": [ + 95 + ], + "slug": "appr-noprob-pr", + "type": "conflrev", + "order": 6, + "desc": "The IESG has approved the conflict review response, but a point has been raised that should be cleared before proceeding to announcement to be sent" + } + }, + { + "pk": 94, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved Request to Not Publish - announcement to be sent", + "next_states": [ + 96, + 98 + ], + "slug": "appr-reqnopub-pend", + "type": "conflrev", + "order": 7, + "desc": "The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response" + } + }, + { + "pk": 95, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved No Problem - announcement to be sent", + "next_states": [ + 97, + 98 + ], + "slug": "appr-noprob-pend", + "type": "conflrev", + "order": 8, + "desc": "The IESG has approved the conflict review response, but the secretariat has not yet sent the response" + } + }, + { + "pk": 96, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved Request to Not Publish - announcement sent", + "next_states": [ + 96 + ], + "slug": "appr-reqnopub-sent", + "type": "conflrev", + "order": 9, + "desc": "The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester" + } + }, + { + "pk": 97, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved No Problem - announcement sent", + "next_states": [ + 97 + ], + "slug": "appr-noprob-sent", + "type": "conflrev", + "order": 10, + "desc": "The secretariat has delivered the IESG's approved conflict review response to the requester" + } + }, + { + "pk": 98, + "model": "doc.state", + "fields": { + "used": true, + "name": "Withdrawn", + "next_states": [ + 90 + ], + "slug": "withdraw", + "type": "conflrev", + "order": 11, + "desc": "The request for conflict review was withdrawn" + } + }, + { + "pk": 99, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead", + "next_states": [ + 90 + ], + "slug": "dead", + "type": "conflrev", + "order": 12, + "desc": "The conflict review has been abandoned" + } + }, + { + "pk": 1, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "draft", + "order": 1, + "desc": "" + } + }, + { + "pk": 2, + "model": "doc.state", + "fields": { + "used": true, + "name": "Expired", + "next_states": [], + "slug": "expired", + "type": "draft", + "order": 2, + "desc": "" + } + }, + { + "pk": 3, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC", + "next_states": [], + "slug": "rfc", + "type": "draft", + "order": 3, + "desc": "" + } + }, + { + "pk": 4, + "model": "doc.state", + "fields": { + "used": true, + "name": "Replaced", + "next_states": [], + "slug": "repl", + "type": "draft", + "order": 4, + "desc": "" + } + }, + { + "pk": 5, + "model": "doc.state", + "fields": { + "used": true, + "name": "Withdrawn by Submitter", + "next_states": [], + "slug": "auth-rm", + "type": "draft", + "order": 5, + "desc": "" + } + }, + { + "pk": 6, + "model": "doc.state", + "fields": { + "used": true, + "name": "Withdrawn by IETF", + "next_states": [], + "slug": "ietf-rm", + "type": "draft", + "order": 6, + "desc": "" + } + }, + { + "pk": 102, + "model": "doc.state", + "fields": { + "used": true, + "name": "New Document", + "next_states": [], + "slug": "newdoc", + "type": "draft-iana-action", + "order": 1, + "desc": "A new document has been received by IANA, but no actions have been taken" + } + }, + { + "pk": 103, + "model": "doc.state", + "fields": { + "used": true, + "name": "In Progress", + "next_states": [], + "slug": "inprog", + "type": "draft-iana-action", + "order": 2, + "desc": "IANA is currently processing the actions for this document" + } + }, + { + "pk": 104, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on Authors", + "next_states": [], + "slug": "waitauth", + "type": "draft-iana-action", + "order": 3, + "desc": "IANA is waiting on the document's authors to respond" + } + }, + { + "pk": 105, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on ADs", + "next_states": [], + "slug": "waitad", + "type": "draft-iana-action", + "order": 4, + "desc": "IANA is waiting on the IETF Area Directors to respond" + } + }, + { + "pk": 106, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on WGC", + "next_states": [], + "slug": "waitwgc", + "type": "draft-iana-action", + "order": 5, + "desc": "IANA is waiting on the IETF Working Group Chairs to respond" + } + }, + { + "pk": 107, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting on RFC Editor", + "next_states": [], + "slug": "waitrfc", + "type": "draft-iana-action", + "order": 6, + "desc": "IANA has notified the RFC Editor that the actions have been completed" + } + }, + { + "pk": 108, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC-Ed-Ack", + "next_states": [], + "slug": "rfcedack", + "type": "draft-iana-action", + "order": 7, + "desc": "Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed" + } + }, + { + "pk": 109, + "model": "doc.state", + "fields": { + "used": true, + "name": "On Hold", + "next_states": [], + "slug": "onhold", + "type": "draft-iana-action", + "order": 8, + "desc": "IANA has suspended work on the document" + } + }, + { + "pk": 110, + "model": "doc.state", + "fields": { + "used": true, + "name": "No IC", + "next_states": [], + "slug": "noic", + "type": "draft-iana-action", + "order": 9, + "desc": "Request completed. There were no IANA actions for this document" + } + }, + { + "pk": 111, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA - Review Needed", + "next_states": [], + "slug": "need-rev", + "type": "draft-iana-review", + "order": 1, + "desc": "Document has not yet been reviewed by IANA." + } + }, + { + "pk": 112, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA OK - Actions Needed", + "next_states": [], + "slug": "ok-act", + "type": "draft-iana-review", + "order": 2, + "desc": "Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly." + } + }, + { + "pk": 113, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA OK - No Actions Needed", + "next_states": [], + "slug": "ok-noact", + "type": "draft-iana-review", + "order": 3, + "desc": "Document requires no IANA action, and the IANA Considerations section indicates this correctly." + } + }, + { + "pk": 114, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA - Not OK", + "next_states": [], + "slug": "not-ok", + "type": "draft-iana-review", + "order": 4, + "desc": "IANA has issues with the text of the IANA Considerations section of the document." + } + }, + { + "pk": 115, + "model": "doc.state", + "fields": { + "used": true, + "name": "Version Changed - Review Needed", + "next_states": [], + "slug": "changed", + "type": "draft-iana-review", + "order": 5, + "desc": "Document revision has changed after review by IANA." + } + }, + { + "pk": 16, + "model": "doc.state", + "fields": { + "used": true, + "name": "Publication Requested", + "next_states": [ + 13, + 11, + 8 + ], + "slug": "pub-req", + "type": "draft-iesg", + "order": 10, + "desc": "A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested." + } + }, + { + "pk": 13, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD Evaluation", + "next_states": [ + 21, + 14, + 12, + 11 + ], + "slug": "ad-eval", + "type": "draft-iesg", + "order": 11, + "desc": "A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole." + } + }, + { + "pk": 21, + "model": "doc.state", + "fields": { + "used": true, + "name": "Expert Review", + "next_states": [ + 13 + ], + "slug": "review-e", + "type": "draft-iesg", + "order": 12, + "desc": "An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the \"MIB doctors\". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the \"note\" field for specific details on the nature of the review." + } + }, + { + "pk": 14, + "model": "doc.state", + "fields": { + "used": true, + "name": "Last Call Requested", + "next_states": [ + 15 + ], + "slug": "lc-req", + "type": "draft-iesg", + "order": 15, + "desc": "The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet." + } + }, + { + "pk": 15, + "model": "doc.state", + "fields": { + "used": true, + "name": "In Last Call", + "next_states": [ + 19, + 20 + ], + "slug": "lc", + "type": "draft-iesg", + "order": 16, + "desc": "The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks." + } + }, + { + "pk": 19, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for Writeup", + "next_states": [ + 20 + ], + "slug": "writeupw", + "type": "draft-iesg", + "order": 18, + "desc": "Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC." + } + }, + { + "pk": 20, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for AD Go-Ahead", + "next_states": [ + 12 + ], + "slug": "goaheadw", + "type": "draft-iesg", + "order": 19, + "desc": "As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole." + } + }, + { + "pk": 12, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation", + "next_states": [ + 18, + 9, + 22 + ], + "slug": "iesg-eva", + "type": "draft-iesg", + "order": 20, + "desc": "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." + } + }, + { + "pk": 18, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation - Defer", + "next_states": [ + 12 + ], + "slug": "defer", + "type": "draft-iesg", + "order": 21, + "desc": "During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat." + } + }, + { + "pk": 9, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved-announcement to be sent", + "next_states": [ + 10 + ], + "slug": "approved", + "type": "draft-iesg", + "order": 27, + "desc": "The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message." + } + }, + { + "pk": 10, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved-announcement sent", + "next_states": [ + 17 + ], + "slug": "ann", + "type": "draft-iesg", + "order": 30, + "desc": "The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor." + } + }, + { + "pk": 17, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC Ed Queue", + "next_states": [ + 7 + ], + "slug": "rfcqueue", + "type": "draft-iesg", + "order": 31, + "desc": "The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html)." + } + }, + { + "pk": 7, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC Published", + "next_states": [ + 8 + ], + "slug": "pub", + "type": "draft-iesg", + "order": 32, + "desc": "The ID has been published as an RFC." + } + }, + { + "pk": 22, + "model": "doc.state", + "fields": { + "used": true, + "name": "DNP-waiting for AD note", + "next_states": [ + 23 + ], + "slug": "nopubadw", + "type": "draft-iesg", + "order": 33, + "desc": "Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the \"note\" field for more details on who has the action item." + } + }, + { + "pk": 23, + "model": "doc.state", + "fields": { + "used": true, + "name": "DNP-announcement to be sent", + "next_states": [ + 8 + ], + "slug": "nopubanw", + "type": "draft-iesg", + "order": 34, + "desc": "The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official \"do not publish\" recommendation message." + } + }, + { + "pk": 11, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD is watching", + "next_states": [ + 16 + ], + "slug": "watching", + "type": "draft-iesg", + "order": 42, + "desc": "An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and \"I-D Exists\" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons)." + } + }, + { + "pk": 8, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead", + "next_states": [ + 16 + ], + "slug": "dead", + "type": "draft-iesg", + "order": 99, + "desc": "Document is \"dead\" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.)" + } + }, + { + "pk": 24, + "model": "doc.state", + "fields": { + "used": true, + "name": "AUTH", + "next_states": [], + "slug": "auth", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting author action" + } + }, + { + "pk": 25, + "model": "doc.state", + "fields": { + "used": true, + "name": "AUTH48", + "next_states": [], + "slug": "auth48", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting final author approval" + } + }, + { + "pk": 26, + "model": "doc.state", + "fields": { + "used": false, + "name": "EDIT", + "next_states": [], + "slug": "edit", + "type": "draft-rfceditor", + "order": 0, + "desc": "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing" + } + }, + { + "pk": 27, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA", + "next_states": [], + "slug": "iana", + "type": "draft-rfceditor", + "order": 0, + "desc": "Document has been edited, but is holding for completion of IANA actions" + } + }, + { + "pk": 28, + "model": "doc.state", + "fields": { + "used": false, + "name": "IESG", + "next_states": [], + "slug": "iesg", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting IESG action" + } + }, + { + "pk": 29, + "model": "doc.state", + "fields": { + "used": true, + "name": "ISR", + "next_states": [], + "slug": "isr", + "type": "draft-rfceditor", + "order": 0, + "desc": "Independent Submission Review by the ISE " + } + }, + { + "pk": 30, + "model": "doc.state", + "fields": { + "used": false, + "name": "ISR-AUTH", + "next_states": [], + "slug": "isr-auth", + "type": "draft-rfceditor", + "order": 0, + "desc": "Independent submission awaiting author action, or in discussion between author and ISE" + } + }, + { + "pk": 31, + "model": "doc.state", + "fields": { + "used": true, + "name": "REF", + "next_states": [], + "slug": "ref", + "type": "draft-rfceditor", + "order": 0, + "desc": "Holding for normative reference" + } + }, + { + "pk": 32, + "model": "doc.state", + "fields": { + "used": true, + "name": "RFC-EDITOR", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting final RFC Editor review before AUTH48" + } + }, + { + "pk": 33, + "model": "doc.state", + "fields": { + "used": true, + "name": "TO", + "next_states": [], + "slug": "timeout", + "type": "draft-rfceditor", + "order": 0, + "desc": "Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work" + } + }, + { + "pk": 34, + "model": "doc.state", + "fields": { + "used": true, + "name": "MISSREF", + "next_states": [], + "slug": "missref", + "type": "draft-rfceditor", + "order": 0, + "desc": "Awaiting missing normative reference" + } + }, + { + "pk": 89, + "model": "doc.state", + "fields": { + "used": false, + "name": "AUTH48-DONE", + "next_states": [ + 74 + ], + "slug": "auth48done", + "type": "draft-rfceditor", + "order": 0, + "desc": "Final approvals are complete" + } + }, + { + "pk": 116, + "model": "doc.state", + "fields": { + "used": true, + "name": "AUTH48-DONE", + "next_states": [], + "slug": "auth48-done", + "type": "draft-rfceditor", + "order": 0, + "desc": "Final approvals are complete" + } + }, + { + "pk": 117, + "model": "doc.state", + "fields": { + "used": true, + "name": "EDIT", + "next_states": [], + "slug": "edit", + "type": "draft-rfceditor", + "order": 0, + "desc": "Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing" + } + }, + { + "pk": 118, + "model": "doc.state", + "fields": { + "used": true, + "name": "IANA", + "next_states": [], + "slug": "iana-crd", + "type": "draft-rfceditor", + "order": 0, + "desc": "RFC-Editor/IANA Registration Coordination" + } + }, + { + "pk": 119, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG", + "next_states": [], + "slug": "iesg", + "type": "draft-rfceditor", + "order": 0, + "desc": "Holding for IESG action" + } + }, + { + "pk": 120, + "model": "doc.state", + "fields": { + "used": true, + "name": "ISR-AUTH", + "next_states": [], + "slug": "isr-auth", + "type": "draft-rfceditor", + "order": 0, + "desc": "Independent Submission awaiting author update, or in discussion between author and ISE" + } + }, + { + "pk": 133, + "model": "doc.state", + "fields": { + "used": true, + "name": "Pending", + "next_states": [], + "slug": "pending", + "type": "draft-rfceditor", + "order": 0, + "desc": "" + } + }, + { + "pk": 45, + "model": "doc.state", + "fields": { + "used": true, + "name": "Candidate IAB Document", + "next_states": [], + "slug": "candidat", + "type": "draft-stream-iab", + "order": 1, + "desc": "A document being considered for the IAB stream." + } + }, + { + "pk": 46, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active IAB Document", + "next_states": [], + "slug": "active", + "type": "draft-stream-iab", + "order": 2, + "desc": "This document has been adopted by the IAB and is being actively developed." + } + }, + { + "pk": 47, + "model": "doc.state", + "fields": { + "used": true, + "name": "Parked IAB Document", + "next_states": [], + "slug": "parked", + "type": "draft-stream-iab", + "order": 3, + "desc": "This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the IAB for some other reason. Annotations probably explain why this document is parked." + } + }, + { + "pk": 48, + "model": "doc.state", + "fields": { + "used": true, + "name": "IAB Review", + "next_states": [], + "slug": "review-i", + "type": "draft-stream-iab", + "order": 4, + "desc": "This document is awaiting the IAB itself to come to internal consensus." + } + }, + { + "pk": 49, + "model": "doc.state", + "fields": { + "used": true, + "name": "Community Review", + "next_states": [], + "slug": "review-c", + "type": "draft-stream-iab", + "order": 5, + "desc": "This document has completed internal consensus within the IAB and is now under community review." + } + }, + { + "pk": 50, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved by IAB, To Be Sent to RFC Editor", + "next_states": [], + "slug": "approved", + "type": "draft-stream-iab", + "order": 6, + "desc": "The consideration of this document is complete, but it has not yet been sent to the RFC Editor for publication (although that is going to happen soon)." + } + }, + { + "pk": 51, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to a Different Organization for Publication", + "next_states": [], + "slug": "diff-org", + "type": "draft-stream-iab", + "order": 7, + "desc": "The IAB does not expect to publish the document itself, but has passed it on to a different organization that might continue work on the document. The expectation is that the other organization will eventually publish the document." + } + }, + { + "pk": 52, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to the RFC Editor", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-stream-iab", + "order": 8, + "desc": "The IAB processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the IAB." + } + }, + { + "pk": 53, + "model": "doc.state", + "fields": { + "used": true, + "name": "Published RFC", + "next_states": [], + "slug": "pub", + "type": "draft-stream-iab", + "order": 9, + "desc": "The document has been published as an RFC." + } + }, + { + "pk": 54, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead IAB Document", + "next_states": [], + "slug": "dead", + "type": "draft-stream-iab", + "order": 10, + "desc": "This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream." + } + }, + { + "pk": 134, + "model": "doc.state", + "fields": { + "used": true, + "name": "Candidate for WG Adoption", + "next_states": [ + 35 + ], + "slug": "wg-cand", + "type": "draft-stream-ietf", + "order": 0, + "desc": "The document has been marked as a candidate for WG adoption by the WG Chair. This state can be used before a call for adoption is issued (and the document is put in the \"Call For Adoption By WG Issued\" state), to indicate that the document is in the queue for a call for adoption, even if none has been issued yet." + } + }, + { + "pk": 35, + "model": "doc.state", + "fields": { + "used": true, + "name": "Call For Adoption By WG Issued", + "next_states": [ + 36, + 37 + ], + "slug": "c-adopt", + "type": "draft-stream-ietf", + "order": 1, + "desc": "4.2.1. Call for Adoption by WG Issued\n\n The \"Call for Adoption by WG Issued\" state should be used to indicate when an I-D is being considered for adoption by an IETF WG. An I-D that is in this state is actively being considered for adoption and has not yet achieved consensus, preference, or selection in the WG.\n\n This state may be used to describe an I-D that someone has asked a WG to consider for adoption, if the WG Chair has agreed with the request. This state may also be used to identify an I-D that a WG Chair asked an author to write specifically for consideration as a candidate WG item [WGDTSPEC], and/or an I-D that is listed as a 'candidate draft' in the WG's charter.\n\n Under normal conditions, it should not be possible for an I-D to be in the \"Call for Adoption by WG Issued\" state in more than one working group at the same time. This said, it is not uncommon for authors to \"shop\" their I-Ds to more than one WG at a time, with the hope of getting their documents adopted somewhere.\n\n After this state is implemented in the Datatracker, an I-D that is in the \"Call for Adoption by WG Issued\" state will not be able to be \"shopped\" to any other WG without the consent of the WG Chairs and the responsible ADs impacted by the shopping.\n\n Note that Figure 1 includes an arc leading from this state to outside of the WG state machine. This illustrates that some I-Ds that are considered do not get adopted as WG drafts. An I-D that is not adopted as a WG draft will transition out of the WG state machine and revert back to having no stream-specific state; however, the status change history log of the I-D will record that the I-D was previously in the \"Call for Adoption by WG Issued\" state." + } + }, + { + "pk": 36, + "model": "doc.state", + "fields": { + "used": true, + "name": "Adopted by a WG", + "next_states": [ + 38 + ], + "slug": "adopt-wg", + "type": "draft-stream-ietf", + "order": 2, + "desc": "4.2.2. Adopted by a WG\n\n The \"Adopted by a WG\" state describes an individual submission I-D that an IETF WG has agreed to adopt as one of its WG drafts.\n\n WG Chairs who use this state will be able to clearly indicate when their WGs adopt individual submission I-Ds. This will facilitate the Datatracker's ability to correctly capture \"Replaces\" information for WG drafts and correct \"Replaced by\" information for individual submission I-Ds that have been replaced by WG drafts.\n\n This state is needed because the Datatracker uses the filename of an I-D as a key to search its database for status information about the I-D, and because the filename of a WG I-D is supposed to be different from the filename of an individual submission I-D. The filename of an individual submission I-D will typically be formatted as 'draft-author-wgname-topic-nn'.\n\n The filename of a WG document is supposed to be formatted as 'draft- ietf-wgname-topic-nn'.\n\n An individual I-D that is adopted by a WG may take weeks or months to be resubmitted by the author as a new (version-00) WG draft. If the \"Adopted by a WG\" state is not used, the Datatracker has no way to determine that an I-D has been adopted until a new version of the I-D is submitted to the WG by the author and until the I-D is approved for posting by a WG Chair." + } + }, + { + "pk": 37, + "model": "doc.state", + "fields": { + "used": true, + "name": "Adopted for WG Info Only", + "next_states": [], + "slug": "info", + "type": "draft-stream-ietf", + "order": 3, + "desc": "4.2.3. Adopted for WG Info Only\n\n The \"Adopted for WG Info Only\" state describes a document that contains useful information for the WG that adopted it, but the document is not intended to be published as an RFC. The WG will not actively develop the contents of the I-D or progress it for publication as an RFC. The only purpose of the I-D is to provide information for internal use by the WG." + } + }, + { + "pk": 38, + "model": "doc.state", + "fields": { + "used": true, + "name": "WG Document", + "next_states": [ + 39, + 40, + 41, + 43 + ], + "slug": "wg-doc", + "type": "draft-stream-ietf", + "order": 4, + "desc": "4.2.4. WG Document\n\n The \"WG Document\" state describes an I-D that has been adopted by an IETF WG and is being actively developed.\n\n A WG Chair may transition an I-D into the \"WG Document\" state at any time as long as the I-D is not being considered or developed in any other WG.\n\n Alternatively, WG Chairs may rely upon new functionality to be added to the Datatracker to automatically move version-00 drafts into the \"WG Document\" state as described in Section 4.1.\n\n Under normal conditions, it should not be possible for an I-D to be in the \"WG Document\" state in more than one WG at a time. This said, I-Ds may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs." + } + }, + { + "pk": 39, + "model": "doc.state", + "fields": { + "used": true, + "name": "Parked WG Document", + "next_states": [ + 38 + ], + "slug": "parked", + "type": "draft-stream-ietf", + "order": 5, + "desc": "4.2.5. Parked WG Document\n\n A \"Parked WG Document\" is an I-D that has lost its author or editor, is waiting for another document to be written or for a review to be completed, or cannot be progressed by the working group for some other reason.\n\n Some of the annotation tags described in Section 4.3 may be used in conjunction with this state to indicate why an I-D has been parked, and/or what may need to happen for the I-D to be un-parked.\n\n Parking a WG draft will not prevent it from expiring; however, this state can be used to indicate why the I-D has stopped progressing in the WG.\n\n A \"Parked WG Document\" that is not expired may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs." + } + }, + { + "pk": 40, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead WG Document", + "next_states": [ + 38 + ], + "slug": "dead", + "type": "draft-stream-ietf", + "order": 6, + "desc": "4.2.6. Dead WG Document\n\n A \"Dead WG Document\" is an I-D that has been abandoned. Note that 'Dead' is not always a final state for a WG I-D. If consensus is subsequently achieved, a \"Dead WG Document\" may be resurrected. A \"Dead WG Document\" that is not resurrected will eventually expire.\n\n Note that an I-D that is declared to be \"Dead\" in one WG and that is not expired may be transferred to a non-dead state in another WG with the consent of the WG Chairs and the responsible ADs." + } + }, + { + "pk": 41, + "model": "doc.state", + "fields": { + "used": true, + "name": "In WG Last Call", + "next_states": [ + 38, + 42, + 43 + ], + "slug": "wg-lc", + "type": "draft-stream-ietf", + "order": 7, + "desc": "4.2.7. In WG Last Call\n\n A document \"In WG Last Call\" is an I-D for which a WG Last Call (WGLC) has been issued and is in progress.\n\n Note that conducting a WGLC is an optional part of the IETF WG process, per Section 7.4 of RFC 2418 [RFC2418].\n\n If a WG Chair decides to conduct a WGLC on an I-D, the \"In WG Last Call\" state can be used to track the progress of the WGLC. The Chair may configure the Datatracker to send a WGLC message to one or more mailing lists when the Chair moves the I-D into this state. The WG Chair may also be able to select a different set of mailing lists for a different document undergoing a WGLC; some documents may deserve coordination with other WGs.\n\n A WG I-D in this state should remain \"In WG Last Call\" until the WG Chair moves it to another state. The WG Chair may configure the Datatracker to send an e-mail after a specified period of time to remind or 'nudge' the Chair to conclude the WGLC and to determine the next state for the document.\n\n It is possible for one WGLC to lead into another WGLC for the same document. For example, an I-D that completed a WGLC as an \"Informational\" document may need another WGLC if a decision is taken to convert the I-D into a Standards Track document." + } + }, + { + "pk": 42, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for WG Chair Go-Ahead", + "next_states": [ + 41, + 43 + ], + "slug": "chair-w", + "type": "draft-stream-ietf", + "order": 8, + "desc": "4.2.8. Waiting for WG Chair Go-Ahead\n\n A WG Chair may wish to place an I-D that receives a lot of comments during a WGLC into the \"Waiting for WG Chair Go-Ahead\" state. This state describes an I-D that has undergone a WGLC; however, the Chair is not yet ready to call consensus on the document.\n\n If comments from the WGLC need to be responded to, or a revision to the I-D is needed, the Chair may place an I-D into this state until all of the WGLC comments are adequately addressed and the (possibly revised) document is in the I-D repository." + } + }, + { + "pk": 43, + "model": "doc.state", + "fields": { + "used": true, + "name": "WG Consensus: Waiting for Write-Up", + "next_states": [ + 44 + ], + "slug": "writeupw", + "type": "draft-stream-ietf", + "order": 9, + "desc": "4.2.9. WG Consensus: Waiting for Writeup\n\n A document in the \"WG Consensus: Waiting for Writeup\" state has essentially completed its development within the working group, and is nearly ready to be sent to the IESG for publication. The last thing to be done is the preparation of a protocol writeup by a Document Shepherd. The IESG requires that a document shepherd writeup be completed before publication of the I-D is requested. The IETF document shepherding process and the role of a WG Document Shepherd is described in RFC 4858 [RFC4858]\n\n A WG Chair may call consensus on an I-D without a formal WGLC and transition an I-D that was in the \"WG Document\" state directly into this state.\n\n The name of this state includes the words \"Waiting for Writeup\" because a good document shepherd writeup takes time to prepare." + } + }, + { + "pk": 44, + "model": "doc.state", + "fields": { + "used": true, + "name": "Submitted to IESG for Publication", + "next_states": [ + 38 + ], + "slug": "sub-pub", + "type": "draft-stream-ietf", + "order": 10, + "desc": "4.2.10. Submitted to IESG for Publication\n\n This state describes a WG document that has been submitted to the IESG for publication and that has not been sent back to the working group for revision.\n\n An I-D in this state may be under review by the IESG, it may have been approved and be in the RFC Editor's queue, or it may have been published as an RFC. Other possibilities exist too. The document may be \"Dead\" (in the IESG state machine) or in a \"Do Not Publish\" state." + } + }, + { + "pk": 55, + "model": "doc.state", + "fields": { + "used": true, + "name": "Candidate RG Document", + "next_states": [], + "slug": "candidat", + "type": "draft-stream-irtf", + "order": 1, + "desc": "This document is under consideration in an RG for becoming an IRTF document. A document in this state does not imply any RG consensus and does not imply any precedence or selection. It's simply a way to indicate that somebody has asked for a document to be considered for adoption by an RG." + } + }, + { + "pk": 56, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active RG Document", + "next_states": [], + "slug": "active", + "type": "draft-stream-irtf", + "order": 2, + "desc": "This document has been adopted by the RG and is being actively developed." + } + }, + { + "pk": 57, + "model": "doc.state", + "fields": { + "used": true, + "name": "Parked RG Document", + "next_states": [], + "slug": "parked", + "type": "draft-stream-irtf", + "order": 3, + "desc": "This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the RG for some other reason." + } + }, + { + "pk": 58, + "model": "doc.state", + "fields": { + "used": true, + "name": "In RG Last Call", + "next_states": [], + "slug": "rg-lc", + "type": "draft-stream-irtf", + "order": 4, + "desc": "The document is in its final review in the RG." + } + }, + { + "pk": 59, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for Document Shepherd", + "next_states": [], + "slug": "sheph-w", + "type": "draft-stream-irtf", + "order": 5, + "desc": "IRTF documents have document shepherds who help RG documents through the process after the RG has finished with the document." + } + }, + { + "pk": 60, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for IRTF Chair", + "next_states": [], + "slug": "chair-w", + "type": "draft-stream-irtf", + "order": 6, + "desc": "The IRTF Chair is meant to be performing some task such as sending a request for IESG Review." + } + }, + { + "pk": 61, + "model": "doc.state", + "fields": { + "used": true, + "name": "Awaiting IRSG Reviews", + "next_states": [], + "slug": "irsg-w", + "type": "draft-stream-irtf", + "order": 7, + "desc": "The document shepherd has taken the document to the IRSG and solicited reviews from one or more IRSG members." + } + }, + { + "pk": 62, + "model": "doc.state", + "fields": { + "used": true, + "name": "In IRSG Poll", + "next_states": [], + "slug": "irsgpoll", + "type": "draft-stream-irtf", + "order": 8, + "desc": "The IRSG is taking a poll on whether or not the document is ready to be published." + } + }, + { + "pk": 63, + "model": "doc.state", + "fields": { + "used": true, + "name": "In IESG Review", + "next_states": [], + "slug": "iesg-rev", + "type": "draft-stream-irtf", + "order": 9, + "desc": "The IRSG has asked the IESG to do a review of the document, as described in RFC5742." + } + }, + { + "pk": 64, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to the RFC Editor", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-stream-irtf", + "order": 10, + "desc": "The RG processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the RG." + } + }, + { + "pk": 65, + "model": "doc.state", + "fields": { + "used": true, + "name": "Published RFC", + "next_states": [], + "slug": "pub", + "type": "draft-stream-irtf", + "order": 11, + "desc": "The document has been published as an RFC." + } + }, + { + "pk": 66, + "model": "doc.state", + "fields": { + "used": true, + "name": "Document on Hold Based On IESG Request", + "next_states": [], + "slug": "iesghold", + "type": "draft-stream-irtf", + "order": 12, + "desc": "The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the IRTF has agreed to such a hold." + } + }, + { + "pk": 67, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead IRTF Document", + "next_states": [], + "slug": "dead", + "type": "draft-stream-irtf", + "order": 13, + "desc": "This document was an active IRTF document, but for some reason it is no longer being pursued for the IRTF stream. It is possible that the document might be revived later, possibly in another stream." + } + }, + { + "pk": 68, + "model": "doc.state", + "fields": { + "used": true, + "name": "Submission Received", + "next_states": [], + "slug": "receive", + "type": "draft-stream-ise", + "order": 1, + "desc": "The draft has been sent to the ISE with a request for publication." + } + }, + { + "pk": 69, + "model": "doc.state", + "fields": { + "used": true, + "name": "Finding Reviewers", + "next_states": [], + "slug": "find-rev", + "type": "draft-stream-ise", + "order": 2, + "desc": " The ISE is finding initial reviewers for the document." + } + }, + { + "pk": 70, + "model": "doc.state", + "fields": { + "used": true, + "name": "In ISE Review", + "next_states": [], + "slug": "ise-rev", + "type": "draft-stream-ise", + "order": 3, + "desc": "The ISE is actively working on the document." + } + }, + { + "pk": 71, + "model": "doc.state", + "fields": { + "used": true, + "name": "Response to Review Needed", + "next_states": [], + "slug": "need-res", + "type": "draft-stream-ise", + "order": 4, + "desc": " One or more reviews have been sent to the author, and the ISE is awaiting response." + } + }, + { + "pk": 72, + "model": "doc.state", + "fields": { + "used": true, + "name": "In IESG Review", + "next_states": [], + "slug": "iesg-rev", + "type": "draft-stream-ise", + "order": 5, + "desc": "The ISE has asked the IESG to do a review of the document, as described in RFC5742." + } + }, + { + "pk": 73, + "model": "doc.state", + "fields": { + "used": true, + "name": "Sent to the RFC Editor", + "next_states": [], + "slug": "rfc-edit", + "type": "draft-stream-ise", + "order": 6, + "desc": "The ISE processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the ISE." + } + }, + { + "pk": 74, + "model": "doc.state", + "fields": { + "used": true, + "name": "Published RFC", + "next_states": [], + "slug": "pub", + "type": "draft-stream-ise", + "order": 7, + "desc": "The document has been published as an RFC." + } + }, + { + "pk": 75, + "model": "doc.state", + "fields": { + "used": true, + "name": "No Longer In Independent Submission Stream", + "next_states": [], + "slug": "dead", + "type": "draft-stream-ise", + "order": 8, + "desc": "This document was actively considered in the Independent Submission stream, but the ISE chose not to publish it. It is possible that the document might be revived later. A document in this state may have a comment explaining the reasoning of the ISE (such as if the document was going to move to a different stream)." + } + }, + { + "pk": 76, + "model": "doc.state", + "fields": { + "used": true, + "name": "Document on Hold Based On IESG Request", + "next_states": [], + "slug": "iesghold", + "type": "draft-stream-ise", + "order": 9, + "desc": "The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the ISE has agreed to such a hold." + } + }, + { + "pk": 79, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "minutes", + "order": 1, + "desc": "" + } + }, + { + "pk": 80, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "minutes", + "order": 2, + "desc": "" + } + }, + { + "pk": 77, + "model": "doc.state", + "fields": { + "used": true, + "name": "Active", + "next_states": [], + "slug": "active", + "type": "slides", + "order": 1, + "desc": "" + } + }, + { + "pk": 78, + "model": "doc.state", + "fields": { + "used": true, + "name": "Deleted", + "next_states": [], + "slug": "deleted", + "type": "slides", + "order": 2, + "desc": "" + } + }, + { + "pk": 121, + "model": "doc.state", + "fields": { + "used": true, + "name": "Needs Shepherd", + "next_states": [ + 122, + 129 + ], + "slug": "needshep", + "type": "statchg", + "order": 1, + "desc": "An RFC status change has been requested, but a shepherding AD has not yet been assigned" + } + }, + { + "pk": 122, + "model": "doc.state", + "fields": { + "used": true, + "name": "AD Review", + "next_states": [ + 130, + 123, + 129 + ], + "slug": "adrev", + "type": "statchg", + "order": 2, + "desc": "The sponsoring AD is preparing an RFC status change document" + } + }, + { + "pk": 130, + "model": "doc.state", + "fields": { + "used": true, + "name": "Last Call Requested", + "next_states": [ + 131 + ], + "slug": "lc-req", + "type": "statchg", + "order": 3, + "desc": "Last Call has been requested for this proposed status change" + } + }, + { + "pk": 131, + "model": "doc.state", + "fields": { + "used": true, + "name": "In Last Call", + "next_states": [ + 132 + ], + "slug": "in-lc", + "type": "statchg", + "order": 4, + "desc": "This proposed status change is in IETF Last Call" + } + }, + { + "pk": 132, + "model": "doc.state", + "fields": { + "used": true, + "name": "Waiting for AD Go-Ahead", + "next_states": [ + 123, + 129 + ], + "slug": "goahead", + "type": "statchg", + "order": 5, + "desc": "The AD is following up on IETF LC comments" + } + }, + { + "pk": 123, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation", + "next_states": [ + 124, + 125, + 126, + 129 + ], + "slug": "iesgeval", + "type": "statchg", + "order": 6, + "desc": "The IESG is considering the proposed RFC status changes" + } + }, + { + "pk": 124, + "model": "doc.state", + "fields": { + "used": true, + "name": "IESG Evaluation - Defer", + "next_states": [ + 123, + 125, + 126, + 129 + ], + "slug": "defer", + "type": "statchg", + "order": 7, + "desc": "The evaluation of the proposed RFC status changes have been deferred to the next telechat" + } + }, + { + "pk": 125, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved - point raised", + "next_states": [ + 126, + 127 + ], + "slug": "appr-pr", + "type": "statchg", + "order": 8, + "desc": "The IESG has approved the RFC status changes, but a point has been raised that should be cleared before proceeding to announcement to be sent" + } + }, + { + "pk": 126, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved - announcement to be sent", + "next_states": [ + 127 + ], + "slug": "appr-pend", + "type": "statchg", + "order": 9, + "desc": "The IESG has approved the RFC status changes, but the secretariat has not yet sent the announcement" + } + }, + { + "pk": 127, + "model": "doc.state", + "fields": { + "used": true, + "name": "Approved - announcement sent", + "next_states": [], + "slug": "appr-sent", + "type": "statchg", + "order": 10, + "desc": "The secretariat has announced the IESG's approved RFC status changes" + } + }, + { + "pk": 129, + "model": "doc.state", + "fields": { + "used": true, + "name": "Dead", + "next_states": [ + 121 + ], + "slug": "dead", + "type": "statchg", + "order": 11, + "desc": "The RFC status changes have been abandoned" + } + }, + { + "pk": 5, + "model": "doc.ballottype", + "fields": { + "doc_type": "conflrev", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "discuss", + "abstain", + "recuse", + "norecord" + ], + "question": "Is this the correct conflict review response?", + "slug": "conflrev", + "order": 0 + } + }, + { + "pk": 6, + "model": "doc.ballottype", + "fields": { + "doc_type": "statchg", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "discuss", + "abstain", + "recuse", + "norecord" + ], + "question": "Do we approve these RFC status changes?", + "slug": "statchg", + "order": 0 + } + }, + { + "pk": 1, + "model": "doc.ballottype", + "fields": { + "doc_type": "charter", + "used": true, + "name": "Ready for external review", + "positions": [ + "yes", + "noobj", + "block", + "abstain", + "norecord" + ], + "question": "Is this charter ready for external review?", + "slug": "r-extrev", + "order": 1 + } + }, + { + "pk": 4, + "model": "doc.ballottype", + "fields": { + "doc_type": "draft", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "discuss", + "abstain", + "recuse", + "norecord" + ], + "question": "", + "slug": "approve", + "order": 1 + } + }, + { + "pk": 2, + "model": "doc.ballottype", + "fields": { + "doc_type": "charter", + "used": true, + "name": "Ready w/o external review", + "positions": [ + "yes", + "noobj", + "block", + "abstain", + "norecord" + ], + "question": "Is this charter ready for external review? Is this charter ready for approval without external review?", + "slug": "r-wo-ext", + "order": 2 + } + }, + { + "pk": 3, + "model": "doc.ballottype", + "fields": { + "doc_type": "charter", + "used": true, + "name": "Approve", + "positions": [ + "yes", + "noobj", + "block", + "abstain", + "norecord" + ], + "question": "Do we approve of this charter?", + "slug": "approve", + "order": 3 + } + } +] \ No newline at end of file diff --git a/ietf/name/fixtures/names.xml b/ietf/name/fixtures/names.xml deleted file mode 100644 index 93b326aa7..000000000 --- a/ietf/name/fixtures/names.xml +++ /dev/null @@ -1,2280 +0,0 @@ - - - - Yes - - True - 1 - False - - - No Objection - - True - 2 - False - - - Discuss - - True - 3 - True - - - Block - - True - 3 - True - - - Abstain - - True - 4 - False - - - Recuse - - True - 5 - False - - - No Record - - True - 6 - False - - - Conflicts with - - True - 0 - - - Conflicts with (secondary) - - True - 0 - - - Conflicts with (tertiary) - - True - 0 - - - Person must be present - - True - 0 - - - Obsoletes - - True - 0 - Obsoleted by - - - Updates - - True - 0 - Updated by - - - Replaces - - True - 0 - Replaced by - - - conflict reviews - - True - 0 - Conflict reviewed by - - - Normative Reference - Normative Reference - True - 0 - fixme - - - Reference - A reference found in a document which does not have split normative/informative reference sections. - True - 0 - fixme - - - Informative Reference - Informative Reference - True - 0 - fixme - - - Moves to Proposed Standard - - True - 0 - Moved to Proposed Standard by - - - Moves to Internet Standard - - True - 0 - Moved to Internet Standard by - - - Moves to Historic - - True - 0 - Moved to Historic by - - - Moves to Informational - - True - 0 - Moved to Informational by - - - Moves to BCP - - True - 0 - Moved to BCP by - - - Moves to Experimental - - True - 0 - Moved to Experimental by - - - Possible Reference - Reference of unknown type, likely found in the text of the document. - True - 3 - fixme - - - Stream state should change - - 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 - - True - 0 - - - Via RFC Editor - - True - 0 - - - Approved in minutes - - True - 0 - - - Shepherd Needed - - True - 0 - - - Waiting for Dependency on Other Document - - True - 0 - - - IESG Review Completed - - True - 0 - - - IANA - The document has IANA actions that are not yet completed. - True - 0 - - - Revised I-D Needed - Issue raised by WG - - True - 0 - - - Point Raised - writeup needed - IESG discussions on the document have raised some issues that need to be brought to the attention of the authors/WG, but those issues have not been written down yet. (It is common for discussions during a telechat to result in such situations. An AD may raise a possible issue during a telechat and only decide as a result of that discussion whether the issue is worth formally writing up and bringing to the attention of the authors/WG). A document stays in the "Point Raised - Writeup Needed" state until *ALL* IESG comments that have been raised have been documented. - True - 1 - - - Awaiting Expert Review/Resolution of Issues Raised - - True - 1 - - - Editor Needed - - True - 1 - - - AD Followup - A generic substate indicating that the shepherding AD has the action item to determine appropriate next steps. In particular, the appropriate steps (and the corresponding next state or substate) depend entirely on the nature of the issues that were raised and can only be decided with active involvement of the shepherding AD. Examples include: - -- if another AD raises an issue, the shepherding AD may first iterate with the other AD to get a better understanding of the exact issue. Or, the shepherding AD may attempt to argue that the issue is not serious enough to bring to the attention of the authors/WG. - -- if a documented issue is forwarded to a WG, some further iteration may be needed before it can be determined whether a new revision is needed or whether the WG response to an issue clarifies the issue sufficiently. - -- when a new revision appears, the shepherding AD will first look at the changes to determine whether they believe all outstanding issues have been raised satisfactorily, prior to asking the ADs who raised the original issues to verify the changes. - True - 2 - - - Awaiting External Review/Resolution of Issues Raised - - True - 2 - - - Waiting for Partner Feedback - - True - 2 - - - External Party - The document is awaiting review or input from an external party (i.e, someone other than the shepherding AD, the authors, or the WG). See the "note" field for more details on who has the action. - True - 3 - - - Awaiting Merge with Other Document - - True - 3 - - - Awaiting Reviews - - True - 3 - - - Author or Editor Needed - - True - 4 - - - Document Shepherd Followup - - True - 4 - - - Revised I-D Needed - An updated I-D is needed to address the issues that have been raised. - True - 5 - - - Waiting for Referenced Document - - True - 5 - - - Waiting for Referencing Document - - True - 6 - - - Revised I-D Needed - Issue raised by WGLC - - True - 7 - - - Revised I-D Needed - Issue raised by AD - - True - 8 - - - Revised I-D Needed - Issue raised by IESG - - True - 9 - - - Doc Shepherd Follow-up Underway - - True - 10 - - - Other - see Comment Log - - True - 11 - - - Charter - - True - 0 - - - Agenda - - True - 0 - - - Minutes - - True - 0 - - - Slides - - True - 0 - - - Draft - - True - 0 - - - Liaison Attachment - - True - 0 - - - Conflict Review - - True - 0 - - - Status Change - - True - 0 - - - Active - - True - 1 - - - Deleted - - True - 2 - - - For review - - True - 3 - - - Chartering/rechartering - - True - 4 - - - BOF - - True - 0 - - - Proposed - - True - 0 - - - Active - - True - 0 - - - Dormant - - True - 0 - - - Concluded - - True - 0 - - - Unknown - - True - 0 - - - Abandonded - Formation of the group (most likely a BoF or Proposed WG) was abandoned - True - 0 - - - BOF Concluded - - True - 0 - - - IETF - - True - 0 - - - Area - - True - 0 - - - AG - Area group - True - 0 - - - WG - Working group - True - 0 - - - RG - Research group - True - 0 - - - Team - - True - 0 - - - Individual - - True - 0 - - - SDO - Standards organization - True - 0 - - - IRTF - - True - 0 - - - RFC Editor - - True - 0 - - - NomCom - - True - 0 - - - Proposed Standard - - True - 1 - - - Draft Standard - - False - 2 - - - Internet Standard - - True - 3 - - - Best Current Practice - - True - 4 - - - Informational - - True - 5 - - - Experimental - - True - 6 - - - Historic - - True - 7 - - - For action - - True - 1 - - - For comment - - True - 2 - - - For information - - True - 3 - - - In response - - True - 4 - - - Pending - - True - 0 - - - Accepted - - True - 0 - - - Declined - - True - 0 - - - Comment - - True - 0 - - - Questionnaire response - - True - 0 - - - Nomination - - True - 0 - - - Offtopic - - True - 0 - - - reStructuredText - - True - 0 - - - Plain - - True - 0 - - - Django - - True - 0 - - - IETF - - True - 0 - - - Interim - - True - 0 - - - Area Director - - True - 0 - - - Incoming Area Director - - True - 0 - - - Chair - - True - 0 - - - Editor - - True - 0 - - - Secretary - - True - 0 - - - Tech Advisor - - True - 0 - - - Executive Director - - True - 0 - - - Administrative Director - - True - 0 - - - Liaison Manager - - True - 0 - - - Authorized Individual - - True - 0 - - - Delegate - - True - 0 - - - At Large Member - - True - 0 - - - Member - - True - 0 - - - Waiting for Scheduling - - True - 0 - - - Waiting for Approval - - True - 0 - - - Approved - - True - 0 - - - Scheduled - - True - 0 - - - Canceled - - True - 0 - - - Disapproved - - True - 0 - - - Not meeting - - True - 0 - - - Deleted - - True - 0 - - - Internet Standard - - True - 0 - - - Draft Standard - - False - 0 - - - Proposed Standard - - True - 0 - - - Informational - - True - 0 - - - Experimental - - True - 0 - - - Best Current Practice - - True - 0 - - - Historic - - True - 0 - - - Unknown - - True - 0 - - - IETF - IETF stream - True - 1 - - - ISE - Independent Submission Editor stream - True - 2 - - - IRTF - Independent Submission Editor stream - True - 3 - - - IAB - IAB stream - True - 4 - - - Legacy - Legacy stream - True - 5 - - - Other - - True - 0 - - - Session - - True - 0 - - - Break - - True - 0 - - - Registration - - True - 0 - - - Plenary - - True - 0 - - - State - - - IESG state - - - IANA state - - - RFC Editor state - - - IETF WG state - - - IRTF state - - - ISE state - - - IAB state - - - State - - - State - - - State - - - State - - - State - - - Conflict Review State - - - IANA Action state - - - IANA Review state - - - RFC Status Change - - - agenda - active - Active - True - - 1 - - - - agenda - deleted - Deleted - True - - 2 - - - - charter - notrev - Not currently under review - True - The proposed charter is not being considered at this time. A proposed charter will remain in this state until an AD moves it to Informal IESG review. - 0 - - - - charter - infrev - Informal IESG review - True - This is the initial state when an AD proposes a new charter. The normal next state is Internal review if the idea is accepted, or Not currently under review if the idea is abandoned. - 0 - - - - charter - intrev - Internal review - True - The IESG and IAB are reviewing the early draft of the charter; this is the initial IESG and IAB review. The usual next state is External review if the idea is adopted, or Informal IESG review if the IESG decides the idea needs more work, or Not currently under review is the idea is abandoned - 0 - - - - charter - extrev - External review - True - The IETF community and possibly other standards development organizations (SDOs) are reviewing the proposed charter. The usual next state is IESG review, although it might move to Not currently under review is the idea is abandoned during the external review. - 0 - - - - charter - iesgrev - IESG review - True - The IESG is reviewing the discussion from the external review of the proposed charter. The usual next state is Approved, or Not currently under review if the idea is abandoned. - 0 - - - - charter - approved - Approved - True - The charter is approved by the IESG. - 0 - - - - conflrev - needshep - Needs Shepherd - True - A conflict review has been requested, but a shepherding AD has not yet been assigned - 1 - - - - conflrev - adrev - AD Review - True - The sponsoring AD is reviewing the document and preparing a proposed response - 2 - - - - conflrev - iesgeval - IESG Evaluation - True - The IESG is considering the proposed conflict review response - 3 - - - - conflrev - defer - IESG Evaluation - Defer - True - The evaluation of the proposed conflict review response has been deferred to the next telechat - 4 - - - - conflrev - appr-reqnopub-pr - Approved Request to Not Publish - point raised - True - The IESG has approved the conflict review response (a request to not publish), but a point has been raised that should be cleared before moving to announcement to be sent - 5 - - - - conflrev - appr-noprob-pr - Approved No Problem - point raised - True - The IESG has approved the conflict review response, but a point has been raised that should be cleared before proceeding to announcement to be sent - 6 - - - - conflrev - appr-reqnopub-pend - Approved Request to Not Publish - announcement to be sent - True - The IESG has approved the conflict review response (a request to not publish), but the secretariat has not yet sent the response - 7 - - - - conflrev - appr-noprob-pend - Approved No Problem - announcement to be sent - True - The IESG has approved the conflict review response, but the secretariat has not yet sent the response - 8 - - - - conflrev - appr-reqnopub-sent - Approved Request to Not Publish - announcement sent - True - The secretariat has delivered the IESG's approved conflict review response (a request to not publish) to the requester - 9 - - - - conflrev - appr-noprob-sent - Approved No Problem - announcement sent - True - The secretariat has delivered the IESG's approved conflict review response to the requester - 10 - - - - conflrev - withdraw - Withdrawn - True - The request for conflict review was withdrawn - 11 - - - - conflrev - dead - Dead - True - The conflict review has been abandoned - 12 - - - - draft - active - Active - True - - 1 - - - - draft - expired - Expired - True - - 2 - - - - draft - rfc - RFC - True - - 3 - - - - draft - repl - Replaced - True - - 4 - - - - draft - auth-rm - Withdrawn by Submitter - True - - 5 - - - - draft - ietf-rm - Withdrawn by IETF - True - - 6 - - - - draft-iana-action - newdoc - New Document - True - A new document has been received by IANA, but no actions have been taken - 1 - - - - draft-iana-action - inprog - In Progress - True - IANA is currently processing the actions for this document - 2 - - - - draft-iana-action - waitauth - Waiting on Authors - True - IANA is waiting on the document's authors to respond - 3 - - - - draft-iana-action - waitad - Waiting on ADs - True - IANA is waiting on the IETF Area Directors to respond - 4 - - - - draft-iana-action - waitwgc - Waiting on WGC - True - IANA is waiting on the IETF Working Group Chairs to respond - 5 - - - - draft-iana-action - waitrfc - Waiting on RFC Editor - True - IANA has notified the RFC Editor that the actions have been completed - 6 - - - - draft-iana-action - rfcedack - RFC-Ed-Ack - True - Request completed. The RFC Editor has acknowledged receipt of IANA's message that the actions have been completed - 7 - - - - draft-iana-action - onhold - On Hold - True - IANA has suspended work on the document - 8 - - - - draft-iana-action - noic - No IC - True - Request completed. There were no IANA actions for this document - 9 - - - - draft-iana-review - need-rev - IANA - Review Needed - True - Document has not yet been reviewed by IANA. - 1 - - - - draft-iana-review - ok-act - IANA OK - Actions Needed - True - Document requires IANA actions, and the IANA Considerations section indicates the details of the actions correctly. - 2 - - - - draft-iana-review - ok-noact - IANA OK - No Actions Needed - True - Document requires no IANA action, and the IANA Considerations section indicates this correctly. - 3 - - - - draft-iana-review - not-ok - IANA - Not OK - True - IANA has issues with the text of the IANA Considerations section of the document. - 4 - - - - draft-iana-review - changed - Version Changed - Review Needed - True - Document revision has changed after review by IANA. - 5 - - - - draft-iesg - pub-req - Publication Requested - True - A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested. - 10 - - - - draft-iesg - ad-eval - AD Evaluation - True - A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole. - 11 - - - - draft-iesg - review-e - Expert Review - True - An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the "MIB doctors". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the "note" field for specific details on the nature of the review. - 12 - - - - draft-iesg - lc-req - Last Call Requested - True - The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet. - 15 - - - - draft-iesg - lc - In Last Call - True - The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks. - 16 - - - - draft-iesg - writeupw - Waiting for Writeup - True - Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC. - 18 - - - - draft-iesg - goaheadw - Waiting for AD Go-Ahead - True - As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole. - 19 - - - - draft-iesg - iesg-eva - IESG Evaluation - 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 - defer - IESG Evaluation - Defer - True - During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat. - 21 - - - - draft-iesg - approved - Approved-announcement to be sent - True - The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message. - 27 - - - - draft-iesg - ann - Approved-announcement sent - 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 - rfcqueue - RFC Ed Queue - True - The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html). - 31 - - - - draft-iesg - pub - RFC Published - True - The ID has been published as an RFC. - 32 - - - - draft-iesg - nopubadw - DNP-waiting for AD note - True - Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the "note" field for more details on who has the action item. - 33 - - - - draft-iesg - nopubanw - DNP-announcement to be sent - True - The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official "do not publish" recommendation message. - 34 - - - - draft-iesg - watching - AD is watching - True - An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and "I-D Exists" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD's own reasons). - 42 - - - - draft-iesg - dead - Dead - True - Document is "dead" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.) - 99 - - - - draft-rfceditor - auth - AUTH - True - Awaiting author action - 0 - - - - draft-rfceditor - auth48 - AUTH48 - True - Awaiting final author approval - 0 - - - - draft-rfceditor - edit - EDIT - False - Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing - 0 - - - - draft-rfceditor - iana - IANA - True - Document has been edited, but is holding for completion of IANA actions - 0 - - - - draft-rfceditor - iesg - IESG - False - Awaiting IESG action - 0 - - - - draft-rfceditor - isr - ISR - True - Independent Submission Review by the ISE - 0 - - - - draft-rfceditor - isr-auth - ISR-AUTH - False - Independent submission awaiting author action, or in discussion between author and ISE - 0 - - - - draft-rfceditor - ref - REF - True - Holding for normative reference - 0 - - - - draft-rfceditor - rfc-edit - RFC-EDITOR - True - Awaiting final RFC Editor review before AUTH48 - 0 - - - - draft-rfceditor - timeout - TO - True - Time-out period during which the IESG reviews document for conflict/concurrence with other IETF working group work - 0 - - - - draft-rfceditor - missref - MISSREF - True - Awaiting missing normative reference - 0 - - - - draft-rfceditor - auth48done - AUTH48-DONE - False - Final approvals are complete - 0 - - - - draft-rfceditor - auth48-done - AUTH48-DONE - True - Final approvals are complete - 0 - - - - draft-rfceditor - edit - EDIT - True - Approved by the stream manager (e.g., IESG, IAB, IRSG, ISE), awaiting processing and publishing - 0 - - - - draft-rfceditor - iana-crd - IANA - True - RFC-Editor/IANA Registration Coordination - 0 - - - - draft-rfceditor - iesg - IESG - True - Holding for IESG action - 0 - - - - draft-rfceditor - isr-auth - ISR-AUTH - True - Independent Submission awaiting author update, or in discussion between author and ISE - 0 - - - - draft-stream-iab - candidat - Candidate IAB Document - True - A document being considered for the IAB stream. - 1 - - - - draft-stream-iab - active - Active IAB Document - True - This document has been adopted by the IAB and is being actively developed. - 2 - - - - draft-stream-iab - parked - Parked IAB Document - True - This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the IAB for some other reason. Annotations probably explain why this document is parked. - 3 - - - - draft-stream-iab - review-i - IAB Review - True - This document is awaiting the IAB itself to come to internal consensus. - 4 - - - - draft-stream-iab - review-c - Community Review - True - This document has completed internal consensus within the IAB and is now under community review. - 5 - - - - draft-stream-iab - approved - Approved by IAB, To Be Sent to RFC Editor - True - The consideration of this document is complete, but it has not yet been sent to the RFC Editor for publication (although that is going to happen soon). - 6 - - - - draft-stream-iab - diff-org - Sent to a Different Organization for Publication - True - The IAB does not expect to publish the document itself, but has passed it on to a different organization that might continue work on the document. The expectation is that the other organization will eventually publish the document. - 7 - - - - draft-stream-iab - rfc-edit - Sent to the RFC Editor - True - The IAB processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the IAB. - 8 - - - - draft-stream-iab - pub - Published RFC - True - The document has been published as an RFC. - 9 - - - - draft-stream-iab - dead - Dead IAB Document - True - This document was an active IAB document, but for some reason it is no longer being pursued for the IAB stream. It is possible that the document might be revived later, possibly in another stream. - 10 - - - - draft-stream-ietf - c-adopt - Call For Adoption By WG Issued - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.1" target="_blank">4.2.1. Call for Adoption by WG Issued</a> - - The "Call for Adoption by WG Issued" state should be used to indicate when an I-D is being considered for adoption by an IETF WG. An I-D that is in this state is actively being considered for adoption and has not yet achieved consensus, preference, or selection in the WG. - - This state may be used to describe an I-D that someone has asked a WG to consider for adoption, if the WG Chair has agreed with the request. This state may also be used to identify an I-D that a WG Chair asked an author to write specifically for consideration as a candidate WG item [WGDTSPEC], and/or an I-D that is listed as a 'candidate draft' in the WG's charter. - - Under normal conditions, it should not be possible for an I-D to be in the "Call for Adoption by WG Issued" state in more than one working group at the same time. This said, it is not uncommon for authors to "shop" their I-Ds to more than one WG at a time, with the hope of getting their documents adopted somewhere. - - After this state is implemented in the Datatracker, an I-D that is in the "Call for Adoption by WG Issued" state will not be able to be "shopped" to any other WG without the consent of the WG Chairs and the responsible ADs impacted by the shopping. - - Note that Figure 1 includes an arc leading from this state to outside of the WG state machine. This illustrates that some I-Ds that are considered do not get adopted as WG drafts. An I-D that is not adopted as a WG draft will transition out of the WG state machine and revert back to having no stream-specific state; however, the status change history log of the I-D will record that the I-D was previously in the "Call for Adoption by WG Issued" state. - 1 - - - - draft-stream-ietf - adopt-wg - Adopted by a WG - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.2" target="_blank">4.2.2. Adopted by a WG</a> - - The "Adopted by a WG" state describes an individual submission I-D that an IETF WG has agreed to adopt as one of its WG drafts. - - WG Chairs who use this state will be able to clearly indicate when their WGs adopt individual submission I-Ds. This will facilitate the Datatracker's ability to correctly capture "Replaces" information for WG drafts and correct "Replaced by" information for individual submission I-Ds that have been replaced by WG drafts. - - This state is needed because the Datatracker uses the filename of an I-D as a key to search its database for status information about the I-D, and because the filename of a WG I-D is supposed to be different from the filename of an individual submission I-D. The filename of an individual submission I-D will typically be formatted as 'draft-author-wgname-topic-nn'. - - The filename of a WG document is supposed to be formatted as 'draft- ietf-wgname-topic-nn'. - - An individual I-D that is adopted by a WG may take weeks or months to be resubmitted by the author as a new (version-00) WG draft. If the "Adopted by a WG" state is not used, the Datatracker has no way to determine that an I-D has been adopted until a new version of the I-D is submitted to the WG by the author and until the I-D is approved for posting by a WG Chair. - 2 - - - - draft-stream-ietf - info - Adopted for WG Info Only - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.3" target="_blank">4.2.3. Adopted for WG Info Only</a> - - The "Adopted for WG Info Only" state describes a document that contains useful information for the WG that adopted it, but the document is not intended to be published as an RFC. The WG will not actively develop the contents of the I-D or progress it for publication as an RFC. The only purpose of the I-D is to provide information for internal use by the WG. - 3 - - - - draft-stream-ietf - wg-doc - WG Document - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.4" target="_blank">4.2.4. WG Document</a> - - The "WG Document" state describes an I-D that has been adopted by an IETF WG and is being actively developed. - - A WG Chair may transition an I-D into the "WG Document" state at any time as long as the I-D is not being considered or developed in any other WG. - - Alternatively, WG Chairs may rely upon new functionality to be added to the Datatracker to automatically move version-00 drafts into the "WG Document" state as described in Section 4.1. - - Under normal conditions, it should not be possible for an I-D to be in the "WG Document" state in more than one WG at a time. This said, I-Ds may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs. - 4 - - - - draft-stream-ietf - parked - Parked WG Document - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.5" target="_blank">4.2.5. Parked WG Document</a> - - A "Parked WG Document" is an I-D that has lost its author or editor, is waiting for another document to be written or for a review to be completed, or cannot be progressed by the working group for some other reason. - - Some of the annotation tags described in Section 4.3 may be used in conjunction with this state to indicate why an I-D has been parked, and/or what may need to happen for the I-D to be un-parked. - - Parking a WG draft will not prevent it from expiring; however, this state can be used to indicate why the I-D has stopped progressing in the WG. - - A "Parked WG Document" that is not expired may be transferred from one WG to another with the consent of the WG Chairs and the responsible ADs. - 5 - - - - draft-stream-ietf - dead - Dead WG Document - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.6" target="_blank">4.2.6. Dead WG Document</a> - - A "Dead WG Document" is an I-D that has been abandoned. Note that 'Dead' is not always a final state for a WG I-D. If consensus is subsequently achieved, a "Dead WG Document" may be resurrected. A "Dead WG Document" that is not resurrected will eventually expire. - - Note that an I-D that is declared to be "Dead" in one WG and that is not expired may be transferred to a non-dead state in another WG with the consent of the WG Chairs and the responsible ADs. - 6 - - - - draft-stream-ietf - wg-lc - In WG Last Call - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.7" target="_blank">4.2.7. In WG Last Call</a> - - A document "In WG Last Call" is an I-D for which a WG Last Call (WGLC) has been issued and is in progress. - - Note that conducting a WGLC is an optional part of the IETF WG process, per Section 7.4 of RFC 2418 [RFC2418]. - - If a WG Chair decides to conduct a WGLC on an I-D, the "In WG Last Call" state can be used to track the progress of the WGLC. The Chair may configure the Datatracker to send a WGLC message to one or more mailing lists when the Chair moves the I-D into this state. The WG Chair may also be able to select a different set of mailing lists for a different document undergoing a WGLC; some documents may deserve coordination with other WGs. - - A WG I-D in this state should remain "In WG Last Call" until the WG Chair moves it to another state. The WG Chair may configure the Datatracker to send an e-mail after a specified period of time to remind or 'nudge' the Chair to conclude the WGLC and to determine the next state for the document. - - It is possible for one WGLC to lead into another WGLC for the same document. For example, an I-D that completed a WGLC as an "Informational" document may need another WGLC if a decision is taken to convert the I-D into a Standards Track document. - 7 - - - - draft-stream-ietf - chair-w - Waiting for WG Chair Go-Ahead - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.8" target="_blank">4.2.8. Waiting for WG Chair Go-Ahead</a> - - A WG Chair may wish to place an I-D that receives a lot of comments during a WGLC into the "Waiting for WG Chair Go-Ahead" state. This state describes an I-D that has undergone a WGLC; however, the Chair is not yet ready to call consensus on the document. - - If comments from the WGLC need to be responded to, or a revision to the I-D is needed, the Chair may place an I-D into this state until all of the WGLC comments are adequately addressed and the (possibly revised) document is in the I-D repository. - 8 - - - - draft-stream-ietf - writeupw - WG Consensus: Waiting for Write-Up - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.9" target="_blank">4.2.9. WG Consensus: Waiting for Writeup</a> - - A document in the "WG Consensus: Waiting for Writeup" state has essentially completed its development within the working group, and is nearly ready to be sent to the IESG for publication. The last thing to be done is the preparation of a protocol writeup by a Document Shepherd. The IESG requires that a document shepherd writeup be completed before publication of the I-D is requested. The IETF document shepherding process and the role of a WG Document Shepherd is described in RFC 4858 [RFC4858] - - A WG Chair may call consensus on an I-D without a formal WGLC and transition an I-D that was in the "WG Document" state directly into this state. - - The name of this state includes the words "Waiting for Writeup" because a good document shepherd writeup takes time to prepare. - 9 - - - - draft-stream-ietf - sub-pub - Submitted to IESG for Publication - True - <a href="http://tools.ietf.org/html/rfc6174#section-4.2.10" target="_blank">4.2.10. Submitted to IESG for Publication</a> - - This state describes a WG document that has been submitted to the IESG for publication and that has not been sent back to the working group for revision. - - An I-D in this state may be under review by the IESG, it may have been approved and be in the RFC Editor's queue, or it may have been published as an RFC. Other possibilities exist too. The document may be "Dead" (in the IESG state machine) or in a "Do Not Publish" state. - 10 - - - - draft-stream-irtf - candidat - Candidate RG Document - True - This document is under consideration in an RG for becoming an IRTF document. A document in this state does not imply any RG consensus and does not imply any precedence or selection. It's simply a way to indicate that somebody has asked for a document to be considered for adoption by an RG. - 1 - - - - draft-stream-irtf - active - Active RG Document - True - This document has been adopted by the RG and is being actively developed. - 2 - - - - draft-stream-irtf - parked - Parked RG Document - True - This document has lost its author or editor, is waiting for another document to be written, or cannot currently be worked on by the RG for some other reason. - 3 - - - - draft-stream-irtf - rg-lc - In RG Last Call - True - The document is in its final review in the RG. - 4 - - - - draft-stream-irtf - sheph-w - Waiting for Document Shepherd - True - IRTF documents have document shepherds who help RG documents through the process after the RG has finished with the document. - 5 - - - - draft-stream-irtf - chair-w - Waiting for IRTF Chair - True - The IRTF Chair is meant to be performing some task such as sending a request for IESG Review. - 6 - - - - draft-stream-irtf - irsg-w - Awaiting IRSG Reviews - True - The document shepherd has taken the document to the IRSG and solicited reviews from one or more IRSG members. - 7 - - - - draft-stream-irtf - irsgpoll - In IRSG Poll - True - The IRSG is taking a poll on whether or not the document is ready to be published. - 8 - - - - draft-stream-irtf - iesg-rev - In IESG Review - True - The IRSG has asked the IESG to do a review of the document, as described in RFC5742. - 9 - - - - draft-stream-irtf - rfc-edit - Sent to the RFC Editor - True - The RG processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the RG. - 10 - - - - draft-stream-irtf - pub - Published RFC - True - The document has been published as an RFC. - 11 - - - - draft-stream-irtf - iesghold - Document on Hold Based On IESG Request - True - The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the IRTF has agreed to such a hold. - 12 - - - - draft-stream-irtf - dead - Dead IRTF Document - True - This document was an active IRTF document, but for some reason it is no longer being pursued for the IRTF stream. It is possible that the document might be revived later, possibly in another stream. - 13 - - - - draft-stream-ise - receive - Submission Received - True - The draft has been sent to the ISE with a request for publication. - 1 - - - - draft-stream-ise - find-rev - Finding Reviewers - True - The ISE is finding initial reviewers for the document. - 2 - - - - draft-stream-ise - ise-rev - In ISE Review - True - The ISE is actively working on the document. - 3 - - - - draft-stream-ise - need-res - Response to Review Needed - True - One or more reviews have been sent to the author, and the ISE is awaiting response. - 4 - - - - draft-stream-ise - iesg-rev - In IESG Review - True - The ISE has asked the IESG to do a review of the document, as described in RFC5742. - 5 - - - - draft-stream-ise - rfc-edit - Sent to the RFC Editor - True - The ISE processing of this document is complete and it has been sent to the RFC Editor for publication. The document may be in the RFC Editor's queue, or it may have been published as an RFC; this state doesn't distinguish between different states occurring after the document has left the ISE. - 6 - - - - draft-stream-ise - pub - Published RFC - True - The document has been published as an RFC. - 7 - - - - draft-stream-ise - dead - No Longer In Independent Submission Stream - True - This document was actively considered in the Independent Submission stream, but the ISE chose not to publish it. It is possible that the document might be revived later. A document in this state may have a comment explaining the reasoning of the ISE (such as if the document was going to move to a different stream). - 8 - - - - draft-stream-ise - iesghold - Document on Hold Based On IESG Request - True - The IESG has requested that the document be held pending further review, as specified in RFC 5742, and the ISE has agreed to such a hold. - 9 - - - - minutes - active - Active - True - - 1 - - - - minutes - deleted - Deleted - True - - 2 - - - - slides - active - Active - True - - 1 - - - - slides - deleted - Deleted - True - - 2 - - - - statchg - needshep - Needs Shepherd - True - An RFC status change has been requested, but a shepherding AD has not yet been assigned - 1 - - - - statchg - adrev - AD Review - True - The sponsoring AD is preparing an RFC status change document - 2 - - - - statchg - lc-req - Last Call Requested - True - Last Call has been requested for this proposed status change - 3 - - - - statchg - in-lc - In Last Call - True - This proposed status change is in IETF Last Call - 4 - - - - statchg - goahead - Waiting for AD Go-Ahead - True - The AD is following up on IETF LC comments - 5 - - - - statchg - iesgeval - IESG Evaluation - True - The IESG is considering the proposed RFC status changes - 6 - - - - statchg - defer - IESG Evaluation - Defer - True - The evaluation of the proposed RFC status changes have been deferred to the next telechat - 7 - - - - statchg - appr-pr - Approved - point raised - True - The IESG has approved the RFC status changes, but a point has been raised that should be cleared before proceeding to announcement to be sent - 8 - - - - statchg - appr-pend - Approved - announcement to be sent - True - The IESG has approved the RFC status changes, but the secretariat has not yet sent the announcement - 9 - - - - statchg - appr-sent - Approved - announcement sent - True - The secretariat has announced the IESG's approved RFC status changes - 10 - - - - statchg - dead - Dead - True - The RFC status changes have been abandoned - 11 - - - - conflrev - conflrev - Approve - Is this the correct conflict review response? - True - 0 - - - - statchg - statchg - Approve - Do we approve these RFC status changes? - True - 0 - - - - charter - r-extrev - Ready for external review - Is this charter ready for external review? - True - 1 - - - - draft - approve - Approve - - True - 1 - - - - charter - r-wo-ext - Ready w/o external review - Is this charter ready for external review? Is this charter ready for approval without external review? - True - 2 - - - - charter - approve - Approve - Do we approve of this charter? - True - 3 - - - diff --git a/ietf/name/generate_fixtures.py b/ietf/name/generate_fixtures.py index c4b8bed35..144f17475 100644 --- a/ietf/name/generate_fixtures.py +++ b/ietf/name/generate_fixtures.py @@ -17,8 +17,8 @@ from django.db.models import Q def output(name, qs): try: - f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.xml" % name), 'w') - f.write(serialize("xml", qs, indent=4)) + f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.json" % name), 'w') + f.write(serialize("json", qs, indent=1)) f.close() except: from django.db import connection @@ -29,12 +29,13 @@ def output(name, qs): # pick all name models directly out of the module objects = [] +import inspect import ietf.name.models for n in dir(ietf.name.models): - if n[:1].upper() == n[:1] and n.endswith("Name"): - model = getattr(ietf.name.models, n) - if not model._meta.abstract: - objects.extend(model.objects.all()) + symbol = getattr(ietf.name.models, n) + if inspect.isclass(symbol) and issubclass(symbol, ietf.name.models.NameModel): + if not symbol._meta.abstract: + objects.extend(symbol.objects.all()) import ietf.doc.models # also pick some other name-like types while we're at it diff --git a/ietf/name/migrations/0018_auto__add_draftsubmissionstatename.py b/ietf/name/migrations/0018_auto__add_draftsubmissionstatename.py new file mode 100644 index 000000000..c1891f69b --- /dev/null +++ b/ietf/name/migrations/0018_auto__add_draftsubmissionstatename.py @@ -0,0 +1,214 @@ +# 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 'DraftSubmissionStateName' + db.create_table('name_draftsubmissionstatename', ( + ('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', ['DraftSubmissionStateName']) + + # Adding M2M table for field next_states on 'DraftSubmissionStateName' + db.create_table('name_draftsubmissionstatename_next_states', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('from_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False)), + ('to_draftsubmissionstatename', models.ForeignKey(orm['name.draftsubmissionstatename'], null=False)) + )) + db.create_unique('name_draftsubmissionstatename_next_states', ['from_draftsubmissionstatename_id', 'to_draftsubmissionstatename_id']) + + + def backwards(self, orm): + + # Deleting model 'DraftSubmissionStateName' + db.delete_table('name_draftsubmissionstatename') + + # Removing M2M table for field next_states on 'DraftSubmissionStateName' + db.delete_table('name_draftsubmissionstatename_next_states') + + + 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'}), + 'penalty': ('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.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': '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'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + '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.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': "orm['name.DraftSubmissionStateName']"}), + '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.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + '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.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': '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.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + '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/0019_populate_draftsubmissionstate.py b/ietf/name/migrations/0019_populate_draftsubmissionstate.py new file mode 100644 index 000000000..0d91bfa02 --- /dev/null +++ b/ietf/name/migrations/0019_populate_draftsubmissionstate.py @@ -0,0 +1,204 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + uploaded, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="uploaded", order=1, name="Uploaded") + submitter, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="auth", order=2, name="Awaiting Submitter Authentication") + author_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="aut-appr", order=3, name="Awaiting Approval from Previous Version Authors'") + group_approval, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="grp-appr", order=4, name="Awaiting Initial Version Approval") + manual_post, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="manual", order=5, name="Awaiting Manual Post") + cancel, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="cancel", order=6, name="Cancelled") + posted, _ = orm.DraftSubmissionStateName.objects.get_or_create(slug="posted", order=7, name="Posted") + + uploaded.next_states = [submitter, author_approval, group_approval, manual_post, cancel] + submitter.next_states = [cancel, posted] + author_approval.next_states = [cancel, posted] + group_approval.next_states = [cancel, posted] + manual_post.next_states = [cancel, posted] + + + def backwards(self, orm): + "Write your backwards methods here." + + + 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'}), + 'penalty': ('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.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': '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'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + '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.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': "orm['name.DraftSubmissionStateName']"}), + '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.feedbacktype': { + 'Meta': {'ordering': "['order']", 'object_name': 'FeedbackType'}, + '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.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': '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.nomineepositionstate': { + 'Meta': {'ordering': "['order']", 'object_name': 'NomineePositionState'}, + '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/models.py b/ietf/name/models.py index 0f97aaa14..7403256d1 100644 --- a/ietf/name/models.py +++ b/ietf/name/models.py @@ -66,3 +66,8 @@ class FeedbackType(NameModel): """Type of feedback: questionnaires, nominations, comments""" class DBTemplateTypeName(NameModel): """reStructuredText, Plain, Django""" +class DraftSubmissionStateName(NameModel): + """Uploaded, Awaiting Submitter Authentication, Awaiting Approval from + Previous Version Authors, Awaiting Initial Version Approval, Awaiting + Manual Post, Cancelled, Posted""" + next_states = models.ManyToManyField('DraftSubmissionStateName', related_name="previous_states", blank=True) diff --git a/ietf/nomcom/models.py b/ietf/nomcom/models.py index 410a20cee..ff6d8c8f4 100644 --- a/ietf/nomcom/models.py +++ b/ietf/nomcom/models.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- import os from django.db import models diff --git a/ietf/nomcom/templatetags/nomcom_tags.py b/ietf/nomcom/templatetags/nomcom_tags.py index 29a640492..97aa4ee67 100644 --- a/ietf/nomcom/templatetags/nomcom_tags.py +++ b/ietf/nomcom/templatetags/nomcom_tags.py @@ -25,7 +25,7 @@ def is_chair(user, year): nomcom = get_nomcom_by_year(year=year) if has_role(user, "Secretariat"): return True - return nomcom.group.is_chair(user) + return nomcom.group.has_role(user, "chair") @register.filter diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 73061570e..864ba0ae8 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -30,7 +30,7 @@ from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_s class NomcomViewsTest(TestCase): """Tests to create a new nomcom""" # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names', 'nomcom_templates'] + perma_fixtures = ['nomcom_templates'] def check_url_status(self, url, status): response = self.client.get(url) @@ -595,7 +595,7 @@ class NomcomViewsTest(TestCase): class NomineePositionStateSaveTest(TestCase): """Tests for the NomineePosition save override method""" # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names', 'nomcom_templates'] + perma_fixtures = ['nomcom_templates'] def setUp(self): nomcom_test_data() @@ -627,7 +627,7 @@ class NomineePositionStateSaveTest(TestCase): class FeedbackTest(TestCase): - perma_fixtures = ['names', 'nomcom_templates'] + perma_fixtures = ['nomcom_templates'] def setUp(self): nomcom_test_data() @@ -659,7 +659,7 @@ class FeedbackTest(TestCase): os.unlink(self.cert_file.name) class ReminderTest(TestCase): - perma_fixtures = ['names', 'nomcom_templates'] + perma_fixtures = ['nomcom_templates'] def setUp(self): nomcom_test_data() diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py index 152da53c2..c2deffd9a 100644 --- a/ietf/nomcom/utils.py +++ b/ietf/nomcom/utils.py @@ -84,18 +84,6 @@ def get_user_email(user): pass return user._email_cache -def is_nomcom_member(user, nomcom): - is_group_member = nomcom.group.is_member(user) - if not is_group_member: - raise PermissionDenied("Must be nomcom member") - - -def is_nomcom_chair(user, nomcom): - is_group_chair = nomcom.group.is_chair(user) - if not is_group_chair: - raise PermissionDenied("Must be nomcom chair") - - def get_hash_nominee_position(date, nominee_position_id): return hashlib.md5('%s%s%s' % (settings.SECRET_KEY, date, nominee_position_id)).hexdigest() diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index 05381d07d..d6632c3ad 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -1,4 +1,4 @@ - # -*- coding: utf-8 -*- + # -*- coding: utf-8 -*- import datetime @@ -101,7 +101,7 @@ def private_key(request, year): def private_index(request, year): nomcom = get_nomcom_by_year(year) all_nominee_positions = NomineePosition.objects.get_by_nomcom(nomcom).not_duplicated() - is_chair = nomcom.group.is_chair(request.user) + is_chair = nomcom.group.has_role(request.user, "chair") message = None if is_chair and request.method == 'POST': action = request.POST.get('action') diff --git a/ietf/secr/announcement/forms.py b/ietf/secr/announcement/forms.py index eca264aac..0dab4343a 100644 --- a/ietf/secr/announcement/forms.py +++ b/ietf/secr/announcement/forms.py @@ -7,7 +7,6 @@ from ietf.secr.utils.group import current_nomcom from ietf.message.models import Message from ietf.ietfauth.decorators import has_role -from ietf.wgchairs.accounts import get_person_for_user # --------------------------------------------- # Globals @@ -185,7 +184,7 @@ class AnnounceForm(forms.ModelForm): def save(self, *args, **kwargs): user = kwargs.pop('user') message = super(AnnounceForm, self).save(commit=False) - message.by = get_person_for_user(user) + message.by = user.get_profile() if self.cleaned_data['to'] == 'Other...': message.to = self.cleaned_data['to_custom'] if kwargs['commit']: diff --git a/ietf/secr/announcement/tests.py b/ietf/secr/announcement/tests.py index 1884843f3..89fcf6c00 100644 --- a/ietf/secr/announcement/tests.py +++ b/ietf/secr/announcement/tests.py @@ -22,9 +22,6 @@ AD_USER='' class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - # ------- Test View -------- # def test_main(self): "Main Test" @@ -37,8 +34,6 @@ class DummyCase(TestCase): name = connection.settings_dict['NAME'] class UnauthorizedCase(TestCase): - perma_fixtures = ['names'] - def test_unauthorized(self): "Unauthorized Test" draft = make_test_data() @@ -48,8 +43,6 @@ class UnauthorizedCase(TestCase): self.assertEquals(r.status_code, 403) class SubmitCase(TestCase): - perma_fixtures = ['names'] - def test_invalid_submit(self): "Invalid Submit" draft = make_test_data() diff --git a/ietf/secr/announcement/views.py b/ietf/secr/announcement/views.py index a32ec44f5..71810c7fc 100644 --- a/ietf/secr/announcement/views.py +++ b/ietf/secr/announcement/views.py @@ -6,7 +6,6 @@ from django.template import RequestContext from ietf.ietfauth.decorators import has_role from ietf.utils.mail import send_mail_text -from ietf.wgchairs.accounts import get_person_for_user from ietf.group.models import Group from ietf.secr.utils.group import current_nomcom from ietf.secr.utils.decorators import check_for_cancel @@ -111,4 +110,4 @@ def confirm(request): 'message': data, 'to': to}, RequestContext(request, {}), - ) \ No newline at end of file + ) diff --git a/ietf/secr/areas/tests.py b/ietf/secr/areas/tests.py index 9b70fc916..cec3b65a8 100644 --- a/ietf/secr/areas/tests.py +++ b/ietf/secr/areas/tests.py @@ -22,7 +22,7 @@ def augment_data(): class MainTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names', 'persons', 'groupgroup', 'groupevents'] + perma_fixtures = ['persons', 'groupgroup', 'groupevents'] def test_main(self): "Main Test" diff --git a/ietf/secr/drafts/email.py b/ietf/secr/drafts/email.py index d72af76e9..534f0578f 100644 --- a/ietf/secr/drafts/email.py +++ b/ietf/secr/drafts/email.py @@ -78,19 +78,6 @@ def get_abbr_authors(draft): return result -def get_authors_email(draft): - """ - Takes a draft object and returns a string of authors suitable for an email to or cc field - """ - authors = [] - for a in draft.authors.all(): - initial = '' - if a.person.first_name: - initial = a.person.first_name[0] + '. ' - entry = '%s%s <%s>' % (initial,a.person.last_name,a.person.email()) - authors.append(entry) - return ', '.join(authors) - def get_last_revision(filename): """ This function takes a filename, in the same form it appears in the InternetDraft record, diff --git a/ietf/secr/drafts/forms.py b/ietf/secr/drafts/forms.py index 5d4d13846..e5ced7dc3 100644 --- a/ietf/secr/drafts/forms.py +++ b/ietf/secr/drafts/forms.py @@ -375,7 +375,7 @@ class UploadForm(forms.Form): # ensure that the basename is unique base = splitext(txt.name)[0] if Document.objects.filter(name=base[:-3]): - raise forms.ValidationError, "This doucment filename already exists: %s" % base[:-3] + raise forms.ValidationError, "This document filename already exists: %s" % base[:-3] # ensure that rev is 00 if base[-2:] != '00': diff --git a/ietf/secr/drafts/tests.py b/ietf/secr/drafts/tests.py index 53c5e89ef..c905dc250 100644 --- a/ietf/secr/drafts/tests.py +++ b/ietf/secr/drafts/tests.py @@ -9,9 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): "Main Test" draft = make_test_data() @@ -25,4 +22,4 @@ class MainTestCase(TestCase): drafts = Document.objects.filter(type='draft') url = reverse('drafts_view', kwargs={'id':drafts[0].name}) response = self.client.get(url, REMOTE_USER=SECR_USER) - self.assertEquals(response.status_code, 200) \ No newline at end of file + self.assertEquals(response.status_code, 200) diff --git a/ietf/secr/drafts/views.py b/ietf/secr/drafts/views.py index b0ebc9126..1191aea14 100644 --- a/ietf/secr/drafts/views.py +++ b/ietf/secr/drafts/views.py @@ -17,7 +17,7 @@ from ietf.meeting.models import Meeting from ietf.name.models import StreamName from ietf.doc.models import Document, DocumentAuthor from ietf.doc.utils import augment_with_start_time -from ietf.submit.models import IdSubmissionDetail, Preapproval +from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName, SubmissionEvent from ietf.utils.draft import Draft from ietf.secr.proceedings.proc_utils import get_progress_stats from ietf.secr.sreq.views import get_meeting @@ -107,7 +107,7 @@ def process_files(request,draft): the basename, revision number and a list of file types. Basename and revision are assumed to be the same for all because this is part of the validation process. - It also creates the IdSubmissionDetail record WITHOUT saving, and places it in the + It also creates the Submission record WITHOUT saving, and places it in the session for saving in the final action step. ''' files = request.FILES @@ -124,29 +124,37 @@ def process_files(request,draft): wrapper = Draft(file.read(),file.name) handle_uploaded_file(file) - # create IdSubmissionDetail record, leaved unsaved - idsub = IdSubmissionDetail( - id_document_name=draft.title, - filename=basename, - revision=revision, - txt_page_count=draft.pages, - filesize=txt_size, - creation_date=wrapper.get_creation_date(), + # create Submission record, leaved unsaved + idsub = Submission( + name=basename, + title=draft.title, + rev=revision, + pages=draft.pages, + file_size=txt_size, + document_date=wrapper.get_creation_date(), submission_date=datetime.date.today(), idnits_message='idnits bypassed by manual posting', - temp_id_document_tag=None, - group_acronym_id=draft.group.id, + group_id=draft.group.id, remote_ip=request.META['REMOTE_ADDR'], first_two_pages=''.join(wrapper.pages[:2]), - status_id=-2, + state=DraftSubmissionStateName.objects.get(slug="posted"), abstract=draft.abstract, - file_type=','.join(file_type_list), - man_posted_date=datetime.date.today(), - man_posted_by=request.user.get_profile()) + file_types=','.join(file_type_list), + ) request.session['idsub'] = idsub return (filename,revision,file_type_list) +def post_submission(request): + submission = request.session['idsub'] + submission.save() + + SubmissionEvent.objects.create( + submission=submission, + by=request.user.person, + desc="Submitted and posted manually") + + def promote_files(draft, types): ''' This function takes one argument, a draft object. It then moves the draft files from @@ -307,8 +315,8 @@ def do_revision(draft, request): promote_files(new_draft, request.session['file_type']) # save the submission record - request.session['idsub'].save() - + post_submission(request) + # send announcement if we are in IESG process if new_draft.get_state('draft-iesg'): announcement_from_form(request.session['email'],by=request.user.get_profile()) @@ -355,8 +363,8 @@ def do_update(draft,request): promote_files(new_draft, request.session['file_type']) # save the submission record - request.session['idsub'].save() - + post_submission(request) + # send announcement announcement_from_form(request.session['email'],by=request.user.get_profile()) @@ -580,7 +588,7 @@ def add(request): promote_files(draft, file_type_list) # save the submission record - request.session['idsub'].save() + post_submission(request) request.session['action'] = 'add' diff --git a/ietf/secr/groups/tests.py b/ietf/secr/groups/tests.py index b1f023594..1c9c6e539 100644 --- a/ietf/secr/groups/tests.py +++ b/ietf/secr/groups/tests.py @@ -9,7 +9,7 @@ SECR_USER='secretary' class GroupsTest(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names','persons','groupgroup',] + perma_fixtures = ['persons','groupgroup',] """ perma_fixtures = [ 'acronym.json', 'area.json', diff --git a/ietf/secr/ipradmin/tests.py b/ietf/secr/ipradmin/tests.py index c64a50654..36e33c677 100644 --- a/ietf/secr/ipradmin/tests.py +++ b/ietf/secr/ipradmin/tests.py @@ -9,9 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): "Main Test" draft = make_test_data() @@ -26,4 +23,4 @@ class MainTestCase(TestCase): url = reverse('drafts_view', kwargs={'id':drafts[0].name}) response = self.client.get(url, REMOTE_USER=SECR_USER) self.assertEquals(response.status_code, 200) -""" \ No newline at end of file +""" diff --git a/ietf/secr/meetings/tests.py b/ietf/secr/meetings/tests.py index e0995c318..af7dfd095 100644 --- a/ietf/secr/meetings/tests.py +++ b/ietf/secr/meetings/tests.py @@ -9,9 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): "Main Test" url = reverse('meetings') diff --git a/ietf/secr/proceedings/tests.py b/ietf/secr/proceedings/tests.py index 2b50c9c64..f62f6daef 100644 --- a/ietf/secr/proceedings/tests.py +++ b/ietf/secr/proceedings/tests.py @@ -10,9 +10,6 @@ import debug SECR_USER='secretary' class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): "Main Test" make_test_data() diff --git a/ietf/secr/roles/tests.py b/ietf/secr/roles/tests.py index 8e49494a2..bb2272212 100644 --- a/ietf/secr/roles/tests.py +++ b/ietf/secr/roles/tests.py @@ -11,13 +11,13 @@ import debug SECR_USER='secretary' def augment_data(): - # need this for the RoleForm intialization - Group.objects.create(acronym='dummy',name='Dummy Group',type_id='sdo') + # need this for the RoleForm intialization + Group.objects.create(acronym='dummy',name='Dummy Group',type_id='sdo') class MainTestCase(TestCase): # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names', 'persons', 'groupgroup'] - + perma_fixtures = ['persons', 'groupgroup'] + def test_main(self): "Main Test" augment_data() diff --git a/ietf/secr/rolodex/tests.py b/ietf/secr/rolodex/tests.py index fa6298bfc..61a632c82 100644 --- a/ietf/secr/rolodex/tests.py +++ b/ietf/secr/rolodex/tests.py @@ -9,9 +9,6 @@ from pyquery import PyQuery SECR_USER='secretary' class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): "Main Test" url = reverse('rolodex') diff --git a/ietf/secr/sreq/tests.py b/ietf/secr/sreq/tests.py index 0854ee55a..4aec3f2f6 100644 --- a/ietf/secr/sreq/tests.py +++ b/ietf/secr/sreq/tests.py @@ -20,9 +20,6 @@ class SreqUrlTestCase(SimpleUrlTestCase): self.doTestUrls(__file__) class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): draft = make_test_data() url = reverse('sessions') @@ -34,8 +31,6 @@ class MainTestCase(TestCase): self.failUnless(len(unsched) > 0) class SubmitRequestCase(TestCase): - perma_fixtures = ['names'] - def test_submit_request(self): draft = make_test_data() acronym = Group.objects.all()[0].acronym @@ -71,4 +66,4 @@ class RetrievePreviousCase(TestCase): # test error if already scheduled # test get previous exists/doesn't exist # test that groups scheduled and unscheduled add up to total groups - # test locking function, access by unauthorized \ No newline at end of file + # test locking function, access by unauthorized diff --git a/ietf/secr/telechat/tests.py b/ietf/secr/telechat/tests.py index c2a0b03c4..887e4f16f 100644 --- a/ietf/secr/telechat/tests.py +++ b/ietf/secr/telechat/tests.py @@ -1,7 +1,7 @@ from django.core.urlresolvers import reverse from ietf.utils import TestCase -from ietf.iesg.models import TelechatDate, TelechatAgendaItem, WGAction +from ietf.iesg.models import TelechatDate, TelechatAgendaItem from ietf.person.models import Person from ietf.utils.test_data import make_test_data @@ -15,9 +15,6 @@ def augment_data(): TelechatDate.objects.create(date=datetime.datetime.today()) class MainTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - def test_main(self): "Main Test" augment_data() diff --git a/ietf/secr/telechat/views.py b/ietf/secr/telechat/views.py index 05d4e79bc..f55462a48 100644 --- a/ietf/secr/telechat/views.py +++ b/ietf/secr/telechat/views.py @@ -8,15 +8,14 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from ietf.doc.models import DocEvent, Document, BallotDocEvent, BallotPositionDocEvent, TelechatDocEvent, WriteupDocEvent, save_document_in_history -from ietf.doc.proxy import InternetDraft from ietf.doc.utils import get_document_content, log_state_changed from ietf.group.models import Group from ietf.name.models import BallotPositionName from ietf.person.models import Person from ietf.doc.lastcall import request_last_call from ietf.doc.mails import email_ad, email_state_changed -from ietf.iesg.models import TelechatDate, TelechatAgendaItem, WGAction -from ietf.iesg.views import _agenda_data +from ietf.iesg.models import TelechatDate, TelechatAgendaItem +from ietf.iesg.agenda import agenda_data, get_doc_section from forms import * import os @@ -25,8 +24,6 @@ import datetime ''' EXPECTED CHANGES: x group pages will be just another doc, charter doc -x charter docs to discuss will be passed in the 'docs' section of agenda -x expand get_section_header to include section 4 x consolidate views (get rid of get_group_header,group,group_navigate) ''' @@ -38,9 +35,7 @@ active_ballot_positions: takes one argument, doc. returns a dictionary with a k NOTE: this function has been deprecated as of Datatracker 4.34. Should now use methods on the Document. For example: doc.active_ballot().active_ad_positions() -_agenda_data: takes a request object and a date string in the format YYYY-MM-DD. - - 2012-07-28 this function was changed to return Document objects instead - of old InternetDraft wrappers +agenda_data: takes a date string in the format YYYY-MM-DD. ''' # ------------------------------------------------- @@ -52,10 +47,11 @@ def get_doc_list(agenda): Document objects in the order they appear in the agenda sections 1-3. ''' docs = [] - for key in sorted(agenda['docs']): - docs.extend(agenda['docs'][key]) + for num, section in sorted(agenda['sections'].iteritems()): + if "docs" in section: + docs.extend(section["docs"]) - return [x['obj'] for x in docs] + return docs def get_doc_writeup(doc): ''' @@ -87,44 +83,29 @@ def get_next_telechat_date(): ''' return TelechatDate.objects.filter(date__gte=datetime.date.today()).order_by('date')[0].date -def get_section_header(file,agenda): +def get_section_header(doc, agenda): ''' This function takes a filename and an agenda dictionary and returns the - agenda section header as a string for use in the doc template + agenda section header as a list for use in the doc template ''' - h1 = {'2':'Protocol Actions','3':'Document Actions','4':'Working Group Actions'} - h2a = {'1':'WG Submissions','2':'Individual Submissions','3':'Status Changes'} - h2b = {'1':'WG Submissions','2':'Individual Submissions via AD','3':'Status Changes','4':'IRTF and Independent Submission Stream Documents'} - h2c = {'1':'WG Creation','2':'WG Chartering'} - h3a = {'1':'New Item','2':'Returning Item','3':'For Action'} - h3b = {'1':'Proposed for IETF Review','2':'Proposed for Approval'} - h3c = {'1':'Under Evaluation for IETF Review','2':'Proposed for Approval'} + num = get_doc_section(doc) - # Robert updated _agenda_data to return Document objects instead of the ID wrapper - #doc = InternetDraft.objects.get(filename=file) - doc = Document.objects.get(name=file) + header = [] - test = {'obj':doc} - for k,v in agenda['docs'].iteritems(): - if test in v: - section = k - count = '%s of %s' % (v.index(test) + 1, len(v)) - break + split = num.split(".") - header = [ '%s %s' % (section[1], h1[section[1]]) ] - if section[1] == '2': - header.append('%s.%s %s' % (section[1], section[2], h2a[section[2]])) - elif section[1] == '4': - header.append('%s.%s %s' % (section[1], section[2], h2c[section[2]])) - else: - header.append('%s.%s %s' % (section[1], section[2], h2b[section[2]])) - if section[1] == '4': - if section[2] == '1': - header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3b[section[3]])) - elif section[2] == '2': - header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3c[section[3]])) - else: - header.append('%s.%s.%s %s' % (section[1], section[2], section[3], h3a[section[3]])) + for i in xrange(num.count(".")): + parent_num = ".".join(split[:i + 1]) + parent = agenda["sections"].get(parent_num) + if parent: + if "." not in parent_num: + parent_num += "." + header.append(u"%s %s" % (parent_num, parent["title"])) + + section = agenda["sections"][num] + header.append(u"%s %s" % (num, section["title"])) + + count = '%s of %s' % (section["docs"].index(doc) + 1, len(section["docs"])) header.append(count) return header @@ -133,9 +114,9 @@ def get_first_doc(agenda): ''' This function takes an agenda dictionary and returns the first document in the agenda ''' - for k,v in sorted(agenda['docs'].iteritems()): - if v: - return v[0]['obj'] + for num, section in sorted(agenda['sections'].iteritems()): + if "docs" in section and section["docs"]: + return section["docs"][0] return None @@ -144,7 +125,7 @@ def get_first_doc(agenda): # ------------------------------------------------- def bash(request, date): - agenda = _agenda_data(request, date=date) + agenda = agenda_data(date=date) return render_to_response('telechat/bash.html', { 'agenda': agenda, @@ -158,7 +139,7 @@ def doc(request, date): displays the message "No Documents" ''' - agenda = _agenda_data(request, date=date) + agenda = agenda_data(date=date) doc = get_first_doc(agenda) if doc: url = reverse('telechat_doc_detail', kwargs={'date':date,'name':doc.name}) @@ -212,8 +193,8 @@ def doc_detail(request, date, name): 'substate':tag} BallotFormset = formset_factory(BallotForm, extra=0) - agenda = _agenda_data(request, date=date) - header = get_section_header(name,agenda) if name else '' + agenda = agenda_data(date=date) + header = get_section_header(doc, agenda) # nav button logic doc_list = get_doc_list(agenda) @@ -329,7 +310,7 @@ def doc_navigate(request, date, name, nav): The view retrieves the appropriate document and redirects to the doc view. ''' doc = get_object_or_404(Document, docalias__name=name) - agenda = _agenda_data(request, date=date) + agenda = agenda_data(date=date) target = name docs = get_doc_list(agenda) @@ -346,10 +327,6 @@ def doc_navigate(request, date, name, nav): def main(request): ''' The is the main view where the user selects an existing telechat or creates a new one. - - NOTES ON EXTERNAL HELPER FUNCTIONS: - _agenda_data(): returns dictionary of agenda sections - get_ballot(name): returns a BallotWrapper and RfcWrapper or IdWrapper ''' if request.method == 'POST': date=request.POST['date'] @@ -371,7 +348,7 @@ def management(request, date): This view displays management issues and lets the user update the status ''' - agenda = _agenda_data(request, date=date) + agenda = agenda_data(date=date) issues = TelechatAgendaItem.objects.filter(type=3).order_by('id') return render_to_response('telechat/management.html', { @@ -386,17 +363,18 @@ def minutes(request, date): This view shows a list of documents that were approved since the last telechat ''' # get the telechat previous to selected one - dates = [ t.date for t in TelechatDate.objects.all() ] y,m,d = date.split('-') current = datetime.date(int(y),int(m),int(d)) - index = dates.index(current) - previous = dates[index + 1] + + previous = TelechatDate.objects.filter(date__lt=current).order_by("-date")[0].date events = DocEvent.objects.filter(type='iesg_approved',time__gte=previous,time__lt=current,doc__type='draft') docs = [ e.doc for e in events ] pa_docs = [ d for d in docs if d.intended_std_level.slug not in ('inf','exp','hist') ] da_docs = [ d for d in docs if d.intended_std_level.slug in ('inf','exp','hist') ] - agenda = _agenda_data(request, date=date) + agenda = agenda_data(date=date) + + # FIXME: this doesn't show other documents return render_to_response('telechat/minutes.html', { 'agenda': agenda, @@ -422,8 +400,8 @@ def new(request): def roll_call(request, date): - agenda = _agenda_data(request, date=date) - ads = Person.objects.filter(role__name='ad') + agenda = agenda_data(date=date) + ads = Person.objects.filter(role__name='ad', role__group__state="active") sorted_ads = sorted(ads, key = lambda a: a.name_parts()[3]) return render_to_response('telechat/roll_call.html', { diff --git a/ietf/secr/templates/telechat/agenda_outline_23.html b/ietf/secr/templates/telechat/agenda_outline_23.html deleted file mode 100644 index a3fc9c8d0..000000000 --- a/ietf/secr/templates/telechat/agenda_outline_23.html +++ /dev/null @@ -1,142 +0,0 @@ -{% comment %} -Copyright (C) 2008-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. -{% endcomment %} -{% with "2. Protocol Actions" as title1 %}{% with 1 as title1_first %} -{% with "2.1 WG Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "2.1.1 New Items" as title3 %} -{% with docs.s211 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.1.2 Returning Items" as title3 %} -{% with docs.s212 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s213 %} -{% with "2.1.3 For Action" as title3 %} -{% with docs.s213 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} -{% endwith %}{# title1_first #} - -{% with "2.2 Individual Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "2.2.1 New Items" as title3 %} -{% with docs.s221 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "2.2.2 Returning Items" as title3 %} -{% with docs.s222 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s223 %} -{% with "2.2.3 For Action" as title3 %} -{% with docs.s223 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %}{# title1 #} - -{% with "3. Document Actions" as title1 %}{% with 1 as title1_first %} - -{% with "3.1 WG Submissions" as title2 %} -{% with 1 as title2_first %} - -{% with "3.1.1 New Items" as title3 %} -{% with docs.s311 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.1.2 Returning Items" as title3 %} -{% with docs.s312 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s313 %} -{% with "3.1.3 For Action" as title3 %} -{% with docs.s313 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %}{# title1_first #} - -{% with "3.2 Individual Submissions Via AD" as title2 %} -{% with 1 as title2_first %} - -{% with "3.2.1 New Items" as title3 %} -{% with docs.s321 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.2.2 Returning Items" as title3 %} -{% with docs.s322 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s323 %} -{% with "3.2.3 For Action" as title3 %} -{% with docs.s323 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% with "3.3 IRTF and Independent Submission Stream Documents" as title2 %} -{% with 1 as title2_first %} - -{% with "3.3.1 New Items" as title3 %} -{% with docs.s331 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endwith %}{# title2_first #} - -{% with "3.3.2 Returning Items" as title3 %} -{% with docs.s332 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} - -{% if docs.s333 %} -{% with "3.3.3 For Action" as title3 %} -{% with docs.s333 as section_docs %}{% include doc_template %}{% endwith %} -{% endwith %} -{% endif %} - -{% endwith %}{# title2 #} - -{% endwith %} diff --git a/ietf/secr/templates/telechat/base_telechat.html b/ietf/secr/templates/telechat/base_telechat.html index cecad39b8..a972b3e5f 100644 --- a/ietf/secr/templates/telechat/base_telechat.html +++ b/ietf/secr/templates/telechat/base_telechat.html @@ -12,77 +12,30 @@ {% endblock %} {% block content %} - +{% load ietf_filters %}

Agenda {{ date }}

{% if agenda %}
    -
  • 2 Protocol Actions
  • -
  • 2.1 WG Submissions
  • -
  • 2.1.1 New Item
  • - {% with agenda.docs.s211 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 2.1.2 Returning Item
  • - {% with agenda.docs.s212 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 2.1.3 For Action
  • - {% with agenda.docs.s213 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 2.2 Individual Submissions
  • -
  • 2.2.1 New Item
  • - {% with agenda.docs.s221 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 2.2.2 Returning Item
  • - {% with agenda.docs.s222 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 2.2.3 For Action
  • - {% with agenda.docs.s223 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 2.3 Status Changes
  • -
  • 2.3.1 New Item
  • - {% with agenda.docs.s231 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 2.3.2 Returning Item
  • - {% with agenda.docs.232 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 3 Document Actions
  • -
  • 3.1 WG Submissions
  • -
  • 3.1.1 New Item
  • - {% with agenda.docs.s311 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.1.2 Returning Item
  • - {% with agenda.docs.s312 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.1.3 For Action
  • - {% with agenda.docs.s313 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 3.2 Individual Submissions via AD
  • -
  • 3.2.1 New Item
  • - {% with agenda.docs.s321 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.2.2 Returning Item
  • - {% with agenda.docs.s322 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.2.3 For Action
  • - {% with agenda.docs.s323 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 3.3 Status Changes
  • -
  • 3.3.1 New Item
  • - {% with agenda.docs.s331 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.3.2 Returning Item
  • - {% with agenda.docs.s332 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 3.4 Independent Submissions via RFC Editor
  • -
  • 3.4.1 New Item
  • - {% with agenda.docs.s341 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.4.2 Returning Item
  • - {% with agenda.docs.s342 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 3.4.3 For Action
  • - {% with agenda.docs.s343 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 4 Working Group Actions
  • -
  • 4.1 WG Creation
  • -
  • 4.1.1 Proposed for IETF Review
  • - {% with agenda.docs.s411 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 4.1.2 Proposed for Approval
  • - {% with agenda.docs.s412 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • -
  • 4.2 WG Rechartering
  • -
  • 4.2.1 Under evaluation for IETF Review
  • - {% with agenda.docs.s421 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} -
  • 4.2.2 Proposed for Approval
  • - {% with agenda.docs.s422 as section_docs %}{% include "telechat/doc_template.html" %}{% endwith %} + {% for num, section in agenda.sections.iteritems %} + {% if num >= "2" and num < "5" %} +
  • {{ num }} {{ section.title }} + + {% if "docs" in section %} + {% if section.docs %} + + {% else %} +
    None
    + {% endif %} +
  • + {% endif %} + + {% endif %} + {% endfor %}
{% else %} Please select a telechat to display the agenda. diff --git a/ietf/secr/templates/telechat/doc.html b/ietf/secr/templates/telechat/doc.html index a26d39fd7..793bf308f 100644 --- a/ietf/secr/templates/telechat/doc.html +++ b/ietf/secr/templates/telechat/doc.html @@ -7,8 +7,8 @@ {% block subsection %}
{% if document %} - {% if not nav_start %}<< Previous{% endif %} - {% if not nav_end %}Next >>{% endif %} + {% if not nav_start %}« Previous{% endif %} + {% if not nav_end %}Next »{% endif %}

{% for line in header %} @@ -64,7 +64,7 @@

Ballot Writeup

-    {{ writeup }}
+{{ writeup }}
     
diff --git a/ietf/secr/templates/telechat/doc_template.html b/ietf/secr/templates/telechat/doc_template.html index 700a717c1..acb165d5a 100644 --- a/ietf/secr/templates/telechat/doc_template.html +++ b/ietf/secr/templates/telechat/doc_template.html @@ -1,7 +1,7 @@ {% if section_docs %} {% else %} diff --git a/ietf/settings.py b/ietf/settings.py index a5c6bc8ca..42c00c66b 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -161,8 +161,6 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'django.contrib.messages', 'south', - 'workflows', - 'permissions', 'ietf.person', 'ietf.name', 'ietf.group', @@ -180,11 +178,8 @@ INSTALLED_APPS = ( 'ietf.utils', #'ietf.proceedings', 'ietf.redirects', - 'ietf.idrfc', 'ietf.wginfo', 'ietf.submit', - 'ietf.ietfworkflows', - 'ietf.wgchairs', 'ietf.wgcharter', 'ietf.sync', 'ietf.community', @@ -314,12 +309,6 @@ LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool %s' % (url, instance.status) + url = urlreverse('submit_submission_status_by_hash', + kwargs=dict(submission_id=instance.pk, + access_token=instance.access_token())) + return '%s' % (url, instance.state) status_link.allow_tags = True -admin.site.register(IdSubmissionDetail, IdSubmissionDetailAdmin) + def draft_link(self, instance): + if instance.state_id == "posted": + return '%s' % (instance.name, instance.rev, instance.name) + else: + return instance.name + draft_link.allow_tags = True + +admin.site.register(Submission, SubmissionAdmin) class PreapprovalAdmin(admin.ModelAdmin): pass admin.site.register(Preapproval, PreapprovalAdmin) -class TempIdAuthorsAdmin(admin.ModelAdmin): - ordering = ["-id"] -admin.site.register(TempIdAuthors, TempIdAuthorsAdmin) - diff --git a/ietf/submit/error_manager.py b/ietf/submit/error_manager.py deleted file mode 100644 index b4dcefd8f..000000000 --- a/ietf/submit/error_manager.py +++ /dev/null @@ -1,19 +0,0 @@ -from ietf.submit.models import IdSubmissionStatus - -class ErrorManager(object): - ERROR_CODES = { - 'DEFAULT': 'Unknow error', - 'INVALID_FILENAME': 111, - 'EXCEEDED_SIZE': 102, - } - - def get_error_str(self, key): - error_code = self.ERROR_CODES.get(key, self.ERROR_CODES['DEFAULT']) - if isinstance(error_code, basestring): - return '%s (%s)' % (key, error_code) - try: - return IdSubmissionStatus.objects.get(status_id=error_code).status_value - except IdSubmissionStatus.DoesNotExist: - return '%s (%s)' % (self.ERROR_CODES['DEFAULT'], key) - -MainErrorManager=ErrorManager() diff --git a/ietf/submit/fixtures/idsubmissionstatus.xml b/ietf/submit/fixtures/idsubmissionstatus.xml deleted file mode 100644 index 280d52693..000000000 --- a/ietf/submit/fixtures/idsubmissionstatus.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - Cancelled - - - Dead - - - Posted by the Secretariat - - - Posted - - - Ready To Post - - - Uploaded - - - ID NITS Passed - - - Initial Version Approval Required - - - Submitter Authentication Required - - - Manual Post Requested - - - External Meta-Data Required - - - Internal Database Has Been Updated - - - ID Announcement Scheduled - - - ID Tracker Notification Scheduled - - - Initial Version Approval Requested - - - Error - Plain text version does not exist - - - File size is larger than 20 MB - - - Duplicate Internet-Draft submission is currently in process. - - - Error - Simultaneous submission from the same IP address - - - Error - Auth key does not match - - - Error - No such Internet-Draft is currently in process - - - Error - Draft is not in an appropriate status for the requested page - - - Error - Unknown Request - - - Error - Invalid Email Address - - - Error - Direct Access is prohibited - - - Error - Invalid version number - - - Error - Invalid filename - - - Error - The document failed idnits verification - - - Creation Date must be within 3 days of the submission date. - - - Error – Not a valid submitter - - - Incorrect Meta-Data - - - The document does not contain a legitimate filename that start with draft-*. - - - Initial Version Approved - - \ No newline at end of file diff --git a/ietf/submit/forms.py b/ietf/submit/forms.py index 1aecf5d35..5aaff363e 100644 --- a/ietf/submit/forms.py +++ b/ietf/submit/forms.py @@ -1,60 +1,47 @@ -import hashlib -import random import os -import subprocess import datetime from django import forms -from django.core.validators import email_re +from django.forms.formsets import formset_factory from django.conf import settings -from django.contrib.sites.models import Site -from django.template.loader import render_to_string from django.utils.html import mark_safe from django.core.urlresolvers import reverse as urlreverse import debug -from ietf.group.models import Group -from ietf.idtracker.models import InternetDraft, IETFWG -from ietf.proceedings.models import Meeting -from ietf.submit.models import IdSubmissionDetail, TempIdAuthors, Preapproval -from ietf.submit.utils import MANUAL_POST_REQUESTED, NONE_WG, UPLOADED, AWAITING_AUTHENTICATION, POSTED, POSTED_BY_SECRETARIAT +from ietf.group.models import Group, Role +from ietf.doc.models import Document +from ietf.meeting.models import Meeting +from ietf.submit.models import Submission, Preapproval, DraftSubmissionStateName +from ietf.submit.utils import validate_submission_rev, validate_submission_document_date from ietf.submit.parsers.pdf_parser import PDFParser from ietf.submit.parsers.plain_parser import PlainParser from ietf.submit.parsers.ps_parser import PSParser from ietf.submit.parsers.xml_parser import XMLParser from ietf.utils.mail import send_mail from ietf.utils.draft import Draft -from ietf.utils.pipe import pipe -from ietf.utils.log import log class UploadForm(forms.Form): - txt = forms.FileField(label=u'.txt format', required=True) xml = forms.FileField(label=u'.xml format', required=False) pdf = forms.FileField(label=u'.pdf format', required=False) ps = forms.FileField(label=u'.ps format', required=False) - fieldsets = [('Upload a draft', ('txt', 'xml', 'pdf', 'ps'))] - - class Media: - css = {'all': ("/css/liaisons.css", )} - - def __init__(self, *args, **kwargs): - self.request=kwargs.pop('request', None) - self.remote_ip=self.request.META.get('REMOTE_ADDR', None) + def __init__(self, request, *args, **kwargs): super(UploadForm, self).__init__(*args, **kwargs) - self.in_first_cut_off = False - self.idnits_message = None - self.shutdown = False - self.draft = None - self.filesize = None - self.group = None - self.file_type = [] - self.read_dates() - def read_dates(self): + self.remote_ip = request.META.get('REMOTE_ADDR', None) + + self.in_first_cut_off = False + self.cutoff_warning = "" + self.shutdown = False + self.set_cutoff_warnings() + + self.group = None + self.parsed_draft = None + + def set_cutoff_warnings(self): now = datetime.datetime.utcnow() first_cut_off = Meeting.get_first_cut_off() second_cut_off = Meeting.get_second_cut_off() @@ -74,170 +61,104 @@ class UploadForm(forms.Form): self.cutoff_warning = 'The cut-off time for the I-D submission was %02dh UTC, %s.
The I-D submission tool will be reopened at %02dh local time at the IETF meeting location, %s.' % (settings.CUTOFF_HOUR, second_cut_off, settings.CUTOFF_HOUR, ietf_monday) self.shutdown = True - def __unicode__(self): - return self.as_div() + def clean_file(self, field_name, parser_class): + f = self.cleaned_data[field_name] + if not f: + return f - def as_div(self): - return render_to_string('submit/submitform.html', {'form': self}) + parsed_info = parser_class(f).critical_parse() + if parsed_info.errors: + raise forms.ValidationError(parsed_info.errors) + + return f - def get_fieldsets(self): - if not self.fieldsets: - yield dict(name=None, fields=self) - else: - for fieldset, fields in self.fieldsets: - fieldset_dict = dict(name=fieldset, fields=[]) - for field_name in fields: - if field_name in self.fields.keyOrder: - fieldset_dict['fields'].append(self[field_name]) - if not fieldset_dict['fields']: - # if there is no fields in this fieldset, we continue to next fieldset - continue - yield fieldset_dict def clean_txt(self): - txt_file = self.cleaned_data['txt'] - if not txt_file: - return txt_file - parsed_info = PlainParser(txt_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - self.filesize=txt_file.size - return txt_file + return self.clean_file("txt", PlainParser) def clean_pdf(self): - pdf_file = self.cleaned_data['pdf'] - if not pdf_file: - return pdf_file - parsed_info = PDFParser(pdf_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - return pdf_file + return self.clean_file("pdf", PDFParser) def clean_ps(self): - ps_file = self.cleaned_data['ps'] - if not ps_file: - return ps_file - parsed_info = PSParser(ps_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - return ps_file + return self.clean_file("ps", PSParser) def clean_xml(self): - xml_file = self.cleaned_data['xml'] - if not xml_file: - return xml_file - parsed_info = XMLParser(xml_file).critical_parse() - if parsed_info.errors: - raise forms.ValidationError(parsed_info.errors) - return xml_file + return self.clean_file("xml", XMLParser) def clean(self): if self.shutdown: raise forms.ValidationError('The tool is shut down') - self.check_paths() - if self.cleaned_data.get('txt', None): - self.get_draft() - self.group=self.get_working_group() - self.check_previous_submission() - if self.draft.revision == '00' and self.in_first_cut_off: + + # sanity check that paths exist (for development servers) + for s in ("IDSUBMIT_STAGING_PATH", "IDSUBMIT_IDNITS_BINARY", + "IDSUBMIT_REPOSITORY_PATH", "INTERNET_DRAFT_ARCHIVE_DIR"): + if not os.path.exists(getattr(settings, s)): + raise forms.ValidationError('%s defined in settings.py does not exist' % s) + + if self.cleaned_data.get('txt'): + # try to parse it + txt_file = self.cleaned_data['txt'] + txt_file.seek(0) + self.parsed_draft = Draft(txt_file.read(), txt_file.name) + txt_file.seek(0) + + if not self.parsed_draft.filename: + raise forms.ValidationError("Draft parser could not extract a valid draft name from the .txt file") + + # check group + self.group = self.deduce_group() + + # check existing + existing = Submission.objects.filter(name=self.parsed_draft.filename, rev=self.parsed_draft.revision).exclude(state__in=("posted", "cancel")) + if existing: + raise forms.ValidationError(mark_safe('Submission with same name and revision is currently being processed. Check the status here' % urlreverse("submit_submission_status", kwargs={ 'submission_id': existing[0].pk }))) + + # cut-off + if self.parsed_draft.revision == '00' and self.in_first_cut_off: raise forms.ValidationError(mark_safe(self.cutoff_warning)) - self.check_tresholds() + + # check thresholds + today = datetime.date.today() + + self.check_submissions_tresholds( + "for the draft %s" % self.parsed_draft.filename, + dict(name=self.parsed_draft.filename, rev=self.parsed_draft.revision, submission_date=today), + settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME, settings.IDSUBMIT_MAX_DAILY_SAME_DRAFT_NAME_SIZE, + ) + self.check_submissions_tresholds( + "for the same submitter", + dict(remote_ip=self.remote_ip, submission_date=today), + settings.IDSUBMIT_MAX_DAILY_SAME_SUBMITTER, settings.IDSUBMIT_MAX_DAILY_SAME_SUBMITTER_SIZE, + ) + if self.group: + self.check_submissions_tresholds( + "for the group \"%s\"" % (self.group.acronym), + dict(group=self.group, submission_date=today), + settings.IDSUBMIT_MAX_DAILY_SAME_GROUP, settings.IDSUBMIT_MAX_DAILY_SAME_GROUP_SIZE, + ) + self.check_submissions_tresholds( + "across all submitters", + dict(submission_date=today), + settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS, settings.IDSUBMIT_MAX_DAILY_SUBMISSIONS_SIZE, + ) + return super(UploadForm, self).clean() - def check_tresholds(self): - filename = self.draft.filename - revision = self.draft.revision - remote_ip = self.remote_ip - today = datetime.date.today() + def check_submissions_tresholds(self, which, filter_kwargs, max_amount, max_size): + submissions = Submission.objects.filter(**filter_kwargs) - # Same draft by name - same_name = IdSubmissionDetail.objects.filter(filename=filename, revision=revision, submission_date=today) - if same_name.count() > settings.MAX_SAME_DRAFT_NAME: - raise forms.ValidationError('The same I-D cannot be submitted more than %s times a day' % settings.MAX_SAME_DRAFT_NAME) - if sum([i.filesize for i in same_name]) > (settings.MAX_SAME_DRAFT_NAME_SIZE * 1048576): - raise forms.ValidationError('The same I-D submission cannot exceed more than %s MByte a day' % settings.MAX_SAME_DRAFT_NAME_SIZE) + if len(submissions) > max_amount: + raise forms.ValidationError("Max submissions %s has been reached for today (maximum is %s submissions)." % (which, max_amount)) + if sum(s.file_size for s in submissions) > max_size * 1024 * 1024: + raise forms.ValidationError("Max uploaded amount %s has been reached for today (maximum is %s MB)." % (which, max_size)) - # Total from same ip - same_ip = IdSubmissionDetail.objects.filter(remote_ip=remote_ip, submission_date=today) - if same_ip.count() > settings.MAX_SAME_SUBMITTER: - raise forms.ValidationError('The same submitter cannot submit more than %s I-Ds a day' % settings.MAX_SAME_SUBMITTER) - if sum([i.filesize for i in same_ip]) > (settings.MAX_SAME_SUBMITTER_SIZE * 1048576): - raise forms.ValidationError('The same submitter cannot exceed more than %s MByte a day' % settings.MAX_SAME_SUBMITTER_SIZE) - - # Total in same group - if self.group: - same_group = IdSubmissionDetail.objects.filter(group_acronym=self.group, submission_date=today) - if same_group.count() > settings.MAX_SAME_WG_DRAFT: - raise forms.ValidationError('The same working group I-Ds cannot be submitted more than %s times a day' % settings.MAX_SAME_WG_DRAFT) - if sum([i.filesize for i in same_group]) > (settings.MAX_SAME_WG_DRAFT_SIZE * 1048576): - raise forms.ValidationError('Total size of same working group I-Ds cannot exceed %s MByte a day' % settings.MAX_SAME_WG_DRAFT_SIZE) - - - # Total drafts for today - total_today = IdSubmissionDetail.objects.filter(submission_date=today) - if total_today.count() > settings.MAX_DAILY_SUBMISSION: - raise forms.ValidationError('The total number of today\'s submission has reached the maximum number of submission per day') - if sum([i.filesize for i in total_today]) > (settings.MAX_DAILY_SUBMISSION_SIZE * 1048576): - raise forms.ValidationError('The total size of today\'s submission has reached the maximum size of submission per day') - - def check_paths(self): - self.staging_path = getattr(settings, 'IDSUBMIT_STAGING_PATH', None) - self.idnits = getattr(settings, 'IDSUBMIT_IDNITS_BINARY', None) - if not self.staging_path: - raise forms.ValidationError('IDSUBMIT_STAGING_PATH not defined in settings.py') - if not os.path.exists(self.staging_path): - raise forms.ValidationError('IDSUBMIT_STAGING_PATH defined in settings.py does not exist') - if not self.idnits: - raise forms.ValidationError('IDSUBMIT_IDNITS_BINARY not defined in settings.py') - if not os.path.exists(self.idnits): - raise forms.ValidationError('IDSUBMIT_IDNITS_BINARY defined in settings.py does not exist') - - def check_previous_submission(self): - filename = self.draft.filename - revision = self.draft.revision - existing = IdSubmissionDetail.objects.filter(filename=filename, revision=revision, - status__pk__gte=0, status__pk__lt=100) - if existing: - raise forms.ValidationError(mark_safe('Duplicate Internet-Draft submission is currently in process. Check it here' % existing[0].pk)) - - def get_draft(self): - if self.draft: - return self.draft - txt_file = self.cleaned_data['txt'] - txt_file.seek(0) - self.draft = Draft(txt_file.read(), txt_file.name) - txt_file.seek(0) - return self.draft - - def save(self): - for ext in ['txt', 'pdf', 'xml', 'ps']: - fd = self.cleaned_data[ext] - if not fd: - continue - self.file_type.append('.%s' % ext) - filename = os.path.join(self.staging_path, '%s-%s.%s' % (self.draft.filename, self.draft.revision, ext)) - destination = open(filename, 'wb+') - for chunk in fd.chunks(): - destination.write(chunk) - destination.close() - self.check_idnits() - return self.save_draft_info(self.draft) - - def check_idnits(self): - filepath = os.path.join(self.staging_path, '%s-%s.txt' % (self.draft.filename, self.draft.revision)) - #p = subprocess.Popen([self.idnits, '--submitcheck', '--nitcount', filepath], stdout=subprocess.PIPE) - cmd = "%s --submitcheck --nitcount %s" % (self.idnits, filepath) - code, out, err = pipe(cmd) - if code != 0: - log("idnits error: %s:\n Error %s: %s" %(cmd, code, err)) - self.idnits_message = out - - def get_working_group(self): - name = self.draft.filename - existing_draft = InternetDraft.objects.filter(filename=name) + def deduce_group(self): + """Figure out group from name or previously submitted draft, returns None if individual.""" + name = self.parsed_draft.filename + existing_draft = Document.objects.filter(name=name, type="draft") if existing_draft: - group = existing_draft[0].group and existing_draft[0].group.ietfwg or None - if group and group.pk != NONE_WG and group.type_id != "area": + group = existing_draft[0].group + if group and group.type_id not in ("individ", "area"): return group else: return None @@ -245,328 +166,109 @@ class UploadForm(forms.Form): if name.startswith('draft-ietf-') or name.startswith("draft-irtf-"): components = name.split("-") if len(components) < 3: - raise forms.ValidationError("The draft name \"%s\" is missing a third part, please rename it") + raise forms.ValidationError(u"The draft name \"%s\" is missing a third part, please rename it" % name) if components[1] == "ietf": group_type = "wg" - else: + elif components[1] == "irtf": group_type = "rg" # first check groups with dashes for g in Group.objects.filter(acronym__contains="-", type=group_type): if name.startswith('draft-%s-%s-' % (components[1], g.acronym)): - return IETFWG().from_object(g) + return g try: - return IETFWG().from_object(Group.objects.get(acronym=components[2], type=group_type)) + return Group.objects.get(acronym=components[2], type=group_type) except Group.DoesNotExist: raise forms.ValidationError('There is no active group with acronym \'%s\', please rename your draft' % components[2]) + elif name.startswith("draft-iab-"): - return IETFWG().from_object(Group.objects.get(acronym="iab")) + return Group.objects.get(acronym="iab") + else: return None - def save_draft_info(self, draft): - document_id = 0 - existing_draft = InternetDraft.objects.filter(filename=draft.filename) - if existing_draft: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - document_id = -1 - else: - document_id = existing_draft[0].id_document_tag - detail = IdSubmissionDetail.objects.create( - id_document_name=draft.get_title(), - filename=draft.filename, - revision=draft.revision, - txt_page_count=draft.get_pagecount(), - filesize=self.filesize, - creation_date=draft.get_creation_date(), - submission_date=datetime.date.today(), - idnits_message=self.idnits_message, - temp_id_document_tag=document_id, - group_acronym=self.group, - remote_ip=self.remote_ip, - first_two_pages=''.join(draft.pages[:2]), - status_id=UPLOADED, - abstract=draft.get_abstract(), - file_type=','.join(self.file_type), - ) - order = 0 - for author in draft.get_author_list(): - full_name, first_name, middle_initial, last_name, name_suffix, email, company = author - order += 1 - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # save full name - TempIdAuthors.objects.create( - id_document_tag=document_id, - first_name=full_name.strip(), - email_address=(email or "").strip(), - author_order=order, - submission=detail) - else: - TempIdAuthors.objects.create( - id_document_tag=document_id, - first_name=first_name, - middle_initial=middle_initial, - last_name=last_name, - name_suffix=name_suffix, - email_address=email, - author_order=order, - submission=detail) - return detail - - -class AutoPostForm(forms.Form): - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - name = forms.CharField(required=True) - else: - first_name = forms.CharField(label=u'Given name', required=True) - last_name = forms.CharField(label=u'Last name', required=True) - email = forms.EmailField(label=u'Email address', required=True) +class NameEmailForm(forms.Form): + """For validating supplied submitter and author information.""" + name = forms.CharField(required=True) + email = forms.EmailField(label=u'Email address') def __init__(self, *args, **kwargs): - self.draft = kwargs.pop('draft', None) - self.validation = kwargs.pop('validation', None) - self.replaces = kwargs.pop('replaces', None) - super(AutoPostForm, self).__init__(*args, **kwargs) + email_required = kwargs.pop("email_required", True) + super(NameEmailForm, self).__init__(*args, **kwargs) - def get_author_buttons(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - buttons = [] - for i in self.validation.authors: - buttons.append('' - % dict(name=i.get_full_name(), - email=i.email()[1] or '')) - return "".join(buttons) + self.fields["email"].required = email_required + self.fields["name"].widget.attrs["class"] = "name" + self.fields["email"].widget.attrs["class"] = "email" + def clean_name(self): + return self.cleaned_data["name"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip() - # this should be moved to a Javascript file and attributes like data-first-name ... - button_template = '' + def clean_email(self): + return self.cleaned_data["email"].replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip() - buttons = [] - for i in self.validation.authors: - full_name = u'%s. %s' % (i.first_name[0], i.last_name) - buttons.append(button_template % {'first_name': i.first_name, - 'last_name': i.last_name, - 'email': i.email()[1] or '', - 'full_name': full_name}) - return ''.join(buttons) + def cleaned_line(self): + line = self.cleaned_data["name"] + email = self.cleaned_data.get("email") + if email: + line += u" <%s>" % email + return line - def save(self, request): - self.save_submitter_info() - self.save_new_draft_info() - self.send_confirmation_mail(request) +class EditSubmissionForm(forms.ModelForm): + title = forms.CharField(required=True, max_length=255) + rev = forms.CharField(label=u'Revision', max_length=2, required=True) + document_date = forms.DateField(required=True) + pages = forms.IntegerField(required=True) + abstract = forms.CharField(widget=forms.Textarea, required=True) - def send_confirmation_mail(self, request): - subject = 'Confirmation for Auto-Post of I-D %s' % self.draft.filename - from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = self.draft.confirmation_email_list() + note = forms.CharField(label=mark_safe(u'Comment to
the Secretariat'), widget=forms.Textarea, required=False) - confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_confirm', kwargs=dict(submission_id=self.draft.submission_id, auth_key=self.draft.auth_key)) - status_url = settings.IDTRACKER_BASE_URL + urlreverse('draft_status_by_hash', kwargs=dict(submission_id=self.draft.submission_id, submission_hash=self.draft.get_hash())) - - send_mail(request, to_email, from_email, subject, 'submit/confirm_autopost.txt', - { 'draft': self.draft, 'confirm_url': confirm_url, 'status_url': status_url }) - - def save_submitter_info(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return TempIdAuthors.objects.create( - id_document_tag=self.draft.temp_id_document_tag, - first_name=self.cleaned_data['name'], - email_address=self.cleaned_data['email'], - author_order=0, - submission=self.draft) - - return TempIdAuthors.objects.create( - id_document_tag=self.draft.temp_id_document_tag, - first_name=self.cleaned_data['first_name'], - last_name=self.cleaned_data['last_name'], - email_address=self.cleaned_data['email'], - author_order=0, - submission=self.draft) - - def save_new_draft_info(self): - salt = hashlib.sha1(str(random.random())).hexdigest()[:5] - self.draft.auth_key = hashlib.sha1(salt+self.cleaned_data['email']).hexdigest() - self.draft.status_id = AWAITING_AUTHENTICATION - self.draft.save() - - -class MetaDataForm(AutoPostForm): - - title = forms.CharField(label=u'Title', required=True) - version = forms.CharField(label=u'Version', required=True) - creation_date = forms.DateField(label=u'Creation date', required=True) - pages = forms.IntegerField(label=u'Pages', required=True) - abstract = forms.CharField(label=u'Abstract', widget=forms.Textarea, required=True) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - name = forms.CharField(required=True) - else: - first_name = forms.CharField(label=u'Given name', required=True) - last_name = forms.CharField(label=u'Last name', required=True) - email = forms.EmailField(label=u'Email address', required=True) - comments = forms.CharField(label=u'Comments to the secretariat', widget=forms.Textarea, required=False) - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'name', 'email', 'comments'] - else: - fields = ['title', 'version', 'creation_date', 'pages', 'abstract', 'first_name', 'last_name', 'email', 'comments'] - - def __init__(self, *args, **kwargs): - super(MetaDataForm, self).__init__(*args, **kwargs) - self.set_initials() - self.authors = self.get_initial_authors() + class Meta: + model = Submission + fields = ['title', 'rev', 'document_date', 'pages', 'abstract', 'note'] def get_initial_authors(self): authors=[] if self.is_bound: for key, value in self.data.items(): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - if key.startswith('name_'): - author = {'errors': {}} - index = key.replace('name_', '') - name = value.strip() - if not name: - author['errors']['name'] = 'This field is required' - email = self.data.get('email_%s' % index, '').strip() - if email and not email_re.search(email): - author['errors']['email'] = 'Enter a valid e-mail address' - if name or email: - author.update({'get_full_name': name, - 'email': (name, email), - 'index': index, - }) - authors.append(author) - - else: - if key.startswith('first_name_'): - author = {'errors': {}} - index = key.replace('first_name_', '') - first_name = value.strip() - if not first_name: - author['errors']['first_name'] = 'This field is required' - last_name = self.data.get('last_name_%s' % index, '').strip() - if not last_name: - author['errors']['last_name'] = 'This field is required' - email = self.data.get('email_%s' % index, '').strip() - if email and not email_re.search(email): - author['errors']['email'] = 'Enter a valid e-mail address' - if first_name or last_name or email: - author.update({'first_name': first_name, - 'last_name': last_name, - 'email': ('%s %s' % (first_name, last_name), email), - 'index': index, - }) - authors.append(author) + if key.startswith('name_'): + author = {'errors': {}} + index = key.replace('name_', '') + name = value.strip() + if not name: + author['errors']['name'] = 'This field is required' + email = self.data.get('email_%s' % index, '').strip() + if email and not email_re.search(email): + author['errors']['email'] = 'Enter a valid e-mail address' + if name or email: + author.update({'name': name, + 'email': email, + 'index': index, + }) + authors.append(author) authors.sort(key=lambda x: x['index']) return authors - def set_initials(self): - self.fields['pages'].initial=self.draft.txt_page_count - self.fields['creation_date'].initial=self.draft.creation_date - self.fields['version'].initial=self.draft.revision - self.fields['abstract'].initial=self.draft.abstract - self.fields['title'].initial=self.draft.id_document_name + def clean_rev(self): + rev = self.cleaned_data["rev"] - def clean_creation_date(self): - creation_date = self.cleaned_data.get('creation_date', None) - if not creation_date: - return None - submit_date = self.draft.submission_date - if (creation_date + datetime.timedelta(days=3) < submit_date or - creation_date - datetime.timedelta(days=3) > submit_date): - raise forms.ValidationError('Creation Date must be within 3 days of submission date') - return creation_date + if len(rev) == 1: + rev = "0" + rev - def clean_version(self): - version = self.cleaned_data.get('version', None) - if not version: - return None - if len(version) > 2: - raise forms.ValidationError('Version field is not in NN format') - try: - version_int = int(version) - except ValueError: - raise forms.ValidationError('Version field is not in NN format') - if version_int > 99 or version_int < 0: - raise forms.ValidationError('Version must be set between 00 and 99') - existing_revisions = [int(i.revision_display()) for i in InternetDraft.objects.filter(filename=self.draft.filename)] - expected = 0 - if existing_revisions: - expected = max(existing_revisions) + 1 - if version_int != expected: - raise forms.ValidationError('Invalid Version Number (Version %02d is expected)' % expected) - return version + error = validate_submission_rev(self.instance.name, rev) + if error: + raise forms.ValidationError(error) - def clean(self): - if bool([i for i in self.authors if i['errors']]): - raise forms.ValidationError('Please fix errors in author list') - return super(MetaDataForm, self).clean() + return rev - def get_authors(self): - if not self.is_bound: - return self.validation.get_authors() - else: - return self.authors - - def move_docs(self, draft, revision): - old_revision = draft.revision - for ext in draft.file_type.split(','): - source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (draft.filename, old_revision, ext)) - dest = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (draft.filename, revision, ext)) - os.rename(source, dest) - - def save_new_draft_info(self): - draft = self.draft - draft.id_document_name = self.cleaned_data['title'] - if draft.revision != self.cleaned_data['version']: - self.move_docs(draft, self.cleaned_data['version']) - draft.revision = self.cleaned_data['version'] - draft.creation_date = self.cleaned_data['creation_date'] - draft.txt_page_count = self.cleaned_data['pages'] - draft.abstract = self.cleaned_data['abstract'] - draft.comment_to_sec = self.cleaned_data['comments'] - draft.status_id = MANUAL_POST_REQUESTED - draft.save() - - # sync authors - draft.tempidauthors_set.all().delete() - - self.save_submitter_info() # submitter is author 0 - - for i, author in enumerate(self.authors): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # save full name - TempIdAuthors.objects.create( - id_document_tag=draft.temp_id_document_tag, - first_name=author["get_full_name"], - email_address=author["email"][1], - author_order=i + 1, - submission=draft) - - def save(self, request): - self.save_new_draft_info() - self.send_mail_to_secretariat(request) - - def send_mail_to_secretariat(self, request): - subject = 'Manual Post Requested for %s' % self.draft.filename - from_email = settings.IDSUBMIT_FROM_EMAIL - to_email = settings.IDSUBMIT_TO_EMAIL - cc = [self.cleaned_data['email']] - cc += [i['email'][1] for i in self.authors] - if self.draft.group_acronym: - cc += [i.person.email()[1] for i in self.draft.group_acronym.wgchair_set.all()] - cc = list(set(cc)) - submitter = self.draft.tempidauthors_set.get(author_order=0) - send_mail(request, to_email, from_email, subject, 'submit/manual_post_mail.txt', { - 'form': self, - 'draft': self.draft, - 'url': settings.IDTRACKER_BASE_URL + urlreverse('draft_status', kwargs=dict(submission_id=self.draft.submission_id)), - 'submitter': submitter - }, - cc=cc) + def clean_document_date(self): + document_date = self.cleaned_data['document_date'] + error = validate_submission_document_date(self.instance.submission_date, document_date) + if error: + raise forms.ValidationError(error) + return document_date class PreapprovalForm(forms.Form): name = forms.CharField(max_length=255, required=True, label="Pre-approved name", initial="draft-ietf-") @@ -588,11 +290,11 @@ class PreapprovalForm(forms.Form): raise forms.ValidationError("Name ends with a dash.") acronym = components[2] if acronym not in self.groups.values_list('acronym', flat=True): - raise forms.ValidationError("WG acronym not recognized as one you can approve drafts for.") + raise forms.ValidationError("Group acronym not recognized as one you can approve drafts for.") if Preapproval.objects.filter(name=n): raise forms.ValidationError("Pre-approval for this name already exists.") - if IdSubmissionDetail.objects.filter(status__in=[POSTED, POSTED_BY_SECRETARIAT ], filename=n): + if Submission.objects.filter(state="posted", name=n): raise forms.ValidationError("A draft with this name has already been submitted and accepted. A pre-approval would not make any difference.") return n diff --git a/ietf/submit/generate_fixtures.py b/ietf/submit/generate_fixtures.py deleted file mode 100755 index cbda2392d..000000000 --- a/ietf/submit/generate_fixtures.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/python - -# boiler plate -import os, sys - -ietf_path = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../ietf')) - -sys.path.insert(0, ietf_path) - -from django.core.management import setup_environ -import settings -setup_environ(settings) - -# script -from django.core.serializers import serialize -from django.db.models import Q - -def output(name, qs): - try: - f = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures/%s.xml" % name), 'w') - f.write(serialize("xml", qs, indent=4)) - f.close() - except: - from django.db import connection - from pprint import pprint - pprint(connection.queries) - raise - -# pick all name models directly out of the module -names = [] - -from ietf.submit.models import IdSubmissionStatus - -output("idsubmissionstatus", IdSubmissionStatus.objects.all()) diff --git a/ietf/submit/mail.py b/ietf/submit/mail.py new file mode 100644 index 000000000..42ddeff3f --- /dev/null +++ b/ietf/submit/mail.py @@ -0,0 +1,152 @@ +from django.conf import settings +from django.core.urlresolvers import reverse as urlreverse +from django.contrib.sites.models import Site +from django.template.loader import render_to_string + +from ietf.utils.mail import send_mail, send_mail_message +from ietf.doc.models import Document +from ietf.person.models import Person +from ietf.group.models import Role +from ietf.message.models import Message +from ietf.utils.accesstoken import generate_access_token + +def submission_confirmation_email_list(submission): + try: + doc = Document.objects.get(name=submission.name) + email_list = [i.author.formatted_email() for i in doc.documentauthor_set.all()] + except Document.DoesNotExist: + email_list = [u"%s <%s>" % (author["name"], author["email"]) + for author in submission.authors_parsed() if author["email"]] + if submission.submitter_parsed()["email"] and submission.submitter not in email_list: + email_list.append(submission.submitter) + return email_list + +def send_submission_confirmation(request, submission): + subject = 'Confirm submission of I-D %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = submission_confirmation_email_list(submission) + + confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_token=generate_access_token(submission.auth_key))) + status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_token=submission.access_token())) + + send_mail(request, to_email, from_email, subject, 'submit/confirm_submission.txt', { + 'submission': submission, + 'confirm_url': confirm_url, + 'status_url': status_url, + }) + + return to_email + +def send_full_url(request, submission): + subject = 'Full URL for managing submission of draft %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = submission_confirmation_email_list(submission) + url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_token=submission.access_token())) + + send_mail(request, to_email, from_email, subject, 'submit/full_url.txt', { + 'submission': submission, + 'url': url, + }) + + return to_email + +def send_approval_request_to_group(request, submission): + subject = 'New draft waiting for approval: %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = [r.formatted_email() for r in Role.objects.filter(group=submission.group, name="chair").select_related("email", "person")] + if not to_email: + return to_email + + send_mail(request, to_email, from_email, subject, 'submit/approval_request.txt', { + 'submission': submission, + 'domain': Site.objects.get_current().domain, + }) + + return to_email + +def send_manual_post_request(request, submission, errors): + subject = u'Manual Post Requested for %s' % submission.name + from_email = settings.IDSUBMIT_FROM_EMAIL + to_email = settings.IDSUBMIT_TO_EMAIL + + cc = [submission.submitter] + cc += [u"%s <%s>" % (author["name"], author["email"]) + for author in submission.authors_parsed() if author["email"]] + if submission.group: + cc += [r.formatted_email() for r in Role.objects.filter(group=submission.group, name="chair").select_related("email", "person")] + cc = list(set(cc)) + + send_mail(request, to_email, from_email, subject, 'submit/manual_post_request.txt', { + 'submission': submission, + 'url': settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status', kwargs=dict(submission_id=submission.pk)), + 'errors': errors, + }, cc=cc) + + +def announce_to_lists(request, submission): + m = Message() + m.by = Person.objects.get(name="(System)") + if request.user.is_authenticated(): + try: + m.by = request.user.get_profile() + except Person.DoesNotExist: + pass + m.subject = 'I-D Action: %s-%s.txt' % (submission.name, submission.rev) + m.frm = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + m.to = settings.IDSUBMIT_ANNOUNCE_LIST_EMAIL + if submission.group and submission.group.list_email: + m.cc = submission.group.list_email + m.body = render_to_string('submit/announce_to_lists.txt', + dict(submission=submission, + settings=settings)) + m.save() + m.related_docs.add(Document.objects.get(name=submission.name)) + + send_mail_message(request, m) + + +def announce_new_version(request, submission, draft, state_change_msg): + to_email = [] + if draft.notify: + to_email.append(draft.notify) + if draft.ad: + to_email.append(draft.ad.role_email("ad").address) + + if draft.stream_id == "iab": + to_email.append("IAB Stream ") + elif draft.stream_id == "ise": + to_email.append("Independent Submission Editor ") + elif draft.stream_id == "irtf": + to_email.append("IRSG ") + + # if it has been sent to the RFC Editor, keep them in the loop + if draft.get_state_slug("draft-iesg") in ("ann", "rfcqueue"): + to_email.append("RFC Editor ") + + active_ballot = draft.active_ballot() + if active_ballot: + for ad, pos in active_ballot.active_ad_positions().iteritems(): + if pos and pos.pos_id == "discuss": + to_email.append(ad.role_email("ad").address) + + if to_email: + subject = 'New Version Notification - %s-%s.txt' % (submission.name, submission.rev) + from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + send_mail(request, to_email, from_email, subject, 'submit/announce_new_version.txt', + {'submission': submission, + 'msg': state_change_msg}) + +def announce_to_authors(request, submission): + authors = [u"%s <%s>" % (author["name"], author["email"]) for author in submission.authors_parsed() if author["email"]] + to_email = list(set(submission_confirmation_email_list(submission) + authors)) + from_email = settings.IDSUBMIT_ANNOUNCE_FROM_EMAIL + subject = 'New Version Notification for %s-%s.txt' % (submission.name, submission.rev) + if submission.group: + group = submission.group.acronym + elif submission.name.startswith('draft-iesg'): + group = 'IESG' + else: + group = 'Individual Submission' + send_mail(request, to_email, from_email, subject, 'submit/announce_to_authors.txt', + {'submission': submission, + 'group': group}) diff --git a/ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py b/ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py new file mode 100644 index 000000000..4030ab46b --- /dev/null +++ b/ietf/submit/migrations/0003_turn_nulls_into_empty_strings.py @@ -0,0 +1,297 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + nullable_string_fields = [ + "id_document_name", + "comment_to_sec", + "filename", + "revision", + "replaces", + "file_type", + "abstract", + "idnits_message", + "first_two_pages", + "remote_ip", + "auth_key", + "submission_hash", + ] + + for f in nullable_string_fields: + orm.IdSubmissionDetail.objects.filter(**{ f: None}).update(**{ f: ""}) + + def backwards(self, orm): + pass + + + 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.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.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.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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', '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'}) + }, + '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'}) + }, + 'group.ietfwg': { + 'Meta': {'object_name': 'IETFWG', 'db_table': "'group_group'", '_ormbases': ['group.Group'], 'proxy': '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'}), + 'revname': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + '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'}) + }, + 'submit.idsubmissiondetail': { + 'Meta': {'object_name': 'IdSubmissionDetail'}, + 'abstract': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'comment_to_sec': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'error_message': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'filename': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'filesize': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'group_acronym': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group.Group']", 'null': 'True', 'blank': 'True'}), + 'id_document_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'idnits_failed': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'invalid_version': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'last_updated_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_updated_time': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}), + 'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'revision': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}), + 'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'submission_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'submission_hash': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'temp_id_document_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'txt_page_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'warning_message': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'wg_submission': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'submit.idsubmissionstatus': { + 'Meta': {'object_name': 'IdSubmissionStatus'}, + 'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + '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'}) + }, + 'submit.tempidauthors': { + 'Meta': {'object_name': 'TempIdAuthors'}, + 'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'id_document_tag': ('django.db.models.fields.IntegerField', [], {}), + 'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionDetail']"}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py b/ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py new file mode 100644 index 000000000..298abaf72 --- /dev/null +++ b/ietf/submit/migrations/0004_fixup_idsubmissiondetail_fields.py @@ -0,0 +1,327 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + depends_on = ( + ("name", "0019_populate_draftsubmissionstate"), + ) + + def forwards(self, orm): + db.rename_table('submit_idsubmissiondetail', 'submit_submission') + + db.rename_column('submit_submission', 'submission_id', "id") + + db.rename_column('submit_submission', 'id_document_name', "title") + db.alter_column('submit_submission', 'title', self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True)) + db.rename_column('submit_submission', 'creation_date', "document_date") + db.rename_column('submit_submission', 'group_acronym_id', 'group_id') + db.rename_column('submit_submission', 'txt_page_count', 'pages') + db.rename_column('submit_submission', 'comment_to_sec', 'note') + db.alter_column('submit_submission', 'note', self.gf('django.db.models.fields.TextField')(default='', blank=True)) + db.rename_column('submit_submission', 'filename', 'name') + db.alter_column('submit_submission', 'name', self.gf('django.db.models.fields.CharField')(db_index=True, default='', max_length=255, blank=True)) + db.rename_column('submit_submission', 'filesize', 'file_size') + db.rename_column('submit_submission', 'revision', 'rev') + db.alter_column('submit_submission', 'rev', self.gf('django.db.models.fields.CharField')(default='', max_length=3, blank=True)) + + db.delete_column('submit_submission', 'idnits_failed') + db.delete_column('submit_submission', 'invalid_version') + db.delete_column('submit_submission', 'temp_id_document_tag') + db.delete_column('submit_submission', 'error_message') + db.delete_column('submit_submission', 'wg_submission') + db.delete_column('submit_submission', 'warning_message') + db.delete_column('submit_submission', 'last_updated_date') + db.delete_column('submit_submission', 'last_updated_time') + + db.add_column('submit_submission', 'state', self.gf('django.db.models.fields.related.ForeignKey')(default='uploaded', to=orm['name.DraftSubmissionStateName'])) + db.add_column('submit_submission', 'authors', self.gf('django.db.models.fields.TextField')(default="")) + db.add_column('submit_submission', 'submitter', self.gf('django.db.models.fields.CharField')(max_length=255, default="")) + + db.alter_column('submit_submission', 'submission_date', self.gf('django.db.models.fields.DateField')()) + + db.alter_column('submit_submission', 'replaces', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) + + db.rename_column('submit_submission', 'file_type', 'file_types') + db.alter_column('submit_submission', 'file_types', self.gf('django.db.models.fields.CharField')(default='', max_length=50)) + + db.alter_column('submit_submission', 'abstract', self.gf('django.db.models.fields.TextField')(default='')) + + db.alter_column('submit_submission', 'idnits_message', self.gf('django.db.models.fields.TextField')(default='')) + + db.alter_column('submit_submission', 'first_two_pages', self.gf('django.db.models.fields.TextField')(default='')) + + db.alter_column('submit_submission', 'remote_ip', self.gf('django.db.models.fields.CharField')(default='', max_length=100)) + + db.alter_column('submit_submission', 'auth_key', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) + + db.rename_column('submit_submission', 'submission_hash', "access_key") + db.alter_column('submit_submission', 'access_key', self.gf('django.db.models.fields.CharField')(default='', max_length=255)) + + db.create_table('submit_submissionevent', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('submission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.Submission'])), + ('time', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)), + ('by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['person.Person'], null=True, blank=True)), + ('desc', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal('submit', ['SubmissionEvent']) + + def backwards(self, orm): + pass + + + 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.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'}), + '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.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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', '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'}) + }, + '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.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.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + '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'}) + }, + 'submit.submission': { + 'Meta': {'object_name': 'Submission'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'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'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'default': "'uploaded'", 'to': "orm['name.DraftSubmissionStateName']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}), + 'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'submit.submissionevent': { + 'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.idsubmissionstatus': { + 'Meta': {'object_name': 'IdSubmissionStatus'}, + 'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + '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'}) + }, + 'submit.tempidauthors': { + 'Meta': {'object_name': 'TempIdAuthors'}, + 'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'id_document_tag': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/migrations/0005_fill_in_new_fields.py b/ietf/submit/migrations/0005_fill_in_new_fields.py new file mode 100644 index 000000000..0c47a81c3 --- /dev/null +++ b/ietf/submit/migrations/0005_fill_in_new_fields.py @@ -0,0 +1,392 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models + +class Migration(DataMigration): + + def forwards(self, orm): + import hashlib, random, time + from django.conf import settings + + state_map = dict((n.slug, n) for n in orm["name.DraftSubmissionStateName"].objects.all()) + status_map = { + -4: "cancel", + -2: "posted", # Secretariat + -1: "posted", + 1: "uploaded", + 4: "auth", + 5: "manual", + 10: "grp-appr", + } + + from django.core.validators import validate_email, ValidationError + + for d in orm.Submission.objects.all().iterator(): + if not d.name: + # get rid of a few mishaps that seem to have been + # accepted without a name + d.delete() + continue + + # map state + state = state_map[status_map.get(d.status_id, "cancel")] + + # map authors + authors = [] + submitter = "" + submitter_email = "" + + for a in d.tempidauthors_set.order_by("author_order"): + parts = (a.first_name or '', a.middle_initial or '', a.last_name or '', a.name_suffix or '') + name = u" ".join(x.strip() for x in parts if x.strip()) + email = a.email_address + orig = email + + # clean + name = name.replace("\n", "").replace("\r", "").replace("<", "").replace(">", "").strip() + email = email.replace("Email:", "").replace("E-mail:", "").replace("mailto:", "").replace(">", "").replace("<", "").replace("\n", "").replace(" ", "").rstrip(",").lstrip(":").strip(".").rstrip(";").rstrip("-").rstrip("\"").lstrip("\"").rstrip("@") + + if email: + line = u"%s <%s>" % (name, email) + else: + line = name + + if a.author_order == 0: + submitter = line + submitter_email = email + else: + authors.append(line) + + # make sure we always have a key + access_key = d.access_key + if not access_key: + access_key = hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random()) + str(d.name.encode("utf-8"))).hexdigest()[:32] + + + # fill in submission event + submitter_person = None + if d.submitter_tag: + try: + submitter_person = orm["person.Person"].objects.get(id=d.submitter_tag) + except models.ObjectDoesNotExist: + pass + + if submitter_email: + try: + submitter_person = orm["person.Person"].objects.get(email__address=submitter_email) + except models.ObjectDoesNotExist: + pass + + if submitter_person and not submitter: + submitter = submitter_person.name + + if submitter_person: + orm.SubmissionEvent.objects.get_or_create( + submission=d, + time=d.submission_date, + by=submitter_person, + desc="Uploaded submission", + ) + + + # fill in manual post events + if d.status_id == -2 and d.man_posted_by: + if d.man_posted_by == "Amy Vezza": + d.man_posted_by = "Amy K. Vezza" + + try: + by = orm["person.Person"].objects.get(name=d.man_posted_by) + orm.SubmissionEvent.objects.get_or_create( + submission=d, + time=d.man_posted_date or d.submission_date, + by=by, + desc="Posted submission manually", + ) + except models.ObjectDoesNotExist: + pass + + # update the new revision doc events that are set to + # "(System)" with our newly discovered submitter + if submitter_person and d.state_id == "posted": + orm["doc.NewRevisionDocEvent"].objects.filter(doc=d.name, rev=d.rev, type="new_revision", by=0).update(by=submitter_person) + + # update the submission itself + orm.Submission.objects.filter(pk=d.pk).update( + submitter=submitter, + state=state, + authors="\n".join(authors), + access_key=access_key, + ) + + + def backwards(self, orm): + "Write your backwards methods here." + + + 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.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'}), + '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.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', 'db_index': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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.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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', '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'}) + }, + '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.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.draftsubmissionstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DraftSubmissionStateName'}, + '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'}) + }, + 'submit.submission': { + 'Meta': {'object_name': 'Submission'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'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'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'man_posted_by': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'man_posted_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'default': "'uploaded'", 'to': "orm['name.DraftSubmissionStateName']"}), + 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.IdSubmissionStatus']", 'null': 'True', 'db_column': "'status_id'", 'blank': 'True'}), + 'sub_email_priority': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'submitter_tag': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'submit.submissionevent': { + 'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + }, + 'submit.idsubmissionstatus': { + 'Meta': {'object_name': 'IdSubmissionStatus'}, + 'status_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'status_value': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + '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'}) + }, + 'submit.tempidauthors': { + 'Meta': {'object_name': 'TempIdAuthors'}, + 'author_order': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'id_document_tag': ('django.db.models.fields.IntegerField', [], {'default': '-1'}), + 'last_modified_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'last_modified_time': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py b/ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py new file mode 100644 index 000000000..7269d3c93 --- /dev/null +++ b/ietf/submit/migrations/0006_auto__del_tempidauthors__del_idsubmissionstatus__del_field_submission_.py @@ -0,0 +1,301 @@ +# 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 'TempIdAuthors' + db.delete_table('submit_tempidauthors') + + # Deleting model 'IdSubmissionStatus' + db.delete_table('submit_idsubmissionstatus') + + # Deleting field 'Submission.man_posted_date' + db.delete_column('submit_submission', 'man_posted_date') + + # Deleting field 'Submission.status' + db.delete_column('submit_submission', 'status_id') + + # Deleting field 'Submission.submitter_tag' + db.delete_column('submit_submission', 'submitter_tag') + + # Deleting field 'Submission.sub_email_priority' + db.delete_column('submit_submission', 'sub_email_priority') + + # Deleting field 'Submission.man_posted_by' + db.delete_column('submit_submission', 'man_posted_by') + + + def backwards(self, orm): + + # Adding model 'TempIdAuthors' + db.create_table('submit_tempidauthors', ( + ('last_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('last_modified_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True)), + ('id_document_tag', self.gf('django.db.models.fields.IntegerField')(default=-1)), + ('last_modified_time', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('email_address', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('middle_initial', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('first_name', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('submission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.Submission'])), + ('name_suffix', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)), + ('author_order', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)), + )) + db.send_create_signal('submit', ['TempIdAuthors']) + + # Adding model 'IdSubmissionStatus' + db.create_table('submit_idsubmissionstatus', ( + ('status_value', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('status_id', self.gf('django.db.models.fields.IntegerField')(primary_key=True)), + )) + db.send_create_signal('submit', ['IdSubmissionStatus']) + + # Adding field 'Submission.man_posted_date' + db.add_column('submit_submission', 'man_posted_date', self.gf('django.db.models.fields.DateField')(null=True, blank=True), keep_default=False) + + # Adding field 'Submission.status' + db.add_column('submit_submission', 'status', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['submit.IdSubmissionStatus'], null=True, db_column='status_id', blank=True), keep_default=False) + + # Adding field 'Submission.submitter_tag' + db.add_column('submit_submission', 'submitter_tag', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False) + + # Adding field 'Submission.sub_email_priority' + db.add_column('submit_submission', 'sub_email_priority', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True), keep_default=False) + + # Adding field 'Submission.man_posted_by' + db.add_column('submit_submission', 'man_posted_by', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True), keep_default=False) + + + 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.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'}), + '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.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', [], {'symmetrical': 'False', 'related_name': "'previous_states'", 'blank': 'True', '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'}) + }, + '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.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.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': "orm['name.DraftSubmissionStateName']"}), + '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'}) + }, + 'submit.preapproval': { + 'Meta': {'object_name': 'Preapproval'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']"}), + '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'}) + }, + 'submit.submission': { + 'Meta': {'object_name': 'Submission'}, + 'abstract': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'access_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'auth_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'authors': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'document_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_types': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), + 'first_two_pages': ('django.db.models.fields.TextField', [], {'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'}), + 'idnits_message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'note': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'pages': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'remote_ip': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'replaces': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'rev': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), + 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['name.DraftSubmissionStateName']"}), + 'submission_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'submitter': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'submit.submissionevent': { + 'Meta': {'ordering': "('-time', '-id')", 'object_name': 'SubmissionEvent'}, + 'by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['person.Person']", 'null': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'submission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['submit.Submission']"}), + 'time': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) + } + } + + complete_apps = ['submit'] diff --git a/ietf/submit/models.py b/ietf/submit/models.py index e73a91611..27184fe45 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -1,85 +1,80 @@ -import re, datetime # +import re, datetime, hashlib from django.conf import settings from django.db import models -from django.utils.hashcompat import md5_constructor -from ietf.idtracker.models import InternetDraft, IETFWG from ietf.person.models import Person +from ietf.group.models import Group +from ietf.name.models import DraftSubmissionStateName +from ietf.utils.accesstoken import generate_random_key, generate_access_token -class IdSubmissionStatus(models.Model): - status_id = models.IntegerField(primary_key=True) - status_value = models.CharField(blank=True, max_length=255) +def parse_email_line(line): + """Split line on the form 'Some Name '""" + m = re.match("([^<]+) <([^>]+)>$", line) + if m: + return dict(name=m.group(1), email=m.group(2)) + else: + return dict(name=line, email="") + +class Submission(models.Model): + state = models.ForeignKey(DraftSubmissionStateName) + remote_ip = models.CharField(max_length=100, blank=True) + + access_key = models.CharField(max_length=255, default=generate_random_key) + auth_key = models.CharField(max_length=255, blank=True) + + # draft metadata + name = models.CharField(max_length=255, db_index=True) + group = models.ForeignKey(Group, null=True, blank=True) + title = models.CharField(max_length=255, blank=True) + abstract = models.TextField(blank=True) + rev = models.CharField(max_length=3, blank=True) + pages = models.IntegerField(null=True, blank=True) + authors = models.TextField(blank=True, help_text="List of author names and emails, one author per line, e.g. \"John Doe <john@example.org>\"") + note = models.TextField(blank=True) + replaces = models.CharField(max_length=255, blank=True) + + first_two_pages = models.TextField(blank=True) + file_types = models.CharField(max_length=50, blank=True) + file_size = models.IntegerField(null=True, blank=True) + document_date = models.DateField(null=True, blank=True) + submission_date = models.DateField(default=datetime.date.today) + + submitter = models.CharField(max_length=255, blank=True, help_text="Name and email of submitter, e.g. \"John Doe <john@example.org>\"") + + idnits_message = models.TextField(blank=True) def __unicode__(self): - return self.status_value + return u"%s-%s" % (self.name, self.rev) -class IdSubmissionDetail(models.Model): - submission_id = models.AutoField(primary_key=True) - temp_id_document_tag = models.IntegerField(null=True, blank=True) - status = models.ForeignKey(IdSubmissionStatus, db_column='status_id', null=True, blank=True) - last_updated_date = models.DateField(null=True, blank=True) - last_updated_time = models.CharField(null=True, blank=True, max_length=25) - id_document_name = models.CharField(null=True, blank=True, max_length=255) - group_acronym = models.ForeignKey(IETFWG, null=True, blank=True) - filename = models.CharField(null=True, blank=True, max_length=255, db_index=True) - creation_date = models.DateField(null=True, blank=True) - submission_date = models.DateField(null=True, blank=True) - remote_ip = models.CharField(null=True, blank=True, max_length=100) - revision = models.CharField(null=True, blank=True, max_length=3) - submitter_tag = models.IntegerField(null=True, blank=True) - auth_key = models.CharField(null=True, blank=True, max_length=255) - idnits_message = models.TextField(null=True, blank=True) - file_type = models.CharField(null=True, blank=True, max_length=50) - comment_to_sec = models.TextField(null=True, blank=True) - abstract = models.TextField(null=True, blank=True) - txt_page_count = models.IntegerField(null=True, blank=True) - error_message = models.CharField(null=True, blank=True, max_length=255) - warning_message = models.TextField(null=True, blank=True) - wg_submission = models.IntegerField(null=True, blank=True) - filesize = models.IntegerField(null=True, blank=True) - man_posted_date = models.DateField(null=True, blank=True) - man_posted_by = models.CharField(null=True, blank=True, max_length=255) - first_two_pages = models.TextField(null=True, blank=True) - sub_email_priority = models.IntegerField(null=True, blank=True) - invalid_version = models.IntegerField(null=True, blank=True) - idnits_failed = models.IntegerField(null=True, blank=True) - submission_hash = models.CharField(null=True, blank=True, max_length=255) + def authors_parsed(self): + res = [] + for line in self.authors.replace("\r", "").split("\n"): + line = line.strip() + if line: + res.append(parse_email_line(line)) + return res + + def submitter_parsed(self): + return parse_email_line(self.submitter) + + def access_token(self): + return generate_access_token(self.access_key) + + +class SubmissionEvent(models.Model): + submission = models.ForeignKey(Submission) + time = models.DateTimeField(default=datetime.datetime.now) + by = models.ForeignKey(Person, null=True, blank=True) + desc = models.TextField() def __unicode__(self): - return u"%s-%s" % (self.filename, self.revision) + return u"%s %s by %s at %s" % (self.submission.name, self.desc, self.by.plain_name() if self.by else "(unknown)", self.time) - def create_hash(self): - self.submission_hash = md5_constructor(settings.SECRET_KEY + self.filename).hexdigest() + class Meta: + ordering = ("-time", "-id") - def get_hash(self): - if not self.submission_hash: - self.create_hash() - self.save() - return self.submission_hash - def draft_link(self): - if self.status_id == -1: - return '%s' % (self.filename, self.revision, self.filename) - else: - return self.filename - draft_link.allow_tags = True - def status_link(self): - return '%s' % (self.submission_id, self.submission_hash, self.status) - status_link.allow_tags = True - - def confirmation_email_list(self): - try: - draft = InternetDraft.objects.get(filename=self.filename) - email_list = list(set(u'%s <%s>' % (i.person.ascii, i.email()) for i in draft.authors)) - except InternetDraft.DoesNotExist: - email_list = list(set(u'%s <%s>' % i.email() for i in self.tempidauthors_set.all())) - return email_list - -def create_submission_hash(sender, instance, **kwargs): - instance.create_hash() - -models.signals.pre_save.connect(create_submission_hash, sender=IdSubmissionDetail) class Preapproval(models.Model): """Pre-approved draft submission name.""" @@ -89,29 +84,3 @@ class Preapproval(models.Model): def __unicode__(self): return self.name - -class TempIdAuthors(models.Model): - id_document_tag = models.IntegerField() - first_name = models.CharField(blank=True, max_length=255) # with new schema, this contains the full name while the other name fields are empty to avoid loss of information - last_name = models.CharField(blank=True, max_length=255) - email_address = models.CharField(blank=True, max_length=255) - last_modified_date = models.DateField(null=True, blank=True) - last_modified_time = models.CharField(blank=True, max_length=100) - author_order = models.IntegerField(null=True, blank=True) - submission = models.ForeignKey(IdSubmissionDetail) - middle_initial = models.CharField(blank=True, max_length=255, null=True) - name_suffix = models.CharField(blank=True, max_length=255, null=True) - - class Meta: - if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - db_table = 'temp_id_authors' - - def email(self): - return (self.get_full_name(), self.email_address) - - def get_full_name(self): - parts = (self.first_name or '', self.middle_initial or '', self.last_name or '', self.name_suffix or '') - return u" ".join(x.strip() for x in parts if x.strip()) - - def __unicode__(self): - return u"%s <%s>" % self.email() diff --git a/ietf/submit/parsers/base.py b/ietf/submit/parsers/base.py index 7dd8618ae..53c0afa38 100644 --- a/ietf/submit/parsers/base.py +++ b/ietf/submit/parsers/base.py @@ -1,26 +1,28 @@ import re - -CUTOFF_HOUR = 17 - - -class MetaDataDraft(object): - revision = None - filename = None +class MetaData(object): + rev = None + name = None group = None - filesize = None + file_size = None first_two_pages = None - page_count = None + pages = None submission_date = None - creation_date = None + document_date = None authors = None class ParseInfo(object): + """Collect errors from a parse""" def __init__(self): self.errors = [] + # warnings are currently unused by the parsers self.warnings = {} - self.metadraft = MetaDataDraft() + # the metadata fields are currently unused, i.e. the plain + # text parser fills in some fields but they are not used + # anywhere (instead the draft parser is used for .txt and the + # other file types have no actual parsing at the moment) + self.metadata = MetaData() def add_error(self, error_str): self.errors.append(error_str) diff --git a/ietf/submit/parsers/plain_parser.py b/ietf/submit/parsers/plain_parser.py index 40e92c054..fb1e4c723 100644 --- a/ietf/submit/parsers/plain_parser.py +++ b/ietf/submit/parsers/plain_parser.py @@ -2,12 +2,9 @@ import datetime import re from django.conf import settings -from ietf.idtracker.models import InternetDraft, IETFWG from django.template.defaultfilters import filesizeformat from ietf.submit.parsers.base import FileParser -NONE_WG_PK = 1027 - class PlainParser(FileParser): @@ -20,14 +17,14 @@ class PlainParser(FileParser): super(PlainParser, self).critical_parse() self.parse_max_size() self.parse_file_charset() - self.parse_filename() + self.parse_name() return self.parsed_info def parse_max_size(self): - if self.fd.size > settings.MAX_PLAIN_DRAFT_SIZE: - self.parsed_info.add_error('File size is larger than %s' % filesizeformat(settings.MAX_PLAIN_DRAFT_SIZE)) - self.parsed_info.metadraft.filesize = self.fd.size - self.parsed_info.metadraft.submission_date = datetime.date.today() + if self.fd.size > settings.IDSUBMIT_MAX_PLAIN_DRAFT_SIZE: + self.parsed_info.add_error('File size is larger than %s' % filesizeformat(settings.IDSUBMIT_MAX_PLAIN_DRAFT_SIZE)) + self.parsed_info.metadata.file_size = self.fd.size + self.parsed_info.metadata.submission_date = datetime.date.today() def parse_file_charset(self): import magic @@ -43,9 +40,9 @@ class PlainParser(FileParser): magic.magic_load(m.cookie, None) filetype = m.from_buffer(content) if not 'ascii' in filetype: - self.parsed_info.add_error('A plain text document must be submitted.') + self.parsed_info.add_error('A plain text ASCII document must be submitted.') - def parse_filename(self): + def parse_name(self): self.fd.file.seek(0) draftre = re.compile('(draft-\S+)') revisionre = re.compile('.*-(\d+)$') @@ -56,24 +53,24 @@ class PlainParser(FileParser): match = draftre.search(line) if not match: continue - filename = match.group(1) - filename = re.sub('^[^\w]+', '', filename) - filename = re.sub('[^\w]+$', '', filename) - filename = re.sub('\.txt$', '', filename) - extra_chars = re.sub('[0-9a-z\-]', '', filename) + name = match.group(1) + name = re.sub('^[^\w]+', '', name) + name = re.sub('[^\w]+$', '', name) + name = re.sub('\.txt$', '', name) + extra_chars = re.sub('[0-9a-z\-]', '', name) if extra_chars: if len(extra_chars) == 1: - self.parsed_info.add_error((u'The filename contains a disallowed character: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + + self.parsed_info.add_error((u'The name contains a disallowed character: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + u'(see http://www.ietf.org/id-info/guidelines.html#naming for details).') else: - self.parsed_info.add_error((u'The filename contains disallowed characters: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + + self.parsed_info.add_error((u'The name contains disallowed characters: %s ' % (', '.join(set(extra_chars))).decode('ascii','replace')) + u'(see http://www.ietf.org/id-info/guidelines.html#naming for details).') - match_revision = revisionre.match(filename) + match_revision = revisionre.match(name) if match_revision: - self.parsed_info.metadraft.revision = match_revision.group(1) + self.parsed_info.metadata.rev = match_revision.group(1) else: - self.parsed_info.add_error(u'The filename found on the first page of the document does not contain a revision: "%s"' % (filename,)) - filename = re.sub('-\d+$', '', filename) - self.parsed_info.metadraft.filename = filename + self.parsed_info.add_error(u'The name found on the first page of the document does not contain a revision: "%s"' % (name,)) + name = re.sub('-\d+$', '', name) + self.parsed_info.metadata.name = name return - self.parsed_info.add_error('The first page of the document does not contain a legitimate filename that start with draft-*') + self.parsed_info.add_error('The first page of the document does not contain a legitimate name that start with draft-*') diff --git a/ietf/submit/templatetags/submit_tags.py b/ietf/submit/templatetags/submit_tags.py index e86142050..5692db021 100644 --- a/ietf/submit/templatetags/submit_tags.py +++ b/ietf/submit/templatetags/submit_tags.py @@ -4,40 +4,37 @@ from django import template from django.conf import settings from django.utils.html import mark_safe, escape -from ietf.submit.utils import POSTED, POSTED_BY_SECRETARIAT - - register = template.Library() @register.inclusion_tag('submit/submission_files.html', takes_context=True) def show_submission_files(context, submission): result = [] - for ext in submission.file_type.split(','): + for ext in submission.file_types.split(','): exists = False - source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.filename, submission.revision, ext)) + source = os.path.join(settings.IDSUBMIT_STAGING_PATH, '%s-%s%s' % (submission.name, submission.rev, ext)) if os.path.exists(source): exists = True - elif submission.status_id in [POSTED, POSTED_BY_SECRETARIAT]: + elif submission.state_id == "posted": continue - result.append({'name': '[%s version ]' % ext[1:].capitalize(), + result.append({'name': '[%s version]' % ext[1:].upper(), 'exists': exists, - 'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.filename, submission.revision, ext)}) + 'url': '%s%s-%s%s' % (settings.IDSUBMIT_STAGING_URL, submission.name, submission.rev, ext)}) return {'files': result} @register.filter -def two_pages_decorated_with_validation(value, validation): - pages = value.first_two_pages or '' - if not 'revision' in validation.warnings.keys(): - return mark_safe('' % escape(pages)) - result = ' +{% endfilter %} +{% endif %} +{% if num == "1.4" %} {% filter linebreaks_crlf %}
-{{ action_items }}
-
{% endfilter %} +{{ section.text }} + +{% endfilter %} +{% endif %} - - - +{% if num >= "2" and num < "5" %} + {% if "doc" in section %} + {% with section.doc as doc %} + {% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/moderator_doc.html" %}{% endif %} + {% if doc.type_id == "conflrev" %}{% include "iesg/moderator_conflict_doc.html" %}{% endif %} + {% if doc.type_id == "charter" %}{% include "iesg/moderator_charter.html" %}{% endif %} + {% endwith %} + {% else %} +

NONE

+ {% endif %} +{% endif %} -{% with "iesg/moderator_doc.html" as doc_template %} -{% with "iesg/moderator_conflict_doc.html" as doc_conflict_template %} -{% include "iesg/agenda_outline_23.html" %} -{% endwith %} -{% endwith %} +{% if num >= "6" and num < "7" %} + {% if num == "6" %} +

NONE

+ {% else %} +

Is there anything that you would like the Secretariat to record in the minutes for this management issue?

- - - +

Decision:

-{% with "iesg/moderator_wg.html" as wg_template %} -{% include "iesg/agenda_outline_4.html" %} -{% endwith %} +

The management issue was discussed.

- - - - -

5. IAB News We Can Use

- - - - - -{% for m in mgmt %} -

6. Management Items
-6.{{forloop.counter}} {{m.title}}

- -

Is there anything that you would like the Secretariat to record in the minutes for this management issue?

- -

Decision:

- -

The management issue was discussed.

- -

Action Item (if applicable):

- -{% empty %} -

6. Management Items

- -

NONE

-{% endfor %} - - - - - -

7. Working Group News

+

Action Item (if applicable):

+ {% endif %} +{% endif %} +{% if num == "7" %} {% filter linebreaks_crlf %}
-{% for name in ad_names %}[ ] {{ name }}
+{% for ad in section.ads %}[ ] {{ ad.plain_name }}
 {% endfor %}
 
{% endfilter %} +{% endif %} - +{% endfor %} + + + diff --git a/ietf/templates/iesg/review_decisions.html b/ietf/templates/iesg/review_decisions.html new file mode 100644 index 000000000..e67f966b9 --- /dev/null +++ b/ietf/templates/iesg/review_decisions.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} + +{% block title %}IESG Review Decisions in {{ timeframe }}{% endblock %} + +{% block morecss %} +div.decisions h3 { margin-bottom: 0.5em; } +div.decisions div { padding-left: 1em; text-indent: -1em; } +{% endblock %} + +{% block content %} +

IESG Review Decisions in {{ timeframe }}

+ +

Showing review decisions announced in {{ timeframe }}.

+ +

Announcements in: + {% for y in years %}{{ y }}{% if not forloop.last %}, {% endif %}{% endfor %} +

+ +
+{% for e in events %} + {% ifchanged %}

{{ e.time|date:"F j, Y" }}

{% endifchanged %} +
{{ e.desc }}: {{ e.doc.name }} {% if e.doc.intended_std_level %}({{ e.doc.intended_std_level }}){% endif %} - {{ e.doc.title }}
+{% endfor %} +
+ +{% endblock %} diff --git a/ietf/templates/iesg/scribe_conflict_doc.html b/ietf/templates/iesg/scribe_conflict_doc.html index 8c618f9aa..8508438d4 100644 --- a/ietf/templates/iesg/scribe_conflict_doc.html +++ b/ietf/templates/iesg/scribe_conflict_doc.html @@ -1,98 +1,23 @@ -{% comment %} -Copyright (C) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). -All rights reserved. Contact: Pasi Eronen +
  • + {{ doc.title }} -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: +
    {{doc.canonical_name}} + [txt] - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +
    + {{ doc.conflictdoc.title }} ({{ doc.conflictdoc.stream }}: {{ doc.conflictdoc.intended_std_level }}) +
    {{ doc.conflictdoc.canonical_name }} + [txt] + {% if doc.conflictdoc.note %} +
    Note: {{ doc.conflictdoc.note|linebreaksbr }} + {% endif %} + {% for ipr in doc.conflictdoc.ipr %} + {% if ipr.ipr.status == 1 %} +
    IPR: {{ ipr.ipr.title }} + {% endif %} + {% endfor %} +
    + Token: {{ doc.ad }} - * 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. -{% endcomment %}{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} -{% if title2_first %} - {% if title1_first %} -

    {{ title1 }}

    - {% endif %} -

    {{ title2 }}

    - {% endif %}

    {{ title3 }}

    - -
      - {% for doc in section_docs %} -
    • - {{ doc.obj.title|escape }} -
      {{doc.obj.canonical_name}} - [txt] -
      - {{ doc.obj.conflictdoc.title|escape }} ({{doc.obj.conflictdoc.stream}}: {{ doc.obj.conflictdoc.intended_std_level }}) -
      {{doc.obj.conflictdoc.canonical_name}} - [txt] - {% if doc.obj.conflictdoc.note %} -
      Note: {{ doc.obj.conflictdoc.note|linebreaksbr }} - {% endif %} - {% for ipr in doc.obj.conflictdoc.ipr %} - {% ifequal ipr.ipr.status 1 %} -
      IPR: {{ ipr.ipr.title|escape }} - {% endifequal %} - {% endfor %} -
      - Token: {{ doc.obj.ad.plain_name|escape }} - {% if doc.obj.active_ballot %} -
      Discusses/comments [ballot]: - - {% endif %} - -

      Telechat:

      -
        -
      • ...
      • -
      • ...
      • -
      • ...
      • -
      • ...
      • -
      • ...
      • -
      + {% include "iesg/scribe_doc_ballot.html" %}
    • -{% empty %} -
    • (none)
    • -{% endfor %} -
    diff --git a/ietf/templates/iesg/scribe_doc.html b/ietf/templates/iesg/scribe_doc.html index 22491e7ea..29b76a018 100644 --- a/ietf/templates/iesg/scribe_doc.html +++ b/ietf/templates/iesg/scribe_doc.html @@ -1,99 +1,25 @@ -{% comment %}{% endcomment %}{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} -{% if title2_first %} - {% if title1_first %} -

    {{ title1 }}

    - {% endif %} -

    {{ title2 }}

    - {% endif %}

    {{ title3 }}

    - -
      - {% for doc in section_docs %} -
    • - {{ doc.obj.title|escape }} ({{ doc.obj.intended_std_level }}) -
      {{doc.obj.canonical_name}} - {% with doc.obj.rfc_number as rfc_number %} - {% if rfc_number %} - [txt] - {% else %} - [txt] - {% endif %} - {% endwith %} -
      Token: {{ doc.obj.ad.plain_name|escape }} ({{doc.obj.area_acronym}} area) - {% if doc.obj.note %} -
      Note: {{ doc.obj.note|linebreaksbr }} - {% endif %} - {% for ipr in doc.obj.ipr %} - {% ifequal ipr.ipr.status 1 %} -
      IPR: {{ ipr.ipr.title|escape }} - {% endifequal %} - {% endfor %} - {% if doc.obj.active_ballot %} -
      Discusses/comments [ballot]: - - {% endif %} - -

      Telechat:

      -
        -
      • ...
      • -
      • ...
      • -
      • ...
      • -
      • ...
      • -
      • ...
      • -
      + {% include "iesg/scribe_doc_ballot.html" %}
    • -{% empty %} -
    • (none)
    • -{% endfor %} -
    diff --git a/ietf/templates/iesg/scribe_doc2.html b/ietf/templates/iesg/scribe_doc2.html deleted file mode 100644 index a33ffd316..000000000 --- a/ietf/templates/iesg/scribe_doc2.html +++ /dev/null @@ -1,57 +0,0 @@ -{% comment %}{% endcomment %}{% comment %} -Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. -{% endcomment %} -{% load ietf_filters %} - - -

    {{ doc.obj.name }}

    -{% autoescape off %} -{% if doc.obj.active_ballot %} - -{% endif%} -{% endautoescape %} \ No newline at end of file diff --git a/ietf/templates/iesg/scribe_doc_ballot.html b/ietf/templates/iesg/scribe_doc_ballot.html new file mode 100644 index 000000000..f00f67f93 --- /dev/null +++ b/ietf/templates/iesg/scribe_doc_ballot.html @@ -0,0 +1,33 @@ + {% with doc.active_ballot as ballot %} + {% if ballot %} +
    Discusses/comments [ballot]: + + {% endif %} + {% endwith %} + +

    Telechat:

    + +
      +
    • ...
    • +
    • ...
    • +
    • ...
    • +
    • ...
    • +
    • ...
    • +
    \ No newline at end of file diff --git a/ietf/templates/iesg/scribe_template.html b/ietf/templates/iesg/scribe_template.html index 78c97dd1b..980089927 100644 --- a/ietf/templates/iesg/scribe_template.html +++ b/ietf/templates/iesg/scribe_template.html @@ -41,40 +41,50 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% load ietf_filters %}{% filter compress_empty_lines %} -

    Scribe template for IESG Narrative Minutes, {{date}}

    +

    Scribe template for IESG Narrative Minutes, {{ date }}

    -{% with "iesg/scribe_doc.html" as doc_template %} -{% with "iesg/scribe_conflict_doc.html" as doc_conflict_template %} -{% include "iesg/agenda_outline_23.html" %} -{% endwith %} -{% endwith %} +{% for num, section in sections %} +

    {{ num }}{% if num|sectionlevel == 1 %}.{% endif %} {{ section.title|safe }}

    + + {% if "docs" in section %} +
      {% for doc in section.docs %} +{% if doc.type_id == "draft" or doc.type_id == "statchg" %}{% include "iesg/scribe_doc.html" %}{% endif %} +{% if doc.type_id == "conflrev" %}{% include "iesg/scribe_conflict_doc.html" %}{% endif %} + {% empty %} +
    • (none)
    • {% endfor %} +
    + {% endif %} +{% endfor %}

    Appendix: Snapshot of discusses/comments

    (at {% now "Y-m-d H:i:s T" %})

    -{% for doc in docs.s211 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s212 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s213 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s221 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s222 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s223 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s231 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s232 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s233 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s311 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s312 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s313 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s321 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s322 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s323 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s331 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s332 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s333 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s341 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s342 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} -{% for doc in docs.s343 %}{% include "iesg/scribe_doc2.html" %}{%endfor%} +{% for doc in appendix_docs %} + +

    {{ doc.name }}

    + + {% with doc.active_ballot as ballot %} + {% if ballot %} + + {% endif %} + {% endwith %} + +{% endfor %} {% endfilter %} diff --git a/ietf/templates/iesg/working_group_actions.html b/ietf/templates/iesg/working_group_actions.html deleted file mode 100644 index 7ab3e7783..000000000 --- a/ietf/templates/iesg/working_group_actions.html +++ /dev/null @@ -1,68 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Working Group Actions{% endblock %} - -{% block morecss %} -.working-group-action .name, -.working-group-action .edit { - padding-left: 10px; -} -.working-group-action form input { - margin-left: 10px; -} -{% endblock %} - -{% block content %} -{% load ietf_filters %} -

    Working Group Actions

    - -

    These are the submitted WG descriptions and milestones for all -Proposed Working Groups and recharter submissions, along with -whatever status information is available.

    - -

    NOTE: Explicit direction by the AD is required to add the group to -an IESG Telechat agenda.

    - -

    Current Items

    - - -{% for wga in current_items %} - - - - {% if user|in_group:"Secretariat" %} - - {% endif %} - -{% endfor %} -
    {{ wga.status_date|date:"Y, M d" }}{{ wga.group_acronym.name }} ({{ wga.group_acronym.acronym }}) -
    - Edit - -
    -
    - -

    Possible Items

    - - -{% for wga in possible_items %} - - - - {% if user|in_group:"Secretariat" %} - - {% endif %} - -{% endfor %} -
    {{ wga.status_date|date:"Y, M d" }}{{ wga.name }} ({{ wga.acronym }}) -
    - - - -
    -
    -{% endblock %} - -{% block content_end %} - -{% endblock %} diff --git a/ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt b/ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt deleted file mode 100644 index dafe2376b..000000000 --- a/ietf/templates/ietfworkflows/annotation_tags_updated_mail.txt +++ /dev/null @@ -1,11 +0,0 @@ -{% autoescape off %} -The annotation tags of document {{ doc }} have been updated. See more information below. - -Annotation tags set: {{ entry.setted }} -Annotation tags reset: {{ entry.unsetted }} -Date of the change: {{ entry.change_date }} -Author of the change: {{ entry.person }} - -Comment: -{{ entry.comment }} -{% endautoescape %} diff --git a/ietf/templates/ietfworkflows/edit_actions.html b/ietf/templates/ietfworkflows/edit_actions.html deleted file mode 100644 index 214905233..000000000 --- a/ietf/templates/ietfworkflows/edit_actions.html +++ /dev/null @@ -1,6 +0,0 @@ -
    - {% for action_name, url in actions %} - {% if not forloop.first %} | {% endif%} {{ action_name }} - {% endfor %} -
    - diff --git a/ietf/templates/ietfworkflows/noworkflow_state_form.html b/ietf/templates/ietfworkflows/noworkflow_state_form.html deleted file mode 100644 index d5e3f0372..000000000 --- a/ietf/templates/ietfworkflows/noworkflow_state_form.html +++ /dev/null @@ -1,23 +0,0 @@ -
    - - - - - -
    Adopt this draft in your group
    -
    - {{ form.comment.errors }} - Comment:
    - -
    -
    - {{ form.weeks.errors }} - Estimated time in 'Call for Adoption by WG/RG Issued': (in weeks) -
    -
    - Adopt in Group: {{ form.group }} - {{ form.group.errors }} -
    - -
    -
    diff --git a/ietf/templates/ietfworkflows/state_edit.html b/ietf/templates/ietfworkflows/state_edit.html deleted file mode 100644 index 5cef5eb00..000000000 --- a/ietf/templates/ietfworkflows/state_edit.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} -{% load ietf_streams %} - -{% block morecss %} -table.state-history p { margin: 0px; } -table.edit-form ul { padding: 0px; list-style-type: none; margin: 0px; margin-bottom: 2em; } -table.edit-form ul li, table.edit-form div.free-change { padding: 0px 2em; } -table.edit-form ul li.evenrow { background-color: #edf5ff; } -table.edit-form textarea { width: 95%; height: 120px; } -table.edit-form span.required { color: red; } -table.edit-form ul.errorlist { border-width: 0px; padding: 0px; margin: 0px;} -table.edit-form ul.errorlist li { color: red; margin: 0px; padding: 0px;} -table.edit-form div.field { margin: 1em 0px; } -table.edit-form div.submit-row { margin: 0px 2em; } -table.edit-form div.error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; } -table.edit-form-tags tr { vertical-align: top; } -table.edit-form-tags textarea { height: 200px; } -table.edit-form-tags ul { border-width: 0px; padding: 0px 2em; } -table.edit-form-tags ul li { padding: 0px; } - -#new-edit-form { clear:both; padding-top: 1em; } -#new-edit-form th { padding-left:0; max-width: 8em;} -#new-edit-form #id_comment { width: 30em; height: 5em; } -#new-edit-form #id_weeks { width: 2em;} -#new-edit-form ul {list-style-type: none; padding-left:0; margin-top:0; margin-bottom:0; } -.rec-state-header { float:left; padding-left:2px; padding-right:.5em; } -.rec-statelist {list-style-type: none; padding-left:0; margin-top:0; margin-bottom:0; } -.rec-state { float:left; padding-right:.5em; } - -{% endblock morecss %} - -{% block title %}Change state for {{ draft }}{% endblock %} - -{% block content %} -

    Change state for {{ draft }}

    - -{% if state and state.slug == "wg-doc" and not milestones %} -

    This document is not part of any milestone. You may wish to add it to one.

    -{% endif %} - -{% if state and state.slug == "sub-pub" and milestones %} -

    This document is part of {% if milestones|length > 1 %}{{ milestones|length }} -milestones{% else %}a milestone{% endif %}. Now that the draft is -submitted to IESG for publication, you may wish to -update the -milestone{{ milestones|pluralize }}.

    -{% endif %} - -{{ form }} - -{% endblock %} diff --git a/ietf/templates/ietfworkflows/state_form.html b/ietf/templates/ietfworkflows/state_form.html deleted file mode 100644 index a96b20f3e..000000000 --- a/ietf/templates/ietfworkflows/state_form.html +++ /dev/null @@ -1,32 +0,0 @@ -
    - -
    - {% with form.get_next_states as next_states %} - {% if next_states %} - Recommended next state{{next_states|pluralize}}: -
      - {% for state in next_states %} -
    • '{{ state.name }}'{% if not forloop.last %}, {% endif %}
    • - {% endfor %} -
    - {% endif %} - {% endwith %} -
    - - - {% for field in form.visible_fields %} - - - - - {% endfor %} -
    {{ field.label_tag }}:{{ field }} - {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} - - {{ field.errors }} -
    -
    - - -
    -
    diff --git a/ietf/templates/ietfworkflows/state_updated_mail.txt b/ietf/templates/ietfworkflows/state_updated_mail.txt deleted file mode 100644 index 7eb828edd..000000000 --- a/ietf/templates/ietfworkflows/state_updated_mail.txt +++ /dev/null @@ -1,12 +0,0 @@ -{% autoescape off %} -The state of document {{ doc }} has been updated. See more information below. - -Previous state: {{ entry.from_state }} -Current state: {{ entry.to_state }} -Transition date: {{ entry.transition_date }} -Author of the change: {{ entry.person }} -{% if entry.set_tags %}Annotation tags set: {{ entry.set_tags }}{% endif %} -{% if entry.reset_tags %}Annotation tags reset: {{ entry.reset_tags }}{% endif %} -Comment: -{{ entry.comment }} -{% endautoescape %} diff --git a/ietf/templates/ietfworkflows/stream_delegates.html b/ietf/templates/ietfworkflows/stream_delegates.html deleted file mode 100644 index ca77c9575..000000000 --- a/ietf/templates/ietfworkflows/stream_delegates.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "base.html" %} -{% load ietf_streams ietf_filters %} - -{% block title %}Manage delegates for {{ stream.name }} stream{% endblock %} - -{% block content %} -

    Stream: {{ stream.name }}

    - -{% if chairs %} -

    {{ stream.name }} stream chairs

    - - -{% for chair in chairs %} - -{% endfor %} -
    NameEmail
    {{ chair }}{{ chair.email.0 }} <{{ chair.email.1 }}>
    -{% endif %} - -

    {{ stream.name }} stream delegates

    -
    -{% if delegates %} - - -{% for delegate in delegates %} - -{% endfor %} -
    NameEmail
    {{ delegate }}{{ delegate.email.0 }} <{{ delegate.email.1 }}>
    -

    - -

    -{% else %} -

    -No delegates for {{ stream.name }} stream assigned. -

    -{% endif %} -

    -Enter a valid e-mail address to add a new delegate. -

    -{% if form.errors.email %}{{ form.errors.email }}{% endif %} -{{ form.email }} - -
    -{% endblock %} diff --git a/ietf/templates/ietfworkflows/stream_form.html b/ietf/templates/ietfworkflows/stream_form.html deleted file mode 100644 index 268f9ecef..000000000 --- a/ietf/templates/ietfworkflows/stream_form.html +++ /dev/null @@ -1,22 +0,0 @@ -
    - - - - - - - - -
    Select the new stream
    -
    - {{ form.comment.errors }} - Comment: *
    - -
    -
    -
    - {{ form.stream.errors }} - {{ form.stream }} -
    -
    -
    diff --git a/ietf/templates/ietfworkflows/stream_history.html b/ietf/templates/ietfworkflows/stream_history.html deleted file mode 100644 index ae4fb7b19..000000000 --- a/ietf/templates/ietfworkflows/stream_history.html +++ /dev/null @@ -1,44 +0,0 @@ -{% load ietf_streams %} - - - - - -
    - {% if workflow %} -
      - {% for tag in tags %} - {{ tag }} - {% endfor %} -
    - {% endif %} -
    -
    - {% if stream %} -

    - {{ stream|default:"Without stream" }}{% if stream.with_groups and streamed.group %} :: {{ streamed.group|default:"" }}{% endif %} -

    -

    Current state: {{ state|default:"None" }}

    - {% else %} -

    Without stream

    - {% endif %} -
    - - {% if history %} -
    - {% if show_more %} -

    Viewing the last 20 entries. Show full log.

    - {% endif %} - {% for entry in history %} - {% workflow_history_entry entry %} - {% endfor %} - {% if show_more %} -

    Viewing the last 20 entries. Show full log.

    - {% endif %} -
    - {% else %} -

    - There is no stream history for this document. -

    - {% endif %} -
    diff --git a/ietf/templates/ietfworkflows/stream_state.html b/ietf/templates/ietfworkflows/stream_state.html deleted file mode 100644 index d06c4ac1e..000000000 --- a/ietf/templates/ietfworkflows/stream_state.html +++ /dev/null @@ -1,11 +0,0 @@ -{% if draft %} -
    -
    -{% if stream %} -{% if state %}{{ state.name }}{% else %}{{ stream }}{% endif %} -{% if milestones %}{% for m in milestones %}{{ m.due|date:"M Y" }}{% endfor %}{% endif %} -{% else %} - No stream assigned -{% endif %} -
    -{% endif %} diff --git a/ietf/templates/ietfworkflows/stream_state_redesign.html b/ietf/templates/ietfworkflows/stream_state_redesign.html deleted file mode 100644 index 0c334f865..000000000 --- a/ietf/templates/ietfworkflows/stream_state_redesign.html +++ /dev/null @@ -1,10 +0,0 @@ -{% if doc %} -
    -
    -{% if stream %} -{% if state %}{{ state.name }}{% else %}{{ stream }}{% endif %} -{% else %} - No stream assigned -{% endif %} -
    -{% endif %} diff --git a/ietf/templates/ietfworkflows/stream_updated_mail.txt b/ietf/templates/ietfworkflows/stream_updated_mail.txt deleted file mode 100644 index b98640b88..000000000 --- a/ietf/templates/ietfworkflows/stream_updated_mail.txt +++ /dev/null @@ -1,11 +0,0 @@ -{% autoescape off %} -The stream of document {{ doc }} has been updated. See more information below. - -Previous stream: {{ entry.from_stream }} -Current stream: {{ entry.to_stream }} -Transition date: {{ entry.transition_date }} -Author of the change: {{ entry.person }} - -Comment: -{{ entry.comment }} -{% endautoescape %} diff --git a/ietf/templates/ietfworkflows/tags_form.html b/ietf/templates/ietfworkflows/tags_form.html deleted file mode 100644 index e3388a868..000000000 --- a/ietf/templates/ietfworkflows/tags_form.html +++ /dev/null @@ -1,22 +0,0 @@ -
    - - - - - - -
    1. Input information about change2. Select annotation tags
    -
    - {{ form.errors.comment }} - Comment: *
    - -
    -
    -
    - {{ form.tags }} -
    -
    - -
    -
    -
    diff --git a/ietf/templates/ietfworkflows/workflow_history_entry.html b/ietf/templates/ietfworkflows/workflow_history_entry.html deleted file mode 100644 index 3f30673f2..000000000 --- a/ietf/templates/ietfworkflows/workflow_history_entry.html +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - - {{ entry.person }} -
    -
    - {{ entry.describe_change|safe|linebreaks }} -
    -
    - {{ entry.comment }} -
    - diff --git a/ietf/templates/submit/announce_new_version.txt b/ietf/templates/submit/announce_new_version.txt index 4ff47877b..79821349d 100644 --- a/ietf/templates/submit/announce_new_version.txt +++ b/ietf/templates/submit/announce_new_version.txt @@ -1,15 +1,15 @@ {% autoescape off %} -A new version (-{{ submission.revision }}) has been submitted for {{ submission.filename }}: -http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt +A new version (-{{ submission.rev }}) has been submitted for {{ submission.name }}: +http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt {% if msg %} {{ msg|striptags }} {% endif %} The IETF datatracker page for this Internet-Draft is: -https://datatracker.ietf.org/doc/{{ submission.filename }}/ +https://datatracker.ietf.org/doc/{{ submission.name }}/ Diff from previous version: -http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }} +http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }} Please note that it may take a couple of minutes from the time of submission until the htmlized version and diff are available at tools.ietf.org. diff --git a/ietf/templates/submit/announce_to_authors.txt b/ietf/templates/submit/announce_to_authors.txt index 5f1e8c61d..1d61b9c86 100644 --- a/ietf/templates/submit/announce_to_authors.txt +++ b/ietf/templates/submit/announce_to_authors.txt @@ -1,23 +1,23 @@ {% autoescape off %} -A new version of I-D, {{ submission.filename }}-{{ submission.revision }}.txt -has been successfully submitted by {{ submitter }} and posted to the +A new version of I-D, {{ submission.name }}-{{ submission.rev }}.txt +has been successfully submitted by {{ submission.submitter_parsed.name }} and posted to the IETF repository. -Filename: {{ submission.filename }} -Revision: {{ submission.revision }} -Title: {{ submission.id_document_name }} -Creation date: {{ submission.creation_date|date:"Y-m-d" }} -Group: {{ wg }} -Number of pages: {{ submission.txt_page_count }} -URL: http://www.ietf.org/internet-drafts/{{ submission.filename }}-{{ submission.revision }}.txt -Status: http://datatracker.ietf.org/doc/{{ submission.filename }} -Htmlized: http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }} -{% ifnotequal submission.revision "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.filename }}-{{ submission.revision }}{% endifnotequal %} +Name: {{ submission.name }} +Revision: {{ submission.rev }} +Title: {{ submission.title }} +Document date: {{ submission.document_date|date:"Y-m-d" }} +Group: {{ group }} +Pages: {{ submission.pages }} +URL: http://www.ietf.org/internet-drafts/{{ submission.name }}-{{ submission.rev }}.txt +Status: https://datatracker.ietf.org/doc/{{ submission.name }}/ +Htmlized: http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }} +{% if submission.rev != "00" %}Diff: http:{{rfcdiff_prefix}}?url2={{ submission.name }}-{{ submission.rev }}{% endif %} Abstract: {{ submission.abstract }} -{{ submission.comment_to_sec|default:"" }} +{{ submission.note|default:"" }} Please note that it may take a couple of minutes from the time of submission until the htmlized version and diff are available at tools.ietf.org. diff --git a/ietf/templates/submit/announce_to_lists.txt b/ietf/templates/submit/announce_to_lists.txt index 76146152d..eb4324bda 100644 --- a/ietf/templates/submit/announce_to_lists.txt +++ b/ietf/templates/submit/announce_to_lists.txt @@ -1,25 +1,25 @@ {% autoescape off %} A New Internet-Draft is available from the on-line Internet-Drafts directories. -{% if submission.group_acronym %} This draft is a work item of the {{ submission.group_acronym.group_acronym.name }} Working Group of the IETF.{% endif %} +{% if submission.group %} This draft is a work item of the {{ submission.group.name }} Working Group of the IETF.{% endif %} - Title : {{ submission.id_document_name }} - Author(s) : {% for author in authors %}{{ author }}{% if not forloop.last %} + Title : {{ submission.title }} + Author{{ submission.authors_parsed|pluralize:" ,s" }} : {% for author in submission.authors_parsed %}{{ author.name }}{% if not forloop.last %} {% endif %}{% endfor %} - Filename : {{ submission.filename }}-{{ submission.revision }}.txt - Pages : {{ submission.txt_page_count }} + Filename : {{ submission.name }}-{{ submission.rev }}.txt + Pages : {{ submission.pages }} Date : {{ submission.submission_date|date:"Y-m-d" }} Abstract: {{ submission.abstract }} The IETF datatracker status page for this draft is: -https://datatracker.ietf.org/doc/{{ submission.filename }} +https://datatracker.ietf.org/doc/{{ submission.name }}/ There's also a htmlized version available at: -http://tools.ietf.org/html/{{ submission.filename }}-{{ submission.revision }} -{% if submission.revision != "00" %} +http://tools.ietf.org/html/{{ submission.name }}-{{ submission.rev }} +{% if submission.rev != "00" %} A diff from the previous version is available at: -http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.filename }}-{{ submission.revision }} +http:{{settings.RFCDIFF_PREFIX}}?url2={{ submission.name }}-{{ submission.rev }} {% endif %} Please note that it may take a couple of minutes from the time of submission diff --git a/ietf/templates/submit/approval_request.txt b/ietf/templates/submit/approval_request.txt new file mode 100644 index 000000000..3eaa989fe --- /dev/null +++ b/ietf/templates/submit/approval_request.txt @@ -0,0 +1,33 @@ +{% autoescape off %} +Hi, + +Chair approval is needed for posting of {{ submission.name }}-{{ submission.rev }}. + +To approve the draft, go to this URL (note: you need to login to be able to approve): + https://{{ domain }}{% url submit_submission_status_by_hash submission_id=submission.pk access_token=submission.access_token %} + + File name : {{ submission.name }} + Revision : {{ submission.rev }} + Submission date : {{ submission.submission_date }} + Group : {{ submission.group|default:"Individual Submission" }} + + Title : {{ submission.title }} + Document date : {{ submission.document_date }} + Pages : {{ submission.pages }} + File size : {{ submission.file_size|filesizeformat }} + + Submitter : {{ submission.submitter }} + + Abstract : {{ submission.abstract }} + + + Authors: +{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%} +{% endfor %} +{% endautoescape %} + + +Best regards, + + The IETF Secretariat + through the draft submission service diff --git a/ietf/templates/submit/approvals.html b/ietf/templates/submit/approvals.html index b4907d139..960eaa61b 100644 --- a/ietf/templates/submit/approvals.html +++ b/ietf/templates/submit/approvals.html @@ -22,16 +22,16 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; }

    You don't have any submissions to approve.

    {% else %} - - - - -{% for d in approvals %} - - - - -{% endfor %} + + + + + {% for s in approvals %} + + + + + {% endfor %}
    SubmissionSubmitted
    {{ d.filename }}-{{ d.revision }}{{ d.submission_date }}
    SubmissionSubmitted
    {{ s.name }}-{{ s.rev }}{{ s.submission_date }}
    {% endif %} @@ -46,19 +46,19 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; } {% else %} - - - - - -{% for p in preapprovals %} - - - - - - -{% endfor %} + + + + + + {% for p in preapprovals %} + + + + + + + {% endfor %}
    Draft namePre-approvedBy
    {{ p.name }}{{ p.time|date:"Y-m-d" }}{{ p.by }}cancel pre-approval
    Draft namePre-approvedBy
    {{ p.name }}{{ p.time|date:"Y-m-d" }}{{ p.by }}cancel pre-approval
    {% endif %} @@ -69,18 +69,19 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; } {% else %} - - - - -{% for d in recently_approved %} - - - - -{% endfor %} + + + + + {% for d in recently_approved %} + + + + + {% endfor %}
    DraftSubmitted
    {{ d.filename }}{{ d.submission_date }}
    DraftSubmitted
    {{ d.name }}{{ d.submission_date }}
    {% endif %} + {% else %}

    Submission approvals

    diff --git a/ietf/templates/submit/confirm_submission.html b/ietf/templates/submit/confirm_submission.html new file mode 100644 index 000000000..ecc10973f --- /dev/null +++ b/ietf/templates/submit/confirm_submission.html @@ -0,0 +1,56 @@ +{% extends "submit/submit_base.html" %} + +{% block title %}Confirm submission of {{ submission.name }}{% endblock %} + +{% block morecss %} +{{ block.super }} +p.error { color: red; font-weight: bold; font-size: 1.5em; } +{% endblock %} + + +{% block submit_content %} + +

    Confirm submission of {{ submission.name }}

    + +{% if submission.state_id != "auth" and submission.state_id != "aut-appr" %} + {% if submission.state_id == "posted" %} +

    The submission has already been posted. See the draft here.

    + {% else %} +

    The submission is not in a state where it can be confirmed.

    + +

    Go to the status page + to see what has happened to it.

    + {% endif %} +{% else %} + + {% if not key_matched %} +

    Incorrect authorization key.

    + +

    Double-check the link you followed. If everything fails, you can go to + the status page, + cancel the submission and try again.

    + {% else %} +

    Authorization key accepted.

    + +

    Please press the button below to finish posting of + {{ submission.name }}-{{ submission.rev }}

    + +
    + +
    + {% endif %} + +{% endif %} + +{% endblock %} + +{% block scripts %} +jQuery(function () { + jQuery("form").submit(function() { + if (this.submittedAlready) + return false; + else + this.submittedAlready = true; + }); +}); +{% endblock %} diff --git a/ietf/templates/submit/confirm_autopost.txt b/ietf/templates/submit/confirm_submission.txt similarity index 77% rename from ietf/templates/submit/confirm_autopost.txt rename to ietf/templates/submit/confirm_submission.txt index d2b586309..0aa84db00 100644 --- a/ietf/templates/submit/confirm_autopost.txt +++ b/ietf/templates/submit/confirm_submission.txt @@ -2,12 +2,13 @@ Hi, The IETF datatracker draft submission service has received your draft -{{ draft.filename }}-{{ draft.revision }}, and requires a +{{ submission.name }}-{{ submission.rev }}, and requires a confirmation step in order to be able to complete the posting of the draft. Please follow this link to the page where you can confirm the posting: -Confirmation URL: {{ confirm_url|safe }} + +{{ confirm_url }} Best regards, diff --git a/ietf/templates/submit/draft_edit.html b/ietf/templates/submit/draft_edit.html deleted file mode 100644 index a1d865374..000000000 --- a/ietf/templates/submit/draft_edit.html +++ /dev/null @@ -1,169 +0,0 @@ -{% extends "submit/draft_status.html" %} -{% load submit_tags %} -{% block title %}Adjust Meta-Data{% endblock %} - -{% block morecss %} -{{ block.super }} -table.metadata-table #id_title, table.metadata-table #id_abstract, table.metadata-table #id_comments { width: 500px; } -table.metadata-table tr.warning th, table.metadata-table tr.warning td { background-color: #ffeebb; } -table.ietf-table tr { vertical-align: top; } -table.ietf-table tr.error { background-color: #ffeebb; border-top: 1px dashed red; border-bottom: 1px dashed red;} -table.ietf-table span.field-error { display: block; color: red; } -{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock %} - -{% block submit_content %} -

    Adjust External Meta-Data

    - - - - - - - - - - -

    Adjust data

    -{% if form.errors %} - -{% endif %} -
    - - - - - - - - -{% if settings.USE_DB_REDESIGN_PROXY_CLASSES %} - -{% else %} - - -{% endif %} - - - - -

    Authors

    - - - - {% if settings.USE_DB_REDESIGN_PROXY_CLASSES %} - - {% else %} - - - {% endif %} - - - - -{% for author in form.get_authors %} - - {% if settings.USE_DB_REDESIGN_PROXY_CLASSES %} - - {% else %} - - - {% endif %} - - -{% endfor %} - -
    NameFirst nameLast nameEmail address
    {{ author.get_full_name|default:"" }}{{ author.errors.name }}{{ author.first_name|default:"" }}{{ author.errors.first_name }}{{ author.last_name|default:"" }}{{ author.errors.last_name }}{{ author.email.1|default:"" }}{{ author.errors.email }}
    -
    - -
    - -
    - -

    -The IETF is an organized activity of the Internet Society -
    Please send problem reports to ietf-action@ietf.org. -

    -{% endblock %} diff --git a/ietf/templates/submit/draft_status.html b/ietf/templates/submit/draft_status.html deleted file mode 100644 index f337cd8e6..000000000 --- a/ietf/templates/submit/draft_status.html +++ /dev/null @@ -1,258 +0,0 @@ -{% extends "submit/submit_base.html" %} -{% load submit_tags %} -{% block title %}Submission status{% endblock %} - -{% block morecss %} -{{ block.super }} -div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; } -div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; } -div.info-message-warning { border: 1px solid orange; background-color: #ffffaa; padding: 5px 10px; margin: 1em 0px; color: black; } -div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; } -table.metadata-table th { white-space: nowrap; font-weight: bold; } -table.metadata-table #id_first_name, table.metadata-table #id_last_name { width: 200px; } -table.metadata-table #id_email { width: 400px; } -table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; } -table.metadata-table th.author { text-align: right; } -table.metadata-table tr { vertical-align: top; } -table.metadata-table tr.warning td, table.metadata-table tr.warning th { background-color: #ffaaaa; } -table.metadata-table div.warn_message { color: red; } -table.metadata-table ul.errorlist { color: red; padding: 0px; margin: 0px; list-style-type: none; } -pre.twopages { margin: 0px; } -{% endblock morecss %} - -{% block pagehead %} - - -{% if can_cancel %} - -{% endif %} - - -{% endblock %} - -{% block submit_content %} - -{% if status %} -

    Status of the submission: {{ status.status_value }}

    -{% endif %} - -{% if message %} -
    {{ message.1|safe }}
    -{% endif %} - -{% if auto_post_form.errors %} -
    Please fix errors in the form below
    -{% endif %} - -

    Check Page

    -

    -{% if validation.passes_idnits %} -Your draft has been verified to meet IDNITS requirements. -{% else %} -Your draft has NOT been verified to meet IDNITS requirements. -{% endif %} -(View IDNITS Results) -

    - - - -

    Meta-Data from the Draft

    -{% if validation.warnings %} - -{% endif %} - - - - - - - - - - - - -{% if validation.warnings.authors %} - -{% endif %} -{% if validation.authors %} -{% for author in validation.authors %} - -{% endfor %} -{% endif %} - - - - - -{% if allow_edit %} -
    - (Leads to manual post by the Secretariat) -
    - - {% if is_valid %} -

    Please edit the following meta-data before proceeding to Auto-Post

    -

    - If you are one of the authors of this document, then please click the button with your name on it to automatically fill in the submitter information as requested below. Otherwise, please manually enter your information. -

    -
    - {{ auto_post_form.get_author_buttons|safe }} - - {{ auto_post_form }} - - -
    - {% endif %} - - -{% else %} - {% if validation.submitter %} -

    Submitter information

    - - - - - {% endif %} -{% endif %} - -{% if can_cancel %} -

    Cancel submission

    -

    -

    -
    -
    - This submission will be canceled, and its uploaded document(s) permanently deleted. -

    -{% endif %} - -{% if can_approve %} -

    -

    - -
    -

    -{% endif %} - -{% if can_force_post %} -

    -

    - -
    -

    -{% endif %} - -{% if show_notify_button %} - -{% endif %} -

    -The IETF is an organized activity of the Internet Society -
    Please send problem reports to ietf-action@ietf.org. -

    -{% endblock %} - -{% block scripts %} -jQuery(function () { - jQuery("form").submit(function() { - if (this.submittedAlready) - return false; - else - this.submittedAlready = true; - }); -}); -{% endblock %} diff --git a/ietf/templates/submit/edit_submission.html b/ietf/templates/submit/edit_submission.html new file mode 100644 index 000000000..13ba47316 --- /dev/null +++ b/ietf/templates/submit/edit_submission.html @@ -0,0 +1,114 @@ +{% extends "submit/submit_base.html" %} + +{% load submit_tags %} + +{% block title %}Adjust Meta-Data of Submitted {{ submission.name }}{% endblock %} + +{% block morecss %} +{{ block.super }} +table.metadata-table tr th { padding-right: 2em; } +table.metadata-table #id_edit-title, table.metadata-table #id_edit-abstract, table.metadata-table #id_edit-note { width: 40em; } +table.authors td { vertical-align: top; } +table.authors tr.empty { display: none; } +input.add-author { margin-left: 42em; } +div.manual-posting { margin-top: 2em; } +{% endblock %} + +{% block submit_content %} +

    Adjust Meta-Data of Submitted {{ submission.name }}

    + + + + + + + + + + + + + + + + + + + +

    Adjust meta-data

    + +{% if form_errors %} + +{% endif %} + +
    + + + {% for field in edit_form %} + {% if field.name != "note" %} + + {% endif %} + {% endfor %} + + {% include "submit/submitter_form.html" %} + + {% with edit_form.note as field %} + + {% endwith %} + + + +

    Authors

    + + + + + + + + + + + + + + {% for form in author_forms %} + + + + + {% endfor %} + + + +
    + +
    + +
    + +
    +
    + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} + +{% block js %} +{{ block.super }} + + +{% endblock %} diff --git a/ietf/templates/submit/request_full_url.txt b/ietf/templates/submit/full_url.txt similarity index 75% rename from ietf/templates/submit/request_full_url.txt rename to ietf/templates/submit/full_url.txt index ecb975303..a7df4d9f2 100644 --- a/ietf/templates/submit/request_full_url.txt +++ b/ietf/templates/submit/full_url.txt @@ -2,7 +2,7 @@ Hi, The datatracker has received a request to send out the link to the URL where you -can confirm the submission of your draft {{ submission.filename }}-{{ submission.revision }}. +can confirm the submission of your draft {{ submission.name }}-{{ submission.rev }}. Please follow this link to get full access to the submission page: {{ url|safe }} diff --git a/ietf/templates/submit/last_confirmation_step.html b/ietf/templates/submit/last_confirmation_step.html deleted file mode 100644 index 1b6ca01a4..000000000 --- a/ietf/templates/submit/last_confirmation_step.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends "submit/submit_base.html" %} - -{% block title %}Confirm Auto-Post{% endblock %} - - -{% block submit_content %} - -

    Confirm auto-post

    -

    -Authorization key accepted. Please press the button below to finish Auto-Post of {{ detail.filename }}-{{ detail.revision }} -

    -
    - -
    -{% endblock %} - -{% block scripts %} -jQuery(function () { - jQuery("form").submit(function() { - if (this.submittedAlready) - return false; - else - this.submittedAlready = true; - }); -}); -{% endblock %} diff --git a/ietf/templates/submit/manual_post_mail.txt b/ietf/templates/submit/manual_post_mail.txt deleted file mode 100644 index 7fd5ffd9c..000000000 --- a/ietf/templates/submit/manual_post_mail.txt +++ /dev/null @@ -1,31 +0,0 @@ -{% autoescape off %} -Hi, - -Manual posting has been requested for the following Internet-Draft: - -I-D Submission Tool URL: - {{ url }} - - File name : {{ draft.filename }} - Version : {{ draft.revision }} - Submission date : {{ draft.submission_date }} - Group : {{ draft.group_acronym|default:"Individual Submission" }} {% if form.validation.warnings.group %}*Please note that this group is not an active one*{% endif %} - - Title : {{ draft.id_document_name }} - Document date : {{ draft.creation_date }} - Pages : {{ draft.txt_page_count }} - File size : {{ draft.filesize|filesizeformat }} - - Submitter : {{ submitter.get_full_name }} <{{ submitter.email.1 }}> - - Abstract : {{ draft.abstract }} - - - Authors: -{% for author in form.get_authors %} {{ author.get_full_name }} <{{ author.email.1 }}> -{% endfor %} - - Comments to the secretariat: - -{{ draft.comment_to_sec }} -{% endautoescape %} diff --git a/ietf/templates/submit/manual_post_request.txt b/ietf/templates/submit/manual_post_request.txt new file mode 100644 index 000000000..1620a476f --- /dev/null +++ b/ietf/templates/submit/manual_post_request.txt @@ -0,0 +1,31 @@ +{% autoescape off %} +Hi, + +Manual posting has been requested for the following Internet-Draft: + +I-D Submission Tool URL: + {{ url }} + + File name : {{ submission.name }} + Revision : {{ submission.rev }} + Submission date : {{ submission.submission_date }} + Group : {{ submission.group|default:"Individual Submission" }} {% if errors.group %}*Please note: {{ errors.group }}*{% endif %} + + Title : {{ submission.title }} + Document date : {{ submission.document_date }} + Pages : {{ submission.pages }} + File size : {{ submission.file_size|filesizeformat }} + + Submitter : {{ submission.submitter }} + + Abstract : {{ submission.abstract }} + + + Authors: +{% for author in submission.authors_parsed %} {{ author.name }}{% if author.email %} <{{ author.email }}>{% endif%} +{% endfor %} + + Comment to the secretariat: + +{{ submission.note }} +{% endautoescape %} diff --git a/ietf/templates/submit/problem-reports-footer.html b/ietf/templates/submit/problem-reports-footer.html new file mode 100644 index 000000000..8216223a5 --- /dev/null +++ b/ietf/templates/submit/problem-reports-footer.html @@ -0,0 +1,4 @@ + diff --git a/ietf/templates/submit/search_submission.html b/ietf/templates/submit/search_submission.html new file mode 100644 index 000000000..40131d6ac --- /dev/null +++ b/ietf/templates/submit/search_submission.html @@ -0,0 +1,18 @@ +{% extends "submit/submit_base.html" %} + +{% block title %}Submission status{% endblock %} + +{% block submit_content %} + +

    Please enter the name of the Internet-Draft you wish to view + submission status for:

    + +
    + {% if error %}
    {{ error }}
    {% endif %} + + +
    + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} diff --git a/ietf/templates/submit/submission_approval.txt b/ietf/templates/submit/submission_approval.txt deleted file mode 100644 index ddfa7fd94..000000000 --- a/ietf/templates/submit/submission_approval.txt +++ /dev/null @@ -1,33 +0,0 @@ -{% autoescape off %} -Hi, - -WG chair approval is needed for posting of {{ draft.filename }}-{{ draft.revision }}. - -To approve the draft, go to this URL (note: you need to login to be able to approve): - https://{{ domain }}/submit/status/{{ draft.submission_id }}/{{ draft.submission_hash }}/ - - File name : {{ draft.filename }} - Version : {{ draft.revision }} - Submission date : {{ draft.submission_date }} - Group : {{ draft.group_acronym|default:"Individual Submission" }} - - Title : {{ draft.id_document_name }} - Document date : {{ draft.creation_date }} - Pages : {{ draft.txt_page_count }} - File size : {{ draft.filesize|filesizeformat }} - - Submitter : {{ submitter.get_full_name }} <{{ submitter.email_address }}> - - Abstract : {{ draft.abstract }} - - - Authors: -{% for author in authors %} {{ author.get_full_name }} <{{ author.email.1 }}> -{% endfor %} -{% endautoescape %} - - -Best regards, - - The IETF Secretariat - through the draft submission service diff --git a/ietf/templates/submit/submission_status.html b/ietf/templates/submit/submission_status.html new file mode 100644 index 000000000..778ea24ef --- /dev/null +++ b/ietf/templates/submit/submission_status.html @@ -0,0 +1,285 @@ +{% extends "submit/submit_base.html" %} + +{% load ietf_filters submit_tags %} + +{% block title %}Status of submission of {{ submission.name }}-{{ submission.rev }}{% endblock %} + +{% block submit_content %} + +{% if submission.state_id != "uploaded" %} +

    Status of the submission: {{ submission.state.name }}

    +{% endif %} + +{% if message %} +
    {{ message.1 }}
    +{% endif %} + +{% if submission.state_id == "aut-appr" and submission.submitter not in confirmation_list|join:", " %} +
    + Please note that since the database does not have your email address in the list of authors of previous + revisions of the document, you are not receiving a confirmation email yourself; one of the + addressees above will have to send a confirmation in order to complete the submission. This is done + to avoid document hijacking. If none of the known previous authors will be able to confirm the + submission, please contact the Secretariat for action. +
    +{% endif %} + +{% if submitter_form.errors %} +
    Please fix errors in the form below
    +{% endif %} + +

    IDNITS

    +

    + {% if passes_idnits %} + Your draft has been verified to meet IDNITS requirements. + {% else %} + Your draft has NOT been verified to meet IDNITS requirements. + {% endif %} + (View IDNITS Results) +

    + + + + + + +

    Meta-Data from the Submission

    + +{% if errors %} + +{% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% for author in submission.authors_parsed %} + + + + + {% endfor %} + + + + + + + + + + + + + + + + + +{% if can_edit %} +
    + + (Leads to manual post by the Secretariat) +
    + + {% if passes_idnits and not errors %} +

    Please edit the following meta-data before posting

    + +

    + +
    + + {% include "submit/submitter_form.html" %} + + + + + {% if requires_group_approval %} + (Notifies group chairs to get approval) + {% else %} + {% if requires_prev_authors_approval %} + (Notifies authors of previous revision of draft to get approval) + {% else %} + (Notifies submitter and authors for confirmation) + {% endif %} + {% endif %} +
    + {% endif %} + +{% else %} + {% if submission.submitter %} +

    Submitter information

    + + + + + {% endif %} +{% endif %} + +{% if can_cancel %} +

    Cancel submission

    +

    + Cancel submission and delete the uploaded file{{ submission.file_types|split:","|pluralize }} permanently: + +

    + + +
    +

    +{% endif %} + +{% if can_group_approve %} +

    Approve submission

    +

    +

    + + +
    +

    +{% endif %} + +{% if can_force_post %} +

    +

    + + +
    +

    +{% endif %} + +{% if show_send_full_url %} + +{% endif %} + +

    History

    + + + + + {% for e in submission.submissionevent_set.all %} + + + + + + {% endfor %} +
    DateByText
    {{ e.time|date:"Y-m-d" }}{{ e.by|default:"" }}{{ e.desc }} +
    + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} + +{% block js %} + + +{% endblock %} diff --git a/ietf/templates/submit/submit_base.html b/ietf/templates/submit/submit_base.html index 984857bde..dc2cd05f2 100644 --- a/ietf/templates/submit/submit_base.html +++ b/ietf/templates/submit/submit_base.html @@ -1,34 +1,22 @@ {% extends "base.html" %} -{% block morecss %} -.ietf-navset { - background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px; - color:white; - border:1px solid black; - padding:4px; -} -.ietf-navset .selected { font-weight:bold; padding: 0 3px; } -.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } -.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; } +{% block pagehead %} +{{ block.super }} + {% endblock %} + {% block content %}

    IETF Internet-Draft Submission

    -{% if selected == "index" %}Upload{% else %}Upload{% endif %} | -{% if selected == "status" %}Status{% else %}Status{% endif %} | +{% if selected == "index" %}Upload{% else %}Upload{% endif %} | +{% if selected == "status" %}Status{% else %}Status{% endif %} | {% if selected == "instructions" %}Tool Instructions{% else %}Tool Instructions{% endif %} | {% if selected == "notewell" %}NOTE WELL{% else %}NOTE WELL{% endif %} | {% if selected == "approvals" %}Approvals{% else %}Approvals{% endif %}
    -{% if form.cutoff_warning %} -
    -{{ form.cutoff_warning|safe }} -
    -{% endif %} - {% block submit_content %} {% endblock %} {% endblock %} diff --git a/ietf/templates/submit/submit_index.html b/ietf/templates/submit/submit_index.html deleted file mode 100644 index bfde12714..000000000 --- a/ietf/templates/submit/submit_index.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "submit/submit_base.html" %} -{% block title %}Upload{% endblock %} - -{% block pagehead %} -{{ form.media }} -{% endblock %} - -{% block submit_content %} -

    This page is used to submit IETF Internet-Drafts to the Internet-Draft repository. The list of current Internet-Drafts can be accessed at http://www.ietf.org/ietf/1id-abstracts.txt

    -

    Internet-Drafts are working documents of the Internet Engineering Task Force (IETF), its areas, and its working groups. Note that other groups may also distribute working documents as Internet-Drafts.

    -

    Internet-Drafts are draft documents, and are valid for a maximum of six months. They may be updated, replaced, or obsoleted by other documents at any time.

    -{% if not form.shutdown %} -

    If you run into problems when submitting an Internet-Draft using this and the following pages, you may alternatively submit your draft by email to internet-drafts@ietf.org. However, be advised that manual processing always takes additional time.

    - - {{ form }} -{% endif %} - -

    -The IETF is an organized activity of the Internet Society -
    Please send problem reports to ietf-action@ietf.org. -

    -{% endblock %} diff --git a/ietf/templates/submit/submit_status.html b/ietf/templates/submit/submit_status.html deleted file mode 100644 index e9a023125..000000000 --- a/ietf/templates/submit/submit_status.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "submit/submit_base.html" %} -{% block title %}Submission status{% endblock %} - -{% block pagehead %} -{{ form.media }} -{% endblock %} - -{% block submit_content %} -

    -Please enter the filename of the Internet-Draft you wish to view submission status for: -

    - -
    -{% if error %}
    {{ error }}
    {% endif %} - - -
    - -

    - -Note that the status page only displays the status of an Internet-Draft with a posting still in progress or an Internet-Draft that has been successfully posted. -

    -

    -The IETF is an organized activity of the Internet Society -
    Please send problem reports to ietf-action@ietf.org. -

    -{% endblock %} diff --git a/ietf/templates/submit/submitform.html b/ietf/templates/submit/submitform.html deleted file mode 100644 index 60816e359..000000000 --- a/ietf/templates/submit/submitform.html +++ /dev/null @@ -1,53 +0,0 @@ -{% load i18n %} - -
    - - - -
    - {% if form.errors %} -
    - Please correct the errors below. -
    - {{ form.non_field_errors }} - {% endif %} -{% for fieldset in form.get_fieldsets %} - {% if fieldset.name %} -
    -

    {{ fieldset.name }}

    - {% endif %} - - {% for field in fieldset.fields %} -
    - -
    -
    {{ field.help_text }}
    - {{ field }} - {{ field.errors }} -
    -
    -
    - {% endfor %} - - {% if fieldset.name %} -
    - {% endif %} -{% endfor %} -
    - -{% if not form.shutdown %} -
    - -
    -{% endif %} - -
    diff --git a/ietf/templates/submit/submitter_form.html b/ietf/templates/submit/submitter_form.html new file mode 100644 index 000000000..8dbb5c3b7 --- /dev/null +++ b/ietf/templates/submit/submitter_form.html @@ -0,0 +1,17 @@ + + Submitter + + If you are one of the authors, please click the button below + with your name on it to automatically fill in the + submitter information. Otherwise, + please manually enter your name and email address.

    + + {% for author in submission.authors_parsed %} + + {% endfor %} + + + + {% for field in submitter_form %} + {{ field.label_tag }}{{ field }}{{ field.errors }} + {% endfor %} diff --git a/ietf/templates/submit/tool_instructions.html b/ietf/templates/submit/tool_instructions.html index b01046972..fd32800ce 100644 --- a/ietf/templates/submit/tool_instructions.html +++ b/ietf/templates/submit/tool_instructions.html @@ -3,7 +3,7 @@ {% block submit_content %}

    I-D Submission Tool Instructions

    -

    Tool URL: http://datatracker.ietf.org/{% url submit_index %}

    +

    Tool URL: https://datatracker.ietf.org{% url submit_upload_submission %}

    This page will explain the purpose and content of each screen in the I-D Submission Tool, and the actions that result by clicking the form buttons on each screen.
    The specification for this tool can be found in RFC 4228. @@ -98,7 +98,7 @@ This is the screen where a user can adjust any meta-data that could have been in Status Screen

    -The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by filename. +The Status screen is the screen where a user can view the current status of a document that has just been submitted by the user, or a document that was submitted previously via the tool. If a link 'Status' is clicked from the tool's first page, then a form field will be provided for a user to look up a document by name.

    Form buttons and resulting actions:
    diff --git a/ietf/templates/submit/upload_submission.html b/ietf/templates/submit/upload_submission.html new file mode 100644 index 000000000..257547965 --- /dev/null +++ b/ietf/templates/submit/upload_submission.html @@ -0,0 +1,63 @@ +{% extends "submit/submit_base.html" %} +{% block title %}Upload{% endblock %} + +{% block morecss %} + {{ block.super }} + form.upload-form h3 { margin: 0; color: #fff; background-color: #2647a0; padding: 0.3em 0.8em; } + form.upload-form table { padding: 0.3em 0.8em; } + form.upload-form .required { color: #f00; } + form.upload-form td { padding-right: 5em; padding-top: 0.4em; padding-bottom: 0.4em; } + form.upload-form .ietf-box { margin-bottom: 1em; } +{% endblock %} + +{% block submit_content %} +{% if form.cutoff_warning %} +
    + {{ form.cutoff_warning|safe }} +
    +{% endif %} + +

    This page is used to submit IETF Internet-Drafts to the +Internet-Draft repository. The list of current Internet-Drafts can be +accessed athttp://www.ietf.org/ietf/1id-abstracts.txt

    + +

    Internet-Drafts are working documents of the Internet Engineering +Task Force (IETF), its areas, and its working groups. Note that other +groups may also distribute working documents as Internet-Drafts.

    + +

    Internet-Drafts are draft documents, and are valid for a maximum of +six months. They may be updated, replaced, or obsoleted by other +documents at any time.

    + +{% if not form.shutdown %} +

    If you run into problems when submitting an Internet-Draft +using this and the following pages, you may alternatively submit +your draft by email to +internet-drafts@ietf.org. +However, be advised that manual processing always takes additional time.

    + + +
    +

    Upload a draft

    + + {{ form.non_field_errors }} + +
    + {% for field in form %} + + + + + {% endfor %} +
    {{ field.label_tag }} {% if field.field.required %}*{% endif %}{{ field }} {{ field.errors }}
    +
    + +
    + +
    + +{% endif %} + +{% include "submit/problem-reports-footer.html" %} + +{% endblock %} diff --git a/ietf/templates/wgchairs/confirm_management_writeup.html b/ietf/templates/wgchairs/confirm_management_writeup.html deleted file mode 100644 index 3bc5ceab9..000000000 --- a/ietf/templates/wgchairs/confirm_management_writeup.html +++ /dev/null @@ -1,55 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% load ietf_filters wgchairs_tags %} - -{% block title %}Change shepherd write-up for {{ doc }}{% endblock %} - -{% block wg_content %} - -

    -Return to shepherd list -

    - -

    Updating write-up for {{ doc }}

    - -

    -Before you modify the shepherd write-up please revise the 'Doc Shepherd Follow-up Underway' annotation tag and set or reset it if appropriate. -

    -

    -Remember that you must provide a comment if you change the annotation tag state. -

    - -
    -
    - - -{% if form.message %} - -{% endif %} - - -
    Doc Shepherd Follow-up Underway
    - {{ form.message.value }} -
    - - - - -
    -{{ form.comment }} -
    -

    - Change write-up and ...
    - -
    - Cancel, I don't want to do any change! -

    - -
    - - - -
    New shepherd write-up
    {{ form.get_writeup|linebreaksbr }}
    - -
    -
    -{% endblock %} diff --git a/ietf/templates/wgchairs/draft_state.html b/ietf/templates/wgchairs/draft_state.html deleted file mode 100644 index 956ebd44f..000000000 --- a/ietf/templates/wgchairs/draft_state.html +++ /dev/null @@ -1 +0,0 @@ -{{ state.name }} diff --git a/ietf/templates/wgchairs/edit_management_shepherd.html b/ietf/templates/wgchairs/edit_management_shepherd.html deleted file mode 100644 index b9a9e616a..000000000 --- a/ietf/templates/wgchairs/edit_management_shepherd.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% load ietf_filters %} - -{% block title %}Change shepherd for {{ doc }}{% endblock %} - -{% block wg_content %} - -

    -Return to shepherd list -Return to {{ doc }} -

    - -

    Change shepherd for {{ doc }}

    - -
    -
    - - - -
    Actual shepherd
    {% if doc.shepherd %}{{ doc.shepherd }}{% else %}No shepherd assigned{% endif %}
    - - - -
    -
    -
    - - -{% if form.message %} - -{% endif %} - - -
    Change shepherd
    - {{ form.message.value }} -
    {{ form.as_p }}
    - - {% if form.can_cancel %}No! I don't want to continue{% endif %} -
    -
    -{% endblock %} diff --git a/ietf/templates/wgchairs/edit_management_writeup.html b/ietf/templates/wgchairs/edit_management_writeup.html deleted file mode 100644 index 7a61437c0..000000000 --- a/ietf/templates/wgchairs/edit_management_writeup.html +++ /dev/null @@ -1,67 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% load ietf_filters wgchairs_tags %} - -{% block title %} {% if can_edit %} Change {% else %} View {% endif %} shepherd write-up for {{ doc }}{% endblock %} - -{% block wg_content %} - -

    -Return to shepherd list -

    - -

    {% if can_edit %} Change {% else %} View {% endif %} shepherd write-up for {{ doc }}

    - - - - -
    Draft stateActual shepherd write-upLast updated
    {% show_state doc %}{{ writeup.writeup|linebreaksbr }}{% if writeup %}{{ writeup.date }} by {{ writeup.person }}{% endif %}
    - -

    -Please, note that the 'Doc Shepherd Follow-up Underway' annotation tag is {% if not followup %}NOT{% endif %} set for {{ doc }}. -

    - -{% if can_edit %} -
    -
    - - -{% if form.message %} - -{% endif %} - - -
    Edit shepherd write-up
    - {{ form.message.value }} -
    -
    - -
    - -
    - -
    - - - -
    Upload a new shepherd write-up
    -

    Replace the current write-up with the contents of a plain ascii file:

    -
    - -
    -
    -{% else %} -{% if authorized_user %} - - - -
    Edit shepherd write-up
    -

    - You can not edit or upload the shepherd write-up for {{ doc }} because the draft is not on "WG Consensus: Waiting for Write-Up" state. -

    -

    - Please contact with the {{ wg }} Working Group chair. -

    -
    -{% endif %} -{% endif %} -{% endblock %} diff --git a/ietf/templates/wgchairs/manage_delegates.html b/ietf/templates/wgchairs/manage_delegates.html deleted file mode 100644 index f1f2eaf58..000000000 --- a/ietf/templates/wgchairs/manage_delegates.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "wginfo/wg_base.html" %} - - -{% block wg_titledetail %}Delegates{% endblock %} - -{% block wg_content %} -
    - -

    Manage delegates

    -

    -Sometimes, a WG has one (or more) WG Secretaries, in addition to the WG Chairs. -This page lets the WG Chairs delegate the authority to do updates to the WG state of WG documents in the datatracker. -

    - -

    -You may at most delegate the datatracker update rights to {{ max_delegates }} persons at any given time. -

    - - - - -
    -{% if delegates %} -
    - - - {% for delegate in delegates %} - - {% endfor %} -
    RemoveDelegate name
    {{ delegate.person }}
    - -
    -{% else %} -No delegates -{% endif %} -
    - - -{% if add_form.message %} - -{% endif %} - -
    Add new delegate
    - {{ add_form.message.value }} -
    -{% if can_add %} -
    - {{ add_form.as_p }} -

    - - {% if add_form.can_cancel %}No! I don't want to continue{% endif %} -

    -
    -{% else %} -You can only assign {{ max_delegates }} delegates. Please remove delegates to add a new one. -{% endif %} -
    -
    -
    -{% endblock %} diff --git a/ietf/templates/wgchairs/manage_workflow.html b/ietf/templates/wgchairs/manage_workflow.html deleted file mode 100644 index bc5a4fbd7..000000000 --- a/ietf/templates/wgchairs/manage_workflow.html +++ /dev/null @@ -1,165 +0,0 @@ -{% extends "wginfo/wg_base.html" %} - -{% block wg_titledetail %}Manage Workflow{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock pagehead %} - -{% block wg_content %} -
    -

    Edit workflow

    -
    - - -
    -
    -
    - - - - - {% for state in states %} - - - -{% endfor %} -
    States used in {{ wg }} Working Group
    {{ state.name }}

    - - - - - {% for tag in tags %} - - - -{% endfor %} -
    Annotation tags used in {{ wg }} Working Group
    {{ tag.name }}
    -
    - - - - - {% for transition in workflow.transitions.all %} - - - - - - {% endfor %} - {% if not workflow.transitions.all.count %} - - {% endif %} -
    Transition nameInitial statesDestination state
    - {{ transition.name }} - - {% for state in transition.states.all %} - {{state.name }}{% if not forloop.last %}
    {% endif %} - {% endfor %} -
    - {{ transition.destination.name }} -
    There are no transitions defined so any state change is allowed
    -
    -
    - -
    -

    -Please note that the states you can not uncheck are needed in all IETF WGs. -

    -

    -You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174 -

    -
    - - - - - {% for state in default_states %} - - - - - -{% endfor %} -
    Used in {{ wg }}Available statesDefinition
    -
    -
    [+] {{ state.statedescription_set.all.0.definition|safe }}
    -
    -
    - -
    -
    - -
    -

    -You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174 -

    -
    - - - - -{{ formset.as_table }} -
    DeleteTransition nameInitial statesDestination state
    - -
    -
    - -
    -
    - - - - - {% for tag in default_tags %} - - - - -{% endfor %} -
    Used in {{ wg }}Available annotation tags
    - -
    -
    -
    -
    - - - -{% endblock %} diff --git a/ietf/templates/wgchairs/notexistdelegate.html b/ietf/templates/wgchairs/notexistdelegate.html deleted file mode 100644 index 706907022..000000000 --- a/ietf/templates/wgchairs/notexistdelegate.html +++ /dev/null @@ -1,28 +0,0 @@ -{% if shepherd %} -

    -The shepherd you are trying to designate does not have a personal user-id and password to log-on to the Datatracker. -

    -

    -An email will be sent to the following addresses to inform that -the person you have designated to be one of your document shepherds -currently does not have login credentials for the Datatracker -and should contact the Secretariat to obtain their own user-id and -password for the Datatracker. -

    -{% else %} -

    -The delegate you are trying to designate does not have a personal user-id and password to log-on to the Datatracker. -

    -

    -An email will be sent to the following addresses to inform that -the person you have designated to be one of your delegates -currently does not have login credentials for the Datatracker -and should contact the Secretariat to obtain their own user-id and -password for the Datatracker. -

    -{% endif %} -
      -{% for email in email_list %} -
    • {{ email }}
    • -{% endfor %} -
    diff --git a/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt b/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt deleted file mode 100644 index de2318a93..000000000 --- a/ietf/templates/wgchairs/notexistsdelegate_delegate_email.txt +++ /dev/null @@ -1,10 +0,0 @@ -{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add you as a {{ wg }} {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. - -You don't have an user/password to log into the datatracker so you must contact -the Secretariat at iesg-secretary@ietf.org in order to get your credentials. - -When you get your credentials, please inform {{ chair }} at -{{ chair.email.1 }} so he/she can finish the designate process. - -Thank you. -{% endautoescape %} diff --git a/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt b/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt deleted file mode 100644 index 27edacc11..000000000 --- a/ietf/templates/wgchairs/notexistsdelegate_secretariat_email.txt +++ /dev/null @@ -1,15 +0,0 @@ -{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add a person with email -{{ delegate_email }} as a {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. - -This person don't have an user/password to log into the datatracker so -an email has been seent to {{ delegate_email }} in order to he/she contacs the -Secretariat to request his/her credentials. - -{% if delegate_persons %} -Please, note that the following persons with {{ delegate_email }} email address -already exists in the system but they can not log in. -{% for person in delegate_persons %} -{{ person.pk }} - {{ person }} -{% endfor %} -{% endif %} -{% endautoescape %} \ No newline at end of file diff --git a/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt b/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt deleted file mode 100644 index 477111dcf..000000000 --- a/ietf/templates/wgchairs/notexistsdelegate_wgchairs_email.txt +++ /dev/null @@ -1,12 +0,0 @@ -{% autoescape off %}{{ chair }} as a WG Chair of {{ wg }} wants to add a person with email -{{ delegate_email }} as a {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. - -This person don't have an user/password to log into the datatracker so -an email has been seent to {{ delegate_email }} in order to he/she contacs the -Secretariat to request his/her credentials. - -When he/she gets her credentials then he/she will send an email to -{{ chair }} at {{ chair.email.1 }}. - -{{ chair }} could then assign this person as {% if shepherd %}shepherd of document {{ shepherd }}{% else %}WG Delegate{% endif %}. -{% endautoescape%} \ No newline at end of file diff --git a/ietf/templates/wgchairs/shepherd_document_row.html b/ietf/templates/wgchairs/shepherd_document_row.html deleted file mode 100644 index a58e1eac5..000000000 --- a/ietf/templates/wgchairs/shepherd_document_row.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load wgchairs_tags %} - - - - {{ doc.title }} - - - Change shepherd - - - {% writeup doc %} - [Edit] - - - {% writeupdate doc %} - - diff --git a/ietf/templates/wgchairs/wg_shepherd_documents.html b/ietf/templates/wgchairs/wg_shepherd_documents.html deleted file mode 100644 index 2afcd9bac..000000000 --- a/ietf/templates/wgchairs/wg_shepherd_documents.html +++ /dev/null @@ -1,122 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% comment %} -Copyright (C) 2010 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. -{% endcomment %} -{% block wg_titledetail %}Documents{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock pagehead %} - -{% block wg_content %} - -

    Documents by its shepherd

    -
    - - -
    -
    - - - - - - - - - {% for doc in no_shepherd %} - {% include "wgchairs/shepherd_document_row.html" %} - {% endfor %} - -
    DocumentChange shepherdShepherd write-upShepherd write-up last update
    -
    - -
    - - - - - - - - - {% for doc in my_documents %} - {% include "wgchairs/shepherd_document_row.html" %} - {% endfor %} - -
    DocumentStatusShepherd write-upShepherd write-up last update
    -
    - -
    -{% regroup other_shepherds by shepherd as regrouped %} -{% for documents in regrouped %} -

    {{ documents.grouper }}

    - - - - - - - - {% for doc in documents.list %} - {% include "wgchairs/shepherd_document_row.html" %} - {% endfor %} -
    DocumentStatusShepherd write-upShepherd write-up last update
    -{% endfor %} -
    - - -{% endblock wg_content %} - diff --git a/ietf/templates/wgchairs/wgchairs_admin_options.html b/ietf/templates/wgchairs/wgchairs_admin_options.html deleted file mode 100644 index b8c45a00f..000000000 --- a/ietf/templates/wgchairs/wgchairs_admin_options.html +++ /dev/null @@ -1,23 +0,0 @@ -{% if can_manage_workflow %} - {% ifequal selected "manage_workflow" %} - Manage workflow - {% else %} - Manage workflow - {% endifequal %} | -{% endif %} - -{% if can_manage_delegates %} - {% ifequal selected "manage_delegates" %} - Manage delegations - {% else %} - Manage delegations - {% endifequal %} | -{% endif %} - -{% if can_manage_shepherds %} - {% ifequal selected "manage_shepherds" %} - Manage shepherds - {% else %} - Manage shepherds - {% endifequal %} | -{% endif %} diff --git a/ietf/templates/wginfo/1wg-charters-by-acronym.txt b/ietf/templates/wginfo/1wg-charters-by-acronym.txt index 247e1466c..bad8939cd 100644 --- a/ietf/templates/wginfo/1wg-charters-by-acronym.txt +++ b/ietf/templates/wginfo/1wg-charters-by-acronym.txt @@ -1,4 +1,4 @@ -{% load ietf_filters %}{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{{ wg }} -{% endif %}{% endif %}{% endfor %} -{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{% include "wginfo/wg-charter.txt" %} -{% endif %}{% endif %}{% endfor %} +{% autoescape off %}{% load ietf_filters %}{% for group in groups %}{{ group.acronym }} +{% endfor %} + +{% for group in groups %}{% include "wginfo/group_entry_with_charter.txt" %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/wginfo/1wg-charters.txt b/ietf/templates/wginfo/1wg-charters.txt index 4e5e70941..767dd1bb0 100644 --- a/ietf/templates/wginfo/1wg-charters.txt +++ b/ietf/templates/wginfo/1wg-charters.txt @@ -1,7 +1,6 @@ -{% load ietf_filters %}{% regroup wg_list|dictsort:"area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.name" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %}{{ wg }} -{% endif %}{% endif %}{% endfor %}{% endfor %} +{% autoescape off %}{% load ietf_filters %}{% for area in areas %}{% for group in area.groups %}{{ group.acronym }} +{% endfor %}{% endfor %} + +{% for area in areas %}{% for group in area.groups %}{% include "wginfo/group_entry_with_charter.txt" %}{% endfor %}{% endfor %}{% endautoescape %} -{% regroup wg_list|dictsort:"area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.name" %}{% if wg.area.area.status_id == 1 and wg.area.area.acronym != "iesg" %}{% if wg.start_date %} -{% include "wginfo/wg-charter.txt" %} -{% endif %}{% endif %}{% endfor %}{% endfor %} diff --git a/ietf/templates/wginfo/1wg-summary-by-acronym.txt b/ietf/templates/wginfo/1wg-summary-by-acronym.txt index d1a0f46be..028d5fd0e 100644 --- a/ietf/templates/wginfo/1wg-summary-by-acronym.txt +++ b/ietf/templates/wginfo/1wg-summary-by-acronym.txt @@ -1,10 +1,10 @@ -{% load ietf_filters %} +{% autoescape off %}{% load ietf_filters %} IETF Working Group Summary (By Acronym) The following Area Abbreviations are used in this document -{% for area in area_list %} -{{ area|upper }} - {{ area.area_acronym.name }}{% endfor %} -{% for wg in wg_list|dictsort:"group_acronym.acronym" %}{% if wg.start_date %} -{{ wg.group_acronym.name|safe }} ({{ wg }}) -- {{ wg.area.area|upper }} -{% include "wginfo/wg_summary.txt" %}{% endif %}{% endfor %} +{% for area in areas %} +{{ area.acronym|upper }} - {{ area.name }}{% endfor %} +{% for group in groups %} +{{ group.name }} ({{ group.acronym }}) -- {{ group.parent.acronym|upper }} +{% include "wginfo/group_entry.txt" %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/wginfo/1wg-summary.txt b/ietf/templates/wginfo/1wg-summary.txt index 55d76d1e2..f6ae51202 100644 --- a/ietf/templates/wginfo/1wg-summary.txt +++ b/ietf/templates/wginfo/1wg-summary.txt @@ -1,8 +1,11 @@ -{% load ietf_filters %} IETF Working Group Summary (By Area) -{% regroup wg_list|dictsort:"area.area.area_acronym.acronym" by area.area as wga_list %}{% for area in wga_list %}{% for wg in area.list|dictsort:"group_acronym.acronym" %}{% ifequal wg.area.area.status_id 1 %}{% if forloop.first %} -{{ wg.area_acronym.name }} ({{ wg.area_acronym }}) -{{ wg.area_acronym.name|dashify }}------{% for ad in wg.area_directors %} - {{ ad.person }} <{{ ad.person.email.1 }}>{% endfor %} -{% endif %}{% if wg.start_date %} -{{ wg.group_acronym.name|safe }} ({{ wg }}) -{% include "wginfo/wg_summary.txt" %}{% endif %}{% endifequal %}{% endfor %}{% endfor %} +{% autoescape off %}{% load ietf_filters %} + IETF Working Group Summary (By Area) + + +{% for area in areas %}{{ area.name }} ({{ area.acronym }}) +{{ area.name|dashify }}------{% for ad in area.ads %} + {{ ad.person.plain_name }} <{{ ad.email.address }}>{% endfor %} + +{% for group in area.groups %}{{ group.name }} ({{ group.acronym }}) +{% include "wginfo/group_entry.txt" %} +{% endfor %}{% endfor %}{% endautoescape %} diff --git a/ietf/templates/wginfo/wg-dirREDESIGN.html b/ietf/templates/wginfo/active_wgs.html similarity index 69% rename from ietf/templates/wginfo/wg-dirREDESIGN.html rename to ietf/templates/wginfo/active_wgs.html index a74f48ba9..4d88435fa 100644 --- a/ietf/templates/wginfo/wg-dirREDESIGN.html +++ b/ietf/templates/wginfo/active_wgs.html @@ -53,47 +53,44 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {% for area in areas %}

    {{ area.name }}

    - {% for ad in area.ads %} - {% if forloop.first %} -

    Area Director{{ forloop.revcounter|pluralize }}:

    + {% if area.ads %} +

    Area Director{{ area.ads|pluralize }}:

    - {% endif %} - - {% if forloop.last %} + {% for ad in area.ads %} + + + + + {% endfor %}
      {{ ad.person.plain_name }} <{{ ad.address }}>{% if ad.incoming %} (Incoming AD){% endif %}
     {{ ad.person.plain_name }} <{{ ad.email.address }}>{% if ad.name == "pre-ad" %} (Incoming AD){% endif %}
    {% endif %} - {% endfor %} - {% for url in area.urls %} - {% if forloop.first %} -

    Area Specific Web Page{{ forloop.revcounter|pluralize}}:

    + {% if area.urls %} +

    Area Specific Web Page{{ area.urls|pluralize}}:

    - {% endif %} + {% for url in area.urls %} {{ url.name }}{% if not forloop.last %}
    {% endif %} - {% if forloop.last %} + {% endfor %}

    {% endif %} - {% endfor %} - {% for wg in area.wgs %} - {% if forloop.first %} -

    Active Working Groups:

    + {% if area.groups %} +

    Active Working Group{{ area.groups|pluralize}}:

    - {% endif %} + {% for group in area.groups %} - - - - + + + + - {% if forloop.last %} + {% endfor %}
    {{ wg.acronym }}{% for ad in area.ads %}{% ifequal ad.person_id wg.ad_id %}{% endifequal %}{% endfor %}{{ wg.name }}{% for chair in wg.chairs %}{{ chair.person.plain_name }}{% if not forloop.last %}, {% endif %}{% endfor %}{{ group.acronym }}{% for ad in area.ads %}{% if ad.person_id == group.ad_id %}{% endif %}{% endfor %}{{ group.name }}{% for chair in group.chairs %}{{ chair.person.plain_name }}{% if not forloop.last %}, {% endif %}{% endfor %}
    - {% endif %} - {% empty %} + {% else %}

    No Active Working Groups

    - {% endfor %}{# wg #} + {% endif %} {% endfor %}{# area #} diff --git a/ietf/templates/wginfo/bofs.html b/ietf/templates/wginfo/bofs.html index 60df23342..3d2d5134a 100644 --- a/ietf/templates/wginfo/bofs.html +++ b/ietf/templates/wginfo/bofs.html @@ -25,7 +25,7 @@ {% for g in groups %} - {{ g.acronym }} + {{ g.acronym }} {{ g.name }} diff --git a/ietf/templates/wginfo/chartering_wgs.html b/ietf/templates/wginfo/chartering_wgs.html index 291daa6a5..67255ca0b 100644 --- a/ietf/templates/wginfo/chartering_wgs.html +++ b/ietf/templates/wginfo/chartering_wgs.html @@ -28,7 +28,7 @@ {% for g in groups %} - {{ g.acronym }} + {{ g.acronym }} {{ g.name }} diff --git a/ietf/templates/wginfo/conclude.html b/ietf/templates/wginfo/conclude.html index d233057c1..76f0ad03c 100644 --- a/ietf/templates/wginfo/conclude.html +++ b/ietf/templates/wginfo/conclude.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Request closing of WG {{ wg.acronym }}{% endblock %} +{% block title %}Request closing of {{ wg.acronym }} {{ wg.type.name }}{% endblock %} {% block morecss %} #id_instructions { @@ -14,22 +14,23 @@ form.conclude .actions { {% endblock %} {% block content %} -

    Request closing of {{ wg.acronym }}

    +

    Request closing of {{ wg.acronym }} {{ wg.type.name }}

    Please provide instructions regarding the disposition of each active Internet-Draft (such as to withdraw the draft, move it to - another WG, convert it to an individual submission, and so on), - wording for the closure announcement, and the status of the WG + another group, convert it to an individual submission, and so on), + wording for the closure announcement, and the status of the group mailing list (will it remain open or should it be closed).

    +
    {{ form.as_table }}
    - Back - + Cancel +
    diff --git a/ietf/templates/wgchairs/manage_workflowREDESIGN.html b/ietf/templates/wginfo/customize_workflow.html similarity index 65% rename from ietf/templates/wgchairs/manage_workflowREDESIGN.html rename to ietf/templates/wginfo/customize_workflow.html index 4a385afaf..147267aaf 100644 --- a/ietf/templates/wgchairs/manage_workflowREDESIGN.html +++ b/ietf/templates/wginfo/customize_workflow.html @@ -1,65 +1,45 @@ -{% extends "wginfo/wg_base.html" %} +{% extends "base.html" %} -{% block wg_titledetail %}Manage Workflow{% endblock %} - -{% block pagehead %} -{{ block.super }} - - - -{% endblock pagehead %} +{% block title %}Customize Workflow for {{ group.acronym }} {{ group.type.name }}{% endblock %} {% block morecss %} {{ block.super }} .state-table .inactive, -.tag-table .inactive { - font-style: italic; - color: #666; -} -.state-table .state { - padding-right: 0.6em; -} -.set-next-states { - margin-top: 1em; -} -.set-next-states label { - display: block; - cursor: pointer; -} -.set-next-states label input { - vertical-align: middle; -} -.set-state input, .set-tag input { - width: 6em; -} -.toggled { - display: none; -} -.toggler { - color: #000; +.tag-table .inactive { font-style: italic; color: #666; } +.state-table .state { margin-bottom: 0.1em; } +.state-table .set-next-states label { display: block; cursor: pointer; } +.state-table .set-next-states label input { vertical-align: middle; } +.state-table .set-state input, .set-tag input { width: 6em; } +.state-table .toggled { display: none; } +.state-table .toggler { + color: #444; + background: #ddd; + font-weight: bold; text-decoration: none; - padding: 0px 3px; + padding: 1px 3px; display: inline-block; margin-left: 0.5em; - font-size: 15px; - font-weight: bold; -} -.inactive .toggler { - color: #666; -} -.toggler:hover { - background-color: #999; - color: #fff; + margin-top: 0.5em; + margin-bottom: 0.5em; } +.state-table .inactive .toggler { color: #aaa; background: #eee; } +.state-table .toggler:hover { color: #fff; background-color: #999; } {% endblock %} -{% block wg_content %} -
    -

    Edit workflow

    +{% block content %} +{% load ietf_filters %} -

    Below you can customize the draft states and tags used in the {{ wg.acronym }} WG. Note that some states are mandatory for WG operation and cannot be deactivated.

    +
    + +

    Customize Workflow for {{ group.acronym }} {{ group.type.name }}

    + +

    Below you can customize the draft states and tags used in the +{{ group.acronym }} {{ group.type.name }}. Note that some states are +mandatory for group operation and cannot be deactivated.

    + +

    You can see the default Working Group I-D State Diagram +in Section 4.1 of RFC6174.

    -

    You can see the default Working Group I-D State Diagram in Section 4.1 of RFC6174.

    States

    @@ -90,20 +70,16 @@ {% endif %} -
    - {{ state.name }} {% if not state.used %} (not used in {{ wg.acronym }}){% endif %} - + -
    -
    {{ state.desc|safe|linebreaks }}
    + {{ state.name }} {% if not state.used %} (not used in {{ group.acronym }}){% endif %} {{ state|statehelp }}
    {% if state.used_next_states %} - {% for n in state.used_next_states %}{{ n.name }}{% if not forloop.last %} {% endif %}{% endfor %} + {% for n in state.used_next_states %}
    {{ n.name }}
    {% endfor %} {% else %} - None +
    None
    {% endif %} - + + + customize
    Select the next states: @@ -142,11 +118,14 @@ - {{ tag.name }} {% if not tag.used %} (not used in {{ wg.acronym }}){% endif %} + {{ tag.name }} {% if not tag.used %} (not used in {{ group.acronym }}){% endif %} {% endfor %} +{% endblock content %} + +{% block js %} - {% endblock %} diff --git a/ietf/templates/wginfo/edit.html b/ietf/templates/wginfo/edit.html index ad77789a9..8af9917b8 100644 --- a/ietf/templates/wginfo/edit.html +++ b/ietf/templates/wginfo/edit.html @@ -9,6 +9,7 @@ Start chartering new WG {% endblock %} {% block morecss %} +form.edit td { padding-bottom: .5em; } form.edit #id_name, form.edit #id_list_email, form.edit #id_list_subscribe, @@ -16,8 +17,7 @@ form.edit #id_list_archive, form.edit #id_urls, form.edit #id_comments { width: 400px; } form.edit input[type=checkbox] { vertical-align: middle; } -ul.errorlist { border-width: 0px; padding: 0px; margin: 0px;} -ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; } +form.edit #id_urls { height: 4em; } {% endblock %} {% block pagehead %} @@ -27,28 +27,30 @@ ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; } {% block content %} {% load ietf_filters %}

    -{% ifequal action "edit" %} +{% if action == "edit" %} Edit WG {{ wg.acronym }} {% else %} - {% ifequal action "charter" %} + {% if action == "charter" %} Start chartering new WG {% else %} Create new WG or BoF - {% endifequal %} -{% endifequal %} + {% endif %} +{% endif %}

    +

    Note that persons with authorization to manage information, e.g. +chairs and delegates, need a Datatracker account to actually do +so. New accounts can be created here.

    +
    {% for field in form.visible_fields %} @@ -65,16 +67,16 @@ Create new WG or BoF
    {{ field.label_tag }}: {% if field.field.required %}*{% endif %} {{ field }} - {% ifequal field.name "ad" %} - {% if user|has_role:"Area Director" %} + {% if field.name == "ad" and user|has_role:"Area Director" %} {% endif %} - {% endifequal %} {% if field.help_text %}
    {{ field.help_text }}
    {% endif %} {{ field.errors }}
    - {% ifequal action "edit" %} - Back - + {% if action == "edit" %} + Cancel + {% else %} - {% ifequal action "charter" %} + {% if action == "charter" %} {% else %} - {% endifequal %} - {% endifequal %} + {% endif %} + {% endif %}
    diff --git a/ietf/templates/wginfo/edit_milestones.html b/ietf/templates/wginfo/edit_milestones.html index ccbf0b3dd..e3a3a8a12 100644 --- a/ietf/templates/wginfo/edit_milestones.html +++ b/ietf/templates/wginfo/edit_milestones.html @@ -42,7 +42,7 @@ tr.milestone.add { font-style: italic; }

    Links: - {{ group.acronym }} {{ group.type.name }} + {{ group.acronym }} {{ group.type.name }} - {{ group.charter.canonical_name }}

    @@ -91,7 +91,7 @@ this list to the milestones currently in use for the {{ group.acronym }} {{ diff --git a/ietf/templates/wginfo/wg_base.html b/ietf/templates/wginfo/group_base.html similarity index 54% rename from ietf/templates/wginfo/wg_base.html rename to ietf/templates/wginfo/group_base.html index cbb96c240..a229f60cc 100644 --- a/ietf/templates/wginfo/wg_base.html +++ b/ietf/templates/wginfo/group_base.html @@ -32,8 +32,8 @@ 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. {% endcomment %} -{% load ietf_filters wgchairs_tags %} -{% block title %}{{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}) - {% block wg_titledetail %}{% endblock %}{% endblock %} +{% load ietf_filters %} +{% block title %}{{ group.name }} ({{ group.acronym }}) - {% block group_subtitle %}{% endblock %}{% endblock %} {% block morecss %} .ietf-navset { @@ -45,39 +45,46 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .ietf-navset .selected { font-weight:bold; padding: 0 3px; } .ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } -.ietf-wg-details { float:right; padding: 4px;margin-top:16px; margin-left: 16px; } -.ietf-wg-details tr { vertical-align: top; } +.ietf-group-details { float:right; padding: 4px;margin-top:16px; margin-left: 16px; } +.ietf-group-details tr { vertical-align: top; } .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;} -.ietf-box th { - font-weight: bold; - padding-top: 1em; - text-align: left; -} -.ietf-box tr:first-child th { - padding-top: 0; -} +.ietf-box th { font-weight: bold; padding-top: 1em; text-align: left; } +.ietf-box tr:first-child th { padding-top: 0; } {% endblock morecss %} {% block content %} -
    -

    {{wg.group_acronym.name}} ({{wg.group_acronym.acronym}}){% if concluded %}
    (concluded {% if wg.state_id == "bof-conc" %}BoF{% else %}WG{% endif %}){% endif %}{% if proposed %}
    (proposed WG){% endif %}

    +
    + +

    {{ group.name}} ({{ group.acronym }}) + {% if group.state_id == "dormant" or group.state_id == "conclude" %}
    (concluded {{ group.type.name }}){% endif %} + {% if group.state_id == "proposed" %}
    (proposed {{ group.type.name }}){% endif %} +

    -{% ifequal selected "documents" %}Documents{% else %}Documents{% endifequal %} | -{% ifequal selected "charter" %}Charter{% else %}Charter{% endifequal %} | -{% wgchairs_admin_options wg %} -History | -{% if wg.clean_email_archive|startswith:"http:" or wg.clean_email_archive|startswith:"https:" or wg.clean_email_archive|startswith:"ftp:" %} -List Archive » | -{% endif %} -Tools WG Page » +
    + Documents | + Charter | + History | + {% if group.list_archive|startswith:"http:" or group.list_archive|startswith:"https:" or group.list_archive|startswith:"ftp:" %} + List Archive » | + {% endif %} + Tools WG Page » +
    + + {% if menu_actions %} +
    + {% for name, url in menu_actions %} + {{ name }} + {% endfor %} +
    + {% endif %}
    -{% block wg_content %} -{% endblock wg_content %} +{% block group_content %} +{% endblock group_content %}
    {% endblock content %} diff --git a/ietf/templates/wginfo/group_charter.html b/ietf/templates/wginfo/group_charter.html new file mode 100644 index 000000000..1f39ac850 --- /dev/null +++ b/ietf/templates/wginfo/group_charter.html @@ -0,0 +1,168 @@ +{% extends "wginfo/group_base.html" %} + +{% load ietf_filters %} +{% block group_subtitle %}Charter{% endblock %} + +{% block morecss %} +{{ block.super }} +h2 a.button { margin-left: 0.5em; font-size: 13px; } +{% endblock %} + +{% block group_content %} +
    + +{% if group.state_id == "conclude" %} +Note: The data for concluded WGs +is occasionally incorrect. +{% endif %} + + + + + + + + + + + + {% if group.parent %} + + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + {% if group.techadvisors %} + + + + + {% endif %} + + {% if group.editors %} + + + + + {% endif %} + + {% if group.secretaries %} + + + + + {% endif %} + + {% if group.delegates %} + + + + + {% endif %} + + + + + + + + {% if group.state_id != "conclude" %} + + + + + + + + + + + + {% endif %} + +
    Group
    Name:{{ group.name }}
    Acronym:{{ group.acronym }}
    {{ group.parent.type.name }}:{{ group.parent.name }} ({{ group.parent.acronym }})
    State:{{ group.state.name }} + {% if requested_close %} + (but in the process of being closed) + {% endif %} +
    Charter: + {% if group.charter %} + {{ group.charter.name }}-{{ group.charter.rev }} ({{ group.charter.get_state.name }}) + {% else %} + none + {% if user|has_role:"Area Director,Secretariat" %} + - Submit Charter + {% endif %} + {% endif %} +
    Personnel
    Chair{{ group.chairs|pluralize }}: + {% for chair in group.chairs %} + {{ chair.person.plain_name }} <{{ chair.email.address }}>
    + {% endfor %} +
    Area Director: + {% if group.areadirector %} + {{ group.areadirector.person.plain_name }} <{{ group.areadirector.address }}> + {% else %}?{% endif %} +
    Tech Advisor{{ group.techadvisors|pluralize }}: + {% for techadvisor in group.techadvisors %} + {{ techadvisor.person.plain_name }} <{{ techadvisor.email.address }}>
    + {% endfor %} +
    Editor{{ group.editors|pluralize }}: + {% for editor in group.editors %} + {{ editor.person.plain_name }} <{{ editor.email.address }}>
    + {% endfor %} +
    Secretar{{ group.secretaries|pluralize:"y,ies" }}: + {% for secretary in group.secretaries %} + {{ secretary.person.plain_name }} <{{ secretary.email.address }}>
    + {% endfor %} +
    Delegate{{ group.delegates|pluralize }}: + {% for delegate in group.delegates %} + {{ delegate.person.plain_name }} <{{ delegate.email.address }}>
    + {% endfor %} +
    Mailing List
    Address:{{ group.list_email|urlize }}
    To Subscribe:{{ group.list_subscribe|urlize }}
    Archive:{{ group.list_archive|urlize }}
    Jabber Chat
    Room Address:xmpp:{{ group.acronym }}@jabber.ietf.org
    Logs:http://jabber.ietf.org/logs/{{ group.acronym }}/
    + +
    + +{% with group.groupurl_set.all as urls %} +{% if urls %} +

    In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at: + {% for url in urls %} + {{ url.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} +

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

    Charter for {% if group.state_id == "proposed" %}Proposed{% endif %} Working Group

    + +

    {{ group.charter_text|escape|format_charter|safe }}

    + +

    {% if group.state_id == "proposed" %}Proposed{% endif %} Milestones

    + +{% with group.milestones as milestones %} +{% include "wginfo/milestones.html" %} +{% endwith %} + +{% if milestones_in_review %} +

    + {{ milestones_in_review|length }} new milestone{{ milestones_in_review|pluralize }} +currently in Area Director review.

    +{% endif %} + +{% endblock %} diff --git a/ietf/templates/wginfo/group_documents.html b/ietf/templates/wginfo/group_documents.html new file mode 100644 index 000000000..a1adc68ef --- /dev/null +++ b/ietf/templates/wginfo/group_documents.html @@ -0,0 +1,18 @@ +{% extends "wginfo/group_base.html" %} + +{% block group_subtitle %}Documents{% endblock %} + +{% block group_content %} +
    + +{% include "doc/search/search_results.html" %} + +{% with docs_related as docs %}{% with meta_related as meta %}{% include "doc/search/search_results.html" %}{% endwith %}{% endwith %} + +
    +{% endblock group_content %} + +{% block js %} + + +{% endblock %} diff --git a/ietf/templates/wginfo/group_entry.txt b/ietf/templates/wginfo/group_entry.txt new file mode 100644 index 000000000..cd8d4b60c --- /dev/null +++ b/ietf/templates/wginfo/group_entry.txt @@ -0,0 +1,4 @@ +{% for chair in group.chairs %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person.plain_name }} <{{ chair.email.address }}> +{% endfor %} WG Mail: {{ group.list_email }} + To Join: {{ group.list_subscribe }} + Archive: {{ group.list_archive }} diff --git a/ietf/templates/wginfo/group_entry_with_charter.txt b/ietf/templates/wginfo/group_entry_with_charter.txt new file mode 100644 index 000000000..abde2531a --- /dev/null +++ b/ietf/templates/wginfo/group_entry_with_charter.txt @@ -0,0 +1,48 @@ +{% autoescape off %}{% load ietf_filters %}{{ group.name }} ({{group.acronym}}) +{{ group.name|dashify }}{{ group.acronym|dashify }}--- + + Charter + Last Modified: {{ group.time.date|date }} + + Current Status: {{ group.state.name }} + + Chair{{ group.chairs|pluralize }}: +{% for chair in group.chairs %} {{ chair.person.name }} <{{chair.email.address}}> +{% endfor %} + {{ group.area.name}} Directors: +{% for ad in group.area.ads %} {{ ad.person.plain_name }} <{{ ad.email.address }}> +{% endfor %} +{% if group.areadirector %} {{ group.area.name }} Advisor: + {{ group.areadirector.person.plain_name }} <{{ group.areadirector.address }}> +{% endif %}{% if group.techadvisors %} + Tech Advisor{{ group.techadvisors|pluralize }}: +{% for techadvisor in group.techadvisors %} {{ techadvisor.person.plain_name }} <{{ techadvisor.email.address }}> +{% endfor %}{% endif %}{% if group.editors %} + Editor{{ group.editors|pluralize }}: +{% for editor in group.editors %} {{ editor.person.plain_name }} <{{ editor.email.address }}> +{% endfor %}{% endif %}{% if group.secretaries %} + Secretar{{ group.secretaries|pluralize:"y,ies" }}: +{% for secretary in group.secretaries %} {{ secretary.person.plain_name }} <{{ secretary.email.address }}> +{% endfor %}{% endif %} + Mailing Lists: + General Discussion: {{ group.list_email }} + To Subscribe: {{ group.list_subscribe }} + Archive: {{ group.list_archive }} + +Description of Working Group: + + {{ group.charter_text|indent }} + +Goals and Milestones: +{% for milestone in group.milestones %} {% if milestone.resolved %}{{ milestone.resolved }} {% else %}{{ milestone.due|date:"M Y" }}{% endif %} - {{ milestone.desc }} +{% endfor %} +Internet-Drafts: +{% for alias in group.drafts %} - {{ alias.document.title }} [{{ alias.name }}-{{ alias.document.rev }}] ({{ alias.document.pages }} pages) +{% endfor %} +{% if group.rfcs %}Requests for Comments: +{% for alias in group.rfcs %} {{ alias.name.upper }}: {{ alias.document.title}} ({{ alias.document.pages }} pages){% for r in alias.rel %} + * {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %} + * {% if r.relationsship == "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endif %} {{ r.source.canonical_name|upper }}{% endfor %} +{% endfor %} +{% else %}No Requests for Comments{% endif %} +{% endautoescape %} diff --git a/ietf/templates/wginfo/history.html b/ietf/templates/wginfo/history.html index ed86d4324..bc1926609 100644 --- a/ietf/templates/wginfo/history.html +++ b/ietf/templates/wginfo/history.html @@ -1,15 +1,16 @@ -{% extends "wginfo/wg_base.html" %} +{% extends "wginfo/group_base.html" %} {% load ietf_filters %} -{% block wg_titledetail %}History{% endblock %} +{% block group_subtitle %}History{% endblock %} -{% block wg_content %} +{% block group_content %} {% load ietf_filters %} -

    WG History

    +

    Group History

    + {% for e in events %} diff --git a/ietf/templates/wginfo/wg-charter.txt b/ietf/templates/wginfo/wg-charter.txt deleted file mode 100644 index c0d8af0d4..000000000 --- a/ietf/templates/wginfo/wg-charter.txt +++ /dev/null @@ -1,47 +0,0 @@ -{% if USE_DB_REDESIGN_PROXY_CLASSES %}{% include "wginfo/wg-charterREDESIGN.txt" %}{% else %}{% load ietf_filters %}{{wg.group_acronym.name|safe}} ({{wg}}) -{{ wg.group_acronym.name|dashify }}{{ wg.group_acronym.acronym|dashify }}--- - - Charter - Last Modified: {{ wg.last_modified_date }} - - Current Status: {{ wg.status }} - - Chair{{ wg.chairs.count|pluralize:",s" }}: -{% for chair in wg.chairs %} {{ chair.person|safe }} <{{chair.person.email.1}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Directors: -{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.person.email.1}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Advisor: - {{ wg.area_director.person|safe }} <{{wg.area_director.person.email.1}}> -{% if wg.wgtechadvisor_set.count %} - Tech Advisor{{ wg.wgtechadvisor_set.count|pluralize:",s" }}: -{% for techadvisor in wg.wgtechadvisor_set.all %} {{ techadvisor.person|safe }} <{{techadvisor.person.email.1}}> -{% endfor %}{% endif %}{% if wg.wgeditor_set.count %} - Editor{{ wg.wgeditor_set.count|pluralize:",s" }}: -{% for editor in wg.wgeditor_set.all %} {{ editor.person|safe }} <{{editor.person.email.1}}> -{% endfor %}{% endif %}{% if wg.secretaries %} - Secretar{{ wg.secretaries.count|pluralize:"y,ies" }}: -{% for secretary in wg.secretaries %} {{ secretary.person|safe }} <{{secretary.person.email.1}}> -{% endfor %}{% endif %} - Mailing Lists: - General Discussion: {{ wg.email_address }} - To Subscribe: {{ wg.email_subscribe }} - Archive: {{ wg.email_archive }} - -Description of Working Group: - - {{ wg.charter_text|indent|safe }} - -Goals and Milestones: -{% for milestone in wg.milestones %} {% ifequal milestone.done 'Done' %}Done {% else %}{%ifequal milestone.expected_due_date.month 1 %}Jan{% endifequal %}{%ifequal milestone.expected_due_date.month 2 %}Feb{% endifequal %}{%ifequal milestone.expected_due_date.month 3 %}Mar{% endifequal %}{%ifequal milestone.expected_due_date.month 4 %}Apr{% endifequal %}{%ifequal milestone.expected_due_date.month 5 %}May{% endifequal %}{%ifequal milestone.expected_due_date.month 6 %}Jun{% endifequal %}{%ifequal milestone.expected_due_date.month 7 %}Jul{% endifequal %}{%ifequal milestone.expected_due_date.month 8 %}Aug{% endifequal %}{%ifequal milestone.expected_due_date.month 9 %}Sep{% endifequal %}{%ifequal milestone.expected_due_date.month 10 %}Oct{% endifequal %}{%ifequal milestone.expected_due_date.month 11 %}Nov{% endifequal %}{%ifequal milestone.expected_due_date.month 12 %}Dec{% endifequal %} {{ milestone.expected_due_date.year }}{% endifequal %} - {{ milestone.description|safe }} -{% endfor %} -Internet-Drafts: -{% for draft in wg.drafts %} - {{draft.title|safe}} [{{draft.filename}}-{{draft.revision}}] ({{ draft.txt_page_count }} pages) -{% endfor %} -{% if wg.rfcs %}Requests for Comments: -{% for rfc in wg.rfcs %} {{rfc}}: {{rfc.title|safe}} ({{ rfc.txt_page_count }} pages){% for obs in rfc.obsoletes%} - * {{obs.action}} RFC{{obs.rfc_acted_on_id}}{% endfor %}{% for obs in rfc.obsoleted_by%} - * {%ifequal obs.action 'Obsoletes'%}OBSOLETED BY{%else%}Updated by{%endifequal%} RFC{{obs.rfc_id}}{% endfor %} -{%endfor%} -{%else%}No Requests for Comments{% endif %}{% endif %} diff --git a/ietf/templates/wginfo/wg-charterREDESIGN.txt b/ietf/templates/wginfo/wg-charterREDESIGN.txt deleted file mode 100644 index 30e84e6ed..000000000 --- a/ietf/templates/wginfo/wg-charterREDESIGN.txt +++ /dev/null @@ -1,47 +0,0 @@ -{% load ietf_filters %}{{wg.name|safe}} ({{wg.acronym}}) -{{ wg.name|dashify }}{{ wg.acronym|dashify }}--- - - Charter - Last Modified: {{ wg.time.date }} - - Current Status: {{ wg.state.name }} - - Chair{{ wg.chairs|pluralize }}: -{% for chair in wg.chairs %} {{ chair.person.name|safe }} <{{chair.address}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Directors: -{% for ad in wg.area_directors %} {{ ad.person|safe }} <{{ad.address}}> -{% endfor %} - {{wg.area.area.area_acronym.name}} Advisor: - {{ wg.areadirector.person.name|safe }} <{{wg.areadirector.address}}> -{% if wg.techadvisors %} - Tech Advisor{{ wg.techadvisors|pluralize }}: -{% for techadvisor in wg.techadvisors %} {{ techadvisor.person.plain_name|safe }} <{{techadvisor.address}}> -{% endfor %}{% endif %}{% if wg.editors %} - Editor{{ wg.editors|pluralize }}: -{% for editor in wg.editors %} {{ editor.person.plain_name|safe }} <{{editor.address}}> -{% endfor %}{% endif %}{% if wg.secretaries %} - Secretar{{ wg.secretaries|pluralize:"y,ies" }}: -{% for secretary in wg.secretaries %} {{ secretary.person.plain_name|safe }} <{{secretary.address}}> -{% endfor %}{% endif %} - Mailing Lists: - General Discussion: {{ wg.email_address }} - To Subscribe: {{ wg.email_subscribe }} - Archive: {{ wg.email_archive }} - -Description of Working Group: - - {{ wg.charter_text|indent|safe }} - -Goals and Milestones: -{% for milestone in wg.milestones %} {% if milestone.resolved %}{{ milestone.resolved }} {% else %}{{ milestone.due|date:"M Y" }}{% endif %} - {{ milestone.desc|safe }} -{% endfor %} -Internet-Drafts: -{% for alias in wg.drafts %} - {{alias.document.title|safe}} [{{alias.name}}-{{alias.document.rev}}] ({{ alias.document.pages }} pages) -{% endfor %} -{% if wg.rfcs %}Requests for Comments: -{% for alias in wg.rfcs %} {{ alias.name.upper }}: {{ alias.document.title|safe}} ({{ alias.document.pages }} pages){% for r in alias.rel %} - * {{ r.action }} {{ r.target.name|upper }}{% endfor %}{% for r in alias.invrel %} - * {% ifequal r.relationsship "obs" %}{{ r.inverse_action|upper }}{% else %}{{ r.action }}{% endifequal %} {{ r.source.canonical_name|upper }}{% endfor %} -{%endfor%} -{%else%}No Requests for Comments{% endif %} diff --git a/ietf/templates/wginfo/wg-dir.html b/ietf/templates/wginfo/wg-dir.html deleted file mode 100644 index b9cfb208b..000000000 --- a/ietf/templates/wginfo/wg-dir.html +++ /dev/null @@ -1,99 +0,0 @@ -{% extends "base.html" %} -{# Copyright The IETF Trust 2009, All Rights Reserved #} -{% comment %} -Portion Copyright (C) 2010 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. -{% endcomment %} - -{% block title %}Active IETF Working Groups{% endblock %} - -{% block morecss %} -.ietf-wg-table { width: 100%; max-width:50em; } -.ietf-wg-table tr { vertical-align:top; } -{% endblock morecss %} - -{% block content %} -

    Active IETF Working Groups

    - -

    See also: - Concluded Working Groups (www.ietf.org), - Concluded Working Groups (tools.ietf.org), - Historic Charters. -

    - - {% for area in areas|dictsort:"area_acronym.name" %} -

    {{ area.area_acronym.name }}

    - - {% for ad in area.areadirector_set.all|dictsort:"person.last_name" %} - {% if forloop.first %} -

    Area Director{{ forloop.revcounter|pluralize }}:

    -
    DateByText
    {{ e.time|date:"Y-m-d"}}
    - {% endif %} - - {% if forloop.last %} -
      {{ ad.person }} <{{ ad.person.email.1 }}>
    - {% endif %} - {% endfor %} - - {% for url in area.additional_urls %} - {% if forloop.first %} -

    Area Specific Web Page{{ forloop.revcounter|pluralize}}:

    -

    - {% endif %} - {{ url.description }}{% if not forloop.last %}
    {% endif %} - {% if forloop.last %} -

    - {% endif %} - {% endfor %} - - {% for wg in area.active_wgs %} - {% if forloop.first %} -

    Active Working Groups:

    -
    - - {% endif %} - - - - - - - {% if forloop.last %} -
    {{ wg }}{% for ad in area.areadirector_set.all|dictsort:"person.last_name" %}{% ifequal ad wg.area_director %}{% endifequal %}{% endfor %}{{ wg.group_acronym.name }}{% for chair in wg.chairs %}{{chair.person}}{% if not forloop.last %}, {% endif %}{% endfor %}
    -
    - {% endif %} - {% empty %} -

    No Active Working Groups

    - {% endfor %}{# wg #} - - {% endfor %}{# area #} -{% endblock %} diff --git a/ietf/templates/wginfo/wg_charter.html b/ietf/templates/wginfo/wg_charter.html deleted file mode 100644 index e4797950b..000000000 --- a/ietf/templates/wginfo/wg_charter.html +++ /dev/null @@ -1,182 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% comment %} -Copyright (C) 2010 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. -{% endcomment %} -{% load ietf_filters %} -{% block wg_titledetail %}Charter{% endblock %} - -{% block morecss %} -{{ block.super }} -h2 a.button { margin-left: 0.5em; font-size: 13px; } -{% endblock %} - -{% block wg_content %} -
    -{% if concluded %} -Note: The data for concluded WGs -is occasionally incorrect. -{% endif %} - - - - - - - - - - - {% if wg.parent %} - - {% endif %} - - - - - - - - - - - - - - - - - - - - - - {% if wg.techadvisors %} - - - - - {% endif %} - {% if wg.editors %} - - - {% endif %} - {% if wg.secretaries %} - - - - - {% endif %} - - - - - - - - {% if not concluded %} - - - - - {% endif %} - -
    Group
    Name:{{ wg.name }}
    Acronym:{{ wg.acronym }}
    Area:{{ wg.parent.name }} ({{ wg.parent.acronym }})
    State:{{ wg.state.name }} - {% if requested_close %} - (but in the process of being closed) - {% endif %} -
    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 %} -

    In addition to the charter maintained by the IETF Secretariat, there is additional information about this working group on the Web at: -{% for url in wg.additional_urls %} -{{ url.name }}{% if not forloop.last %}, {% endif %} -{% endfor %} -

    -{% endif %} - -

    Charter for {% if wg.state_id == "proposed" %}Proposed{% endif %} Working Group

    -

    {{ wg.charter_text|escape|format_charter|safe }}

    - -

    {% if wg.state_id == "proposed" %}Proposed{% endif %} Milestones -{% if wg.state_id != "proposed" %} -{% if user|has_role:"Area Director,Secretariat" or is_chair %} -Add or edit milestones -{% endif %} -{% endif %} -

    - -{% with wg.milestones as milestones %}{% include "wginfo/milestones.html" %}{% endwith %} - -{% if milestones_in_review %} -

    + {{ milestones_in_review|length }} new milestone{{ milestones_in_review|pluralize }} -currently in Area Director review.

    -{% endif %} -{% endblock wg_content %} diff --git a/ietf/templates/wginfo/wg_documents.html b/ietf/templates/wginfo/wg_documents.html deleted file mode 100644 index f0fb4529b..000000000 --- a/ietf/templates/wginfo/wg_documents.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends "wginfo/wg_base.html" %} -{% comment %} -Copyright (C) 2010 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. -{% endcomment %} -{% block wg_titledetail %}Documents{% endblock %} - -{% block wg_content %} -
    - -{% include "doc/search/search_results.html" %} - -{% with docs_related as docs %}{% include "doc/search/search_results.html" %}{% endwith %} - -
    -{% endblock wg_content %} - -{% block js %} - - -{% endblock %} diff --git a/ietf/templates/wginfo/wg_documents.txt b/ietf/templates/wginfo/wg_documents.txt deleted file mode 100644 index 69302b533..000000000 --- a/ietf/templates/wginfo/wg_documents.txt +++ /dev/null @@ -1,2 +0,0 @@ -{% load ietf_filters %}{% regroup docs by get_state as grouped_docs %}{% for doc_group in grouped_docs %}{% for doc in doc_group.list %}{% include "wginfo/wg_documents_entry.txt" %}{% endfor %}{% endfor %}{% regroup docs_related by get_state as grouped_docs_related %}{% for doc_group in grouped_docs_related %}{% for doc in doc_group.list %}Related {% include "wginfo/wg_documents_entry.txt" %}{% endfor %}{% endfor %} - diff --git a/ietf/templates/wginfo/wg_documents_entry.txt b/ietf/templates/wginfo/wg_documents_entry.txt deleted file mode 100644 index ad034af62..000000000 --- a/ietf/templates/wginfo/wg_documents_entry.txt +++ /dev/null @@ -1 +0,0 @@ -{% load ietf_filters %}{{doc_group.grouper}} {% if doc.get_state_slug == "rfc" %}{{doc.rfc_number}} {{doc.title|clean_whitespace}}{% else %}{{doc.name}}-{{doc.rev}} {{doc.title|clean_whitespace}}{% endif %} diff --git a/ietf/templates/wginfo/wg_summary.txt b/ietf/templates/wginfo/wg_summary.txt deleted file mode 100644 index 603d9e2f8..000000000 --- a/ietf/templates/wginfo/wg_summary.txt +++ /dev/null @@ -1,5 +0,0 @@ -{% for chair in wg.wgchair_set.all %}{% if forloop.first %} Chair{{ forloop.revcounter|pluralize:": ,s:" }} {% else %} {% endif %}{{ chair.person|safe }} <{{ chair.person.email.1 }}> -{% endfor %} WG Mail: {{ wg.email_address }} - To Join: {{ wg.email_subscribe }}{%if wg.email_keyword %} - In Body: {{ wg.email_keyword|safe }}{% endif %} - Archive: {{ wg.email_archive }} diff --git a/ietf/urls.py b/ietf/urls.py index 861f5fc55..fc14c83d7 100644 --- a/ietf/urls.py +++ b/ietf/urls.py @@ -71,7 +71,6 @@ urlpatterns = patterns('', (r'^secr/', include('ietf.secr.urls')), (r'^sitemap-(?P
    .+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}), (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', { 'sitemaps': sitemaps}), - (r'^streams/', include('ietf.ietfworkflows.urls')), (r'^submit/', include('ietf.submit.urls')), (r'^sync/', include('ietf.sync.urls')), (r'^wg/', include('ietf.wginfo.urls')), diff --git a/ietf/utils/accesstoken.py b/ietf/utils/accesstoken.py new file mode 100644 index 000000000..da07c9a3a --- /dev/null +++ b/ietf/utils/accesstoken.py @@ -0,0 +1,15 @@ +import time, random, hashlib + +from django.conf import settings + +def generate_random_key(max_length=32): + """Generate a random access token.""" + return hashlib.sha256(settings.SECRET_KEY + ("%.16f" % time.time()) + ("%.16f" % random.random())).hexdigest()[:max_length] + +def generate_access_token(key, max_length=32): + """Make an access token out of key.""" + assert key, "key must not be empty" + # we hash it with the private key to make sure only we can + # generate and use the final token - so storing the key in the + # database is safe + return hashlib.sha256(settings.SECRET_KEY + key).hexdigest()[:max_length] diff --git a/ietf/utils/mail.py b/ietf/utils/mail.py index a99962b36..669fda92b 100644 --- a/ietf/utils/mail.py +++ b/ietf/utils/mail.py @@ -176,8 +176,13 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F if extra: for k, v in extra.items(): if v: - msg[k] = v - if test_mode or settings.SERVER_MODE == 'production': + msg[k] = v + # start debug server with python -m smtpd -n -c DebuggingServer localhost:2025 + # then put USING_DEBUG_EMAIL_SERVER=True and EMAIL_HOST='localhost' + # and EMAIL_PORT=2025 in settings_local.py + debugging = getattr(settings, "USING_DEBUG_EMAIL_SERVER", False) and settings.EMAIL_HOST == 'localhost' and settings.EMAIL_PORT == 2025 + + if test_mode or debugging or settings.SERVER_MODE == 'production': send_smtp(msg, bcc) elif settings.SERVER_MODE == 'test': if toUser: @@ -188,7 +193,7 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=F copy_to = settings.EMAIL_COPY_TO except AttributeError: copy_to = "ietf.tracker.archive+%s@gmail.com" % settings.SERVER_MODE - if copy_to and not test_mode: # if we're running automated tests, this copy is just annoying + if copy_to and not test_mode and not debugging: # if we're running automated tests, this copy is just annoying if bcc: msg['X-Tracker-Bcc']=bcc copy_email(msg, copy_to,originalBcc=bcc) diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index 8d697b278..67df82345 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -1,7 +1,7 @@ from django.conf import settings from django.contrib.auth.models import User -from ietf.iesg.models import TelechatDate, WGAction +from ietf.iesg.models import TelechatDate from ietf.ipr.models import IprDetail, IprDocAlias from ietf.meeting.models import Meeting from ietf.doc.models import * @@ -112,15 +112,6 @@ def make_test_data(): ) group.charter = charter group.save() - WGAction.objects.create( - pk=group.pk, - note="", - status_date=datetime.date.today(), - agenda=1, - token_name="Aread", - category=13, - telechat_date=date2 - ) # persons diff --git a/ietf/wgchairs/.gitignore b/ietf/wgchairs/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/wgchairs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/wgchairs/__init__.py b/ietf/wgchairs/__init__.py deleted file mode 100644 index e8d53c9a3..000000000 --- a/ietf/wgchairs/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# coding: latin-1 - -from types import ModuleType -import urls, models, views, forms, accounts - -# These people will be sent a stack trace if there's an uncaught exception in -# code any of the modules imported above: -DEBUG_EMAILS = [ - ('Emilio A. Sánchez', 'esanchez@yaco.es'), -] - -for k in locals().keys(): - m = locals()[k] - if isinstance(m, ModuleType): - if hasattr(m, "DEBUG_EMAILS"): - DEBUG_EMAILS += list(getattr(m, "DEBUG_EMAILS")) - setattr(m, "DEBUG_EMAILS", DEBUG_EMAILS) - diff --git a/ietf/wgchairs/accounts.py b/ietf/wgchairs/accounts.py deleted file mode 100644 index c6b5b7645..000000000 --- a/ietf/wgchairs/accounts.py +++ /dev/null @@ -1,108 +0,0 @@ -from django.conf import settings -from ietf.group.models import Role - -def is_secretariat(user): - if not user or not user.is_authenticated(): - return False - return bool(user.groups.filter(name='Secretariat')) - - -def is_area_director_for_group(person, group): - return bool(group.area.area.areadirector_set.filter(person=person).count()) - -def is_area_director_for_groupREDESIGN(person, group): - return bool(Role.objects.filter(group=group.parent, person=person, name__in=("ad", "pre-ad"))) - - -def is_group_chair(person, group): - if group.chairs().filter(person=person): - return True - return False - -def is_group_chairREDESIGN(person, group): - return bool(Role.objects.filter(group=group, person=person, name="chair")) - - -def is_group_delegate(person, group): - return bool(group.wgdelegate_set.filter(person=person).count()) - -def is_group_delegateREDESIGN(person, group): - return bool(Role.objects.filter(group=group, person=person, name="delegate")) - - -def get_person_for_user(user): - try: - return user.get_profile().person() - except: - return None - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.liaisons.accounts import is_secretariat, get_person_for_user - is_area_director_for_group = is_area_director_for_groupREDESIGN - is_group_chair = is_group_chairREDESIGN - is_group_delegate = is_group_delegateREDESIGN - - -def can_do_wg_workflow_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_do_wg_workflow_in_document(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - return (is_secretariat(user) or can_do_wg_workflow_in_group(document.group.ietfwg)) - - -def can_manage_workflow_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_manage_delegates_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_manage_shepherds_in_group(user, group): - person = get_person_for_user(user) - if not person: - return False - return (is_secretariat(user) or is_group_chair(person, group)) - - -def can_manage_shepherd_of_a_document(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - return can_manage_shepherds_in_group(user, document.group.ietfwg) - - -def can_manage_writeup_of_a_document_no_state(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - group = document.group.ietfwg - return (is_secretariat(user) or - is_group_chair(person, group) or - is_area_director_for_group(person, group) or - is_group_delegate(person, group)) - - -def can_manage_writeup_of_a_document(user, document): - person = get_person_for_user(user) - if not person or not document.group: - return False - return (can_manage_writeup_of_a_document_no_state(user, document) or - person == document.shepherd) - - - diff --git a/ietf/wgchairs/forms.py b/ietf/wgchairs/forms.py deleted file mode 100644 index 001acec7c..000000000 --- a/ietf/wgchairs/forms.py +++ /dev/null @@ -1,478 +0,0 @@ -import datetime - -from django import forms -from django.conf import settings -from django.db.models import Q -from django.forms.models import BaseModelFormSet -from django.template.loader import render_to_string -from django.utils.safestring import mark_safe - -from ietf.wgchairs.models import WGDelegate, ProtoWriteUp -from ietf.wgchairs.accounts import get_person_for_user -from ietf.ietfworkflows.constants import REQUIRED_STATES -from ietf.ietfworkflows.utils import (get_default_workflow_for_wg, get_workflow_for_wg, - update_tags, FOLLOWUP_TAG, get_state_by_name) -from ietf.ietfworkflows.models import AnnotationTag, State -from ietf.idtracker.models import PersonOrOrgInfo -from ietf.utils.mail import send_mail_text - -from workflows.models import Transition - -from ietf.doc.models import WriteupDocEvent -from ietf.person.models import Person, Email -from ietf.group.models import Group, Role, RoleName -from ietf.group.utils import save_group_in_history -from ietf.name.models import DocTagName - - -class RelatedWGForm(forms.Form): - - can_cancel = False - - def __init__(self, *args, **kwargs): - self.wg = kwargs.pop('wg', None) - self.user = kwargs.pop('user', None) - self.message = {} - super(RelatedWGForm, self).__init__(*args, **kwargs) - - def get_message(self): - return self.message - - def set_message(self, msg_type, msg_value): - self.message = {'type': msg_type, - 'value': msg_value, - } - - -class TagForm(RelatedWGForm): - - tags = forms.ModelMultipleChoiceField(AnnotationTag.objects.filter(workflow__name='Default WG Workflow'), - widget=forms.CheckboxSelectMultiple, required=False) - - def save(self): - workflow = get_workflow_for_wg(self.wg) - workflow.selected_tags.clear() - for tag in self.cleaned_data['tags']: - workflow.selected_tags.add(tag) - return workflow - - -class StateForm(RelatedWGForm): - - states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'), - widget=forms.CheckboxSelectMultiple, required=False) - - def update_transitions(self, workflow): - for transition in workflow.transitions.all(): - if not workflow.selected_states.filter(pk=transition.destination.pk).count(): - transition.delete() - continue - for state in transition.states.all(): - if not workflow.selected_states.filter(pk=state.pk).count(): - transition.states.remove(state) - if not transition.states.count(): - transition.delete() - continue - - def save(self): - workflow = get_workflow_for_wg(self.wg) - workflow.selected_states.clear() - for state in self.cleaned_data['states']: - workflow.selected_states.add(state) - for name in REQUIRED_STATES: - rstate = get_state_by_name(name) - if rstate: - workflow.selected_states.add(rstate) - self.update_transitions(workflow) - return workflow - - -class DeleteTransitionForm(RelatedWGForm): - - transitions = forms.ModelMultipleChoiceField(Transition.objects.all(), - widget=forms.CheckboxSelectMultiple) - - def __init__(self, *args, **kwargs): - super(DeleteTransitionForm, self).__init__(*args, **kwargs) - workflow = get_workflow_for_wg(self.wg) - self.fields['transitions'].queryset = self.fields['transitions'].queryset.filter(workflow=workflow) - - def save(self): - for transition in self.cleaned_data['transitions']: - transition.delete() - - -class TransitionForm(forms.ModelForm): - - states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow')) - - class Meta: - model = Transition - fields = ('name', 'states', 'destination', ) - - def __init__(self, *args, **kwargs): - self.wg = kwargs.pop('wg', None) - self.user = kwargs.pop('user', None) - super(TransitionForm, self).__init__(*args, **kwargs) - workflow = get_workflow_for_wg(self.wg) - self.fields['states'].queryset = workflow.selected_states.all() - self.fields['destination'].queryset = workflow.selected_states.all() - self.fields['destination'].required = True - if self.instance.pk: - self.fields['states'].initial = [i.pk for i in self.instance.states.all()] - self.instance.workflow = workflow - - def as_row(self): - return self._html_output(u'%(errors)s%(field)s%(help_text)s', u'%s', '', u'
    %s', False) - - def save(self, *args, **kwargs): - instance = super(TransitionForm, self).save(*args, **kwargs) - for state in self.cleaned_data['states']: - state.transitions.add(instance) - - -class TransitionFormSet(BaseModelFormSet): - - form = TransitionForm - can_delete = True - extra = 2 - max_num = 0 - can_order = False - model = Transition - - def __init__(self, *args, **kwargs): - self.wg = kwargs.pop('wg', None) - self.user = kwargs.pop('user', None) - super(TransitionFormSet, self).__init__(*args, **kwargs) - - def _construct_form(self, i, **kwargs): - kwargs = kwargs or {} - kwargs.update({'wg': self.wg, 'user': self.user}) - return super(TransitionFormSet, self)._construct_form(i, **kwargs) - - def as_table(self): - html = u'' - csscl = 'oddrow' - for form in self.forms: - html += u'' % csscl - html += form.as_row() - html += u'' - if csscl == 'oddrow': - csscl = 'evenrow' - else: - csscl = 'oddrow' - return mark_safe(u'\n'.join([unicode(self.management_form), html])) - - -def workflow_form_factory(request, wg, user): - - if request.POST.get('update_transitions', None): - return TransitionFormSet(wg=wg, user=user, data=request.POST) - elif request.POST.get('update_states', None): - return StateForm(wg=wg, user=user, data=request.POST) - return TagForm(wg=wg, user=user, data=request.POST) - - -class RemoveDelegateForm(RelatedWGForm): - - delete = forms.MultipleChoiceField() - - def __init__(self, *args, **kwargs): - super(RemoveDelegateForm, self).__init__(*args, **kwargs) - self.fields['delete'].choices = [(i.pk, i.pk) for i in self.wg.wgdelegate_set.all()] - - def save(self): - delegates = self.cleaned_data.get('delete') - save_group_in_history(Group.objects.get(pk=self.wg.pk)) - WGDelegate.objects.filter(pk__in=delegates).delete() - self.set_message('success', 'Delegates removed') - -def assign_shepherd(user, internetdraft, shepherd): - if internetdraft.shepherd == shepherd: - return - - from ietf.doc.models import save_document_in_history, DocEvent, Document - - # saving the proxy object is a bit of a mess, so convert it to a - # proper document - doc = Document.objects.get(name=internetdraft.name) - - save_document_in_history(doc) - - doc.time = datetime.datetime.now() - doc.shepherd = shepherd - doc.save() - - e = DocEvent(type="changed_document") - e.time = doc.time - e.doc = doc - e.by = user.get_profile() - if not shepherd: - e.desc = u"Unassigned shepherd" - else: - e.desc = u"Changed shepherd to %s" % shepherd.plain_name() - e.save() - - # update proxy too - internetdraft.shepherd = shepherd - -class AddDelegateForm(RelatedWGForm): - - email = forms.EmailField() - form_type = forms.CharField(widget=forms.HiddenInput, initial='single') - - def __init__(self, *args, **kwargs): - self.shepherd = kwargs.pop('shepherd', False) - super(AddDelegateForm, self).__init__(*args, **kwargs) - self.next_form = self - - def get_next_form(self): - return self.next_form - - def get_person(self, email): - persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).filter( - Q(iesglogin__isnull=False)| - Q(legacywgpassword__isnull=False)| - Q(legacyliaisonuser__isnull=False)).distinct() - if not persons: - raise PersonOrOrgInfo.DoesNotExist - if len(persons) > 1: - raise PersonOrOrgInfo.MultipleObjectsReturned - return persons[0] - - def save(self): - email = self.cleaned_data.get('email') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - try: - person = Person.objects.filter(email__address=email).exclude(user=None).distinct().get() - except Person.DoesNotExist: - self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system') - return - except Person.MultipleObjectsReturned: - self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('multiple', 'There are multiple users with this email in the system') - return - else: - try: - person = self.get_person(email) - except PersonOrOrgInfo.DoesNotExist: - self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system') - return - except PersonOrOrgInfo.MultipleObjectsReturned: - self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd) - self.next_form.set_message('multiple', 'There are multiple users with this email in the system') - return - if self.shepherd: - self.assign_shepherd(person) - else: - self.create_delegate(person) - - def assign_shepherd(self, person): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - assign_shepherd(self.user, self.shepherd, person) - else: - self.shepherd.shepherd = person - self.shepherd.save() - self.next_form = AddDelegateForm(wg=self.wg, user=self.user, shepherd=self.shepherd) - self.next_form.set_message('success', 'Shepherd assigned successfully') - - def create_delegate(self, person): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - created = False - e = Email.objects.get(address=self.cleaned_data.get('email')) - if not Role.objects.filter(name="delegate", group=self.wg, person=person, email=e): - created = True - save_group_in_history(Group.objects.get(pk=self.wg.pk)) - delegate, _ = Role.objects.get_or_create( - name=RoleName.objects.get(slug="delegate"), group=self.wg, person=e.person, email=e) - else: - (delegate, created) = WGDelegate.objects.get_or_create(wg=self.wg, - person=person) - if not created: - self.set_message('error', 'The email belongs to a person who is already a delegate') - else: - self.next_form = AddDelegateForm(wg=self.wg, user=self.user) - self.next_form.set_message('success', 'A new delegate has been added') - - -class MultipleDelegateForm(AddDelegateForm): - - email = forms.EmailField(widget=forms.HiddenInput) - form_type = forms.CharField(widget=forms.HiddenInput, initial='multiple') - persons = forms.ChoiceField(widget=forms.RadioSelect, help_text='Please select one person from the list') - submit_msg = 'Designate as delegate' - - def __init__(self, *args, **kwargs): - self.email = kwargs.pop('email', None) - super(MultipleDelegateForm, self).__init__(*args, **kwargs) - if not self.email: - self.email = self.data.get('email', None) - self.fields['email'].initial = self.email - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - self.fields['persons'].choices = [(i.pk, unicode(i)) for i in Person.objects.filter(email__address=self.email).exclude(user=None).distinct().order_by('name')] - else: - self.fields['persons'].choices = [(i.pk, unicode(i)) for i in PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).filter( - Q(iesglogin__isnull=False)| - Q(legacywgpassword__isnull=False)| - Q(legacyliaisonuser__isnull=False)).distinct().order_by('first_name')] - - def save(self): - person_id = self.cleaned_data.get('persons') - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - person = Person.objects.get(pk=person_id) - else: - person = PersonOrOrgInfo.objects.get(pk=person_id) - if self.shepherd: - self.assign_shepherd(person) - else: - self.create_delegate(person) - - -class NotExistDelegateForm(MultipleDelegateForm): - - email = forms.EmailField(widget=forms.HiddenInput) - form_type = forms.CharField(widget=forms.HiddenInput, initial='notexist') - can_cancel = True - submit_msg = 'Send email to these addresses' - - def __init__(self, *args, **kwargs): - super(NotExistDelegateForm, self).__init__(*args, **kwargs) - self.email_list = [] - del(self.fields['persons']) - - def get_email_list(self): - if self.email_list: - return self.email_list - email_list = [self.email] - email_list.append('IETF Secretariat ') - email_list += ['%s <%s>' % i.person.email() for i in self.wg.wgchair_set.all() if i.person.email()] - self.email_list = email_list - return email_list - - def as_p(self): - email_list = self.get_email_list() - info = render_to_string('wgchairs/notexistdelegate.html', {'email_list': email_list, 'shepherd': self.shepherd}) - return info + super(NotExistDelegateForm, self).as_p() - - def send_email(self, to_email, template): - if self.shepherd: - subject = 'WG shepherd needs system credentials' - else: - subject = 'WG Delegate needs system credentials' - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - persons = Person.objects.filter(email__address=self.email).distinct() - else: - persons = PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).distinct() - body = render_to_string(template, - {'chair': get_person_for_user(self.user), - 'delegate_email': self.email, - 'shepherd': self.shepherd, - 'delegate_persons': persons, - 'wg': self.wg, - }) - - send_mail_text(self.request, to_email, settings.DEFAULT_FROM_EMAIL, subject, body) - - def save(self): - self.next_form = AddDelegateForm(wg=self.wg, user=self.user) - if settings.DEBUG: - self.next_form.set_message('warning', 'Email was not sent cause tool is in DEBUG mode') - else: - # this is ugly... - email_list = self.get_email_list() - delegate = email_list[0] - secretariat = email_list[1] - wgchairs = email_list[2:] - self.send_email(delegate, 'wgchairs/notexistsdelegate_delegate_email.txt') - self.send_email(secretariat, 'wgchairs/notexistsdelegate_secretariat_email.txt') - self.send_email(wgchairs, 'wgchairs/notexistsdelegate_wgchairs_email.txt') - self.next_form.set_message('success', 'Email sent successfully') - - -def add_form_factory(request, wg, user, shepherd=False): - if request.method != 'POST' or request.POST.get('update_shepehrd'): - return AddDelegateForm(wg=wg, user=user, shepherd=shepherd) - - if request.POST.get('form_type', None) == 'multiple': - f = MultipleDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd) - elif request.POST.get('form_type', None) == 'notexist': - f = NotExistDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd) - elif request.POST.get('form_type', None) == 'single': - f = AddDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd) - else: - f = AddDelegateForm(wg=wg, user=user, shepherd=shepherd) - - f.request = request - return f - -class WriteUpEditForm(RelatedWGForm): - - writeup = forms.CharField(widget=forms.Textarea, required=False) - followup = forms.BooleanField(required=False) - comment = forms.CharField(widget=forms.Textarea, required=False) - - def __init__(self, *args, **kwargs): - self.doc = kwargs.pop('doc', None) - self.doc_writeup = self.doc.protowriteup_set.all() - if self.doc_writeup.count(): - self.doc_writeup = self.doc_writeup[0] - else: - self.doc_writeup = None - super(WriteUpEditForm, self).__init__(*args, **kwargs) - self.person = get_person_for_user(self.user) - - def get_writeup(self): - return self.data.get('writeup', self.doc_writeup and self.doc_writeup.writeup or '') - - def save(self): - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - e = WriteupDocEvent(type="changed_protocol_writeup") - e.doc = self.doc - e.by = self.person - e.desc = e.get_type_display() - e.text = self.cleaned_data['writeup'] - e.save() - from ietf.wgchairs.models import ProtoWriteUpProxy - self.doc_writeup = ProtoWriteUpProxy.objects.get(pk=e.pk) - else: - if not self.doc_writeup: - self.doc_writeup = ProtoWriteUp.objects.create( - person=self.person, - draft=self.doc, - writeup=self.cleaned_data['writeup']) - else: - self.doc_writeup.writeup = self.cleaned_data['writeup'] - self.doc_writeup.save() - - if self.data.get('modify_tag', False): - followup = self.cleaned_data.get('followup', False) - comment = self.cleaned_data.get('comment', False) - try: - shepherd = self.doc.shepherd - except PersonOrOrgInfo.DoesNotExist: - shepherd = None - if shepherd: - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - extra_notify = [shepherd.formatted_email()] - else: - extra_notify = ['%s <%s>' % shepherd.email()] - else: - extra_notify = [] - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - tags = DocTagName.objects.filter(slug="sheph-u") - else: - tags = [FOLLOWUP_TAG] - if followup: - update_tags(self.request, self.doc, comment, self.person, set_tags=tags, extra_notify=extra_notify) - else: - update_tags(self.request, self.doc, comment, self.person, reset_tags=tags, extra_notify=extra_notify) - return self.doc_writeup - - def is_valid(self): - if self.data.get('confirm', False) and self.data.get('modify_tag', False): - self.fields['comment'].required = True - else: - self.fields['comment'].required = False - return super(WriteUpEditForm, self).is_valid() diff --git a/ietf/wgchairs/migrations/.gitignore b/ietf/wgchairs/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/wgchairs/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/wgchairs/migrations/0001_initial.py b/ietf/wgchairs/migrations/0001_initial.py deleted file mode 100644 index 6aa9fb58f..000000000 --- a/ietf/wgchairs/migrations/0001_initial.py +++ /dev/null @@ -1,110 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.wgchairs.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'WGDelegate' - db.create_table('wgchairs_wgdelegate', ( - ('id', orm['wgchairs.WGDelegate:id']), - ('person', orm['wgchairs.WGDelegate:person']), - ('wg', orm['wgchairs.WGDelegate:wg']), - )) - db.send_create_signal('wgchairs', ['WGDelegate']) - - - - def backwards(self, orm): - - # Deleting model 'WGDelegate' - db.delete_table('wgchairs_wgdelegate') - - - - models = { - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.area': { - 'Meta': {'db_table': "'areas'"}, - 'area_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'extra_email_addresses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaStatus']"}) - }, - 'idtracker.areadirector': { - 'Meta': {'db_table': "'area_directors'"}, - 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Area']", 'null': 'True', 'db_column': "'area_acronym_id'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"}) - }, - 'idtracker.areastatus': { - 'Meta': {'db_table': "'area_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.ietfwg': { - 'Meta': {'db_table': "'groups_ietf'"}, - 'area_director': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaDirector']", 'null': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'dormant_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), - 'email_archive': ('django.db.models.fields.CharField', [], {'max_length': '95', 'blank': 'True'}), - 'email_keyword': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'email_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'}), - 'group_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'group_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGType']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'meeting_scheduled': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'meeting_scheduled_old': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'proposed_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGStatus']"}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'idtracker.wgstatus': { - 'Meta': {'db_table': "'g_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.wgtype': { - 'Meta': {'db_table': "'g_type'"}, - 'group_type_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'type': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'group_type'"}) - }, - 'wgchairs.wgdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'wg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IETFWG']"}) - } - } - - complete_apps = ['wgchairs'] diff --git a/ietf/wgchairs/migrations/0002_add_writeup.py b/ietf/wgchairs/migrations/0002_add_writeup.py deleted file mode 100644 index e1754ed22..000000000 --- a/ietf/wgchairs/migrations/0002_add_writeup.py +++ /dev/null @@ -1,163 +0,0 @@ - -from south.db import db -from django.db import models -from ietf.wgchairs.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'ProtoWriteUp' - db.create_table('wgchairs_protowriteup', ( - ('id', orm['wgchairs.protowriteup:id']), - ('person', orm['wgchairs.protowriteup:person']), - ('draft', orm['wgchairs.protowriteup:draft']), - ('date', orm['wgchairs.protowriteup:date']), - ('writeup', orm['wgchairs.protowriteup:writeup']), - )) - db.send_create_signal('wgchairs', ['ProtoWriteUp']) - - - - def backwards(self, orm): - - # Deleting model 'ProtoWriteUp' - db.delete_table('wgchairs_protowriteup') - - - - models = { - 'idtracker.acronym': { - 'Meta': {'db_table': "'acronym'"}, - 'acronym': ('django.db.models.fields.CharField', [], {'max_length': '12'}), - 'acronym_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'idtracker.area': { - 'Meta': {'db_table': "'areas'"}, - 'area_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'extra_email_addresses': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaStatus']"}) - }, - 'idtracker.areadirector': { - 'Meta': {'db_table': "'area_directors'"}, - 'area': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Area']", 'null': 'True', 'db_column': "'area_acronym_id'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'db_column': "'person_or_org_tag'"}) - }, - 'idtracker.areastatus': { - 'Meta': {'db_table': "'area_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idintendedstatus': { - 'Meta': {'db_table': "'id_intended_status'"}, - 'intended_status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'intended_status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.idstatus': { - 'Meta': {'db_table': "'id_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.ietfwg': { - 'Meta': {'db_table': "'groups_ietf'"}, - 'area_director': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.AreaDirector']", 'null': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'blank': 'True'}), - 'concluded_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'dormant_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '60', 'blank': 'True'}), - 'email_archive': ('django.db.models.fields.CharField', [], {'max_length': '95', 'blank': 'True'}), - 'email_keyword': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'email_subscribe': ('django.db.models.fields.CharField', [], {'max_length': '120', 'blank': 'True'}), - 'group_acronym': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['idtracker.Acronym']", 'unique': 'True', 'primary_key': 'True'}), - 'group_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGType']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'meeting_scheduled': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'meeting_scheduled_old': ('django.db.models.fields.CharField', [], {'max_length': '3', 'blank': 'True'}), - 'proposed_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.WGStatus']"}) - }, - 'idtracker.internetdraft': { - 'Meta': {'db_table': "'internet_drafts'"}, - 'abstract': ('django.db.models.fields.TextField', [], {}), - 'b_approve_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_discussion_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'b_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'dunn_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True'}), - 'expired_tombstone': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'extension_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'file_type': ('django.db.models.fields.CharField', [], {'max_length': '20'}), - 'filename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.Acronym']", 'db_column': "'group_acronym_id'"}), - 'id_document_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'id_document_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'intended_status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDIntendedStatus']"}), - 'last_modified_date': ('django.db.models.fields.DateField', [], {}), - 'lc_changes': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True'}), - 'lc_expiration_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'lc_sent_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), - 'local_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), - 'replaced_by': ('django.db.models.fields.related.ForeignKey', ["orm['idtracker.InternetDraft']"], {'related_name': "'replaces_set'", 'null': 'True', 'db_column': "'replaced_by'", 'blank': 'True'}), - 'review_by_rfc_editor': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'revision': ('django.db.models.fields.CharField', [], {'max_length': '2'}), - 'revision_date': ('django.db.models.fields.DateField', [], {}), - 'rfc_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), - 'shepherd': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']", 'null': 'True', 'blank': 'True'}), - 'start_date': ('django.db.models.fields.DateField', [], {}), - 'status': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IDStatus']"}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'id_document_name'"}), - 'txt_page_count': ('django.db.models.fields.IntegerField', [], {}), - 'wgreturn_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) - }, - 'idtracker.personororginfo': { - 'Meta': {'db_table': "'person_or_org_info'"}, - 'address_type': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'created_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'date_created': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}), - 'date_modified': ('django.db.models.fields.DateField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'first_name_key': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'last_name_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}), - 'middle_initial': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'middle_initial_key': ('django.db.models.fields.CharField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), - 'modified_by': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}), - 'name_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'name_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), - 'person_or_org_tag': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'record_type': ('django.db.models.fields.CharField', [], {'max_length': '8', 'null': 'True', 'blank': 'True'}) - }, - 'idtracker.wgstatus': { - 'Meta': {'db_table': "'g_status'"}, - 'status': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'status_value'"}), - 'status_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) - }, - 'idtracker.wgtype': { - 'Meta': {'db_table': "'g_type'"}, - 'group_type_id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'type': ('django.db.models.fields.CharField', [], {'max_length': '25', 'db_column': "'group_type'"}) - }, - 'wgchairs.protowriteup': { - 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now()'}), - 'draft': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.InternetDraft']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'writeup': ('django.db.models.fields.TextField', [], {}) - }, - 'wgchairs.wgdelegate': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.PersonOrOrgInfo']"}), - 'wg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['idtracker.IETFWG']"}) - } - } - - complete_apps = ['wgchairs'] diff --git a/ietf/wgchairs/migrations/__init__.py b/ietf/wgchairs/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/wgchairs/models.py b/ietf/wgchairs/models.py deleted file mode 100644 index ea0a74c97..000000000 --- a/ietf/wgchairs/models.py +++ /dev/null @@ -1,87 +0,0 @@ -import datetime - -from django.db import models -from django.conf import settings - -from ietf.idtracker.models import (IETFWG, PersonOrOrgInfo, - InternetDraft) - - -class WGDelegate(models.Model): - person = models.ForeignKey( - PersonOrOrgInfo, - ) - - wg = models.ForeignKey(IETFWG, related_name="old_wgdelegate_set" if settings.USE_DB_REDESIGN_PROXY_CLASSES else None) - - def __unicode__(self): - return "%s" % self.person - - class Meta: - verbose_name = "WG Delegate" - -class ProtoWriteUp(models.Model): - person = models.ForeignKey( - PersonOrOrgInfo, - blank=False, - null=False, - ) - - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.idtracker.models import InternetDraftOld as InternetDraft - - draft = models.ForeignKey( - InternetDraft, - blank=False, - null=False, - ) - - date = models.DateTimeField( - default=datetime.datetime.now(), - blank=False, - null=False, - ) - - writeup = models.TextField( - blank=False, - null=False, - ) - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.group.models import Role - class WGDelegateProxy(Role): - #person = models.ForeignKey(PersonOrOrgInfo) # same name - #wg = models.ForeignKey(IETFWG) - @property - def wg(self): - return self.group - - def __unicode__(self): - return u"%s" % self.person - - class Meta: - proxy = True - - from ietf.doc.models import WriteupDocEvent - class ProtoWriteUpProxy(WriteupDocEvent): - #person = models.ForeignKey(PersonOrOrgInfo, blank=False, null=False) - @property - def person(self): - return self.by - #draft = models.ForeignKey(InternetDraft, blank=False, null=False) - @property - def draft(self): - return self.doc - #date = models.DateTimeField(default=datetime.datetime.now(), blank=False, null=False) - @property - def date(self): - return self.time - #writeup = models.TextField(blank=False, null=False) - @property - def writeup(self): - return self.text - class Meta: - proxy = True - - #WGDelegateOld = WGDelegate - WGDelegate = WGDelegateProxy diff --git a/ietf/wgchairs/templatetags/.gitignore b/ietf/wgchairs/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/ietf/wgchairs/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/ietf/wgchairs/templatetags/__init__.py b/ietf/wgchairs/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/ietf/wgchairs/templatetags/wgchairs_tags.py b/ietf/wgchairs/templatetags/wgchairs_tags.py deleted file mode 100644 index df51dafae..000000000 --- a/ietf/wgchairs/templatetags/wgchairs_tags.py +++ /dev/null @@ -1,47 +0,0 @@ -from django.conf import settings -from django import template - -from ietf.ietfworkflows.utils import get_state_for_draft -from ietf.wgchairs.accounts import (can_manage_workflow_in_group, - can_manage_delegates_in_group, - can_manage_shepherds_in_group) - - -register = template.Library() - - -@register.inclusion_tag('wgchairs/wgchairs_admin_options.html', takes_context=True) -def wgchairs_admin_options(context, wg): - request = context.get('request', None) - user = request and request.user - return {'user': user, - 'can_manage_delegates': can_manage_delegates_in_group(user, wg), - 'can_manage_workflow': can_manage_workflow_in_group(user, wg), - 'can_manage_shepherds': can_manage_shepherds_in_group(user, wg), - 'wg': wg, - 'selected': context.get('selected', None), - } - -@register.simple_tag -def writeup(doc): - writeup = doc.protowriteup_set.all() - if not writeup: - return '' - else: - return writeup[0].writeup - - -@register.simple_tag -def writeupdate(doc): - writeup = doc.protowriteup_set.all() - if not writeup: - return '' - else: - return writeup[0].date - - -@register.inclusion_tag('wgchairs/draft_state.html', takes_context=True) -def show_state(context, doc): - return {'doc': doc, - 'state': get_state_for_draft(doc), - } diff --git a/ietf/wgchairs/tests.py b/ietf/wgchairs/tests.py deleted file mode 100644 index 1eab843bc..000000000 --- a/ietf/wgchairs/tests.py +++ /dev/null @@ -1,217 +0,0 @@ -import datetime, os, shutil - -from django.conf import settings -from django.contrib.auth.models import User -from django.core.urlresolvers import reverse as urlreverse -from StringIO import StringIO -from pyquery import PyQuery - -from ietf.utils.test_utils import login_testing_unauthorized -from ietf.utils.test_data import make_test_data -from ietf.utils.mail import outbox -from ietf.utils import TestCase - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - from ietf.person.models import Person, Email - from ietf.group.models import Group, GroupHistory, Role, GroupStateTransitions - from ietf.doc.models import Document, State, WriteupDocEvent - from ietf.name.models import DocTagName - -class ManageDelegatesTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - - def test_delete_delegate(self): - make_test_data() - - url = urlreverse('manage_delegates', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - delegates = Role.objects.filter(name="delegate", group__acronym="mars") - self.assertTrue(len(delegates) > 0) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=delete]')), len(delegates)) - - # delete - r = self.client.post(url, - dict(remove="1", - delete=[d.pk for d in delegates])) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=delete]')), 0) - self.assertEquals(Role.objects.filter(name="delegate", group__acronym="mars").count(), 0) - - def test_add_not_existing_delegate(self): - make_test_data() - - url = urlreverse('manage_delegates', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=email]')), 1) - - # add non-existing - r = self.client.post(url, - dict(email="unknown@example.com", - form_type="single")) - self.assertEquals(r.status_code, 200) - self.assertTrue("unknown@example.com" in r.content) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[type=submit][value*="Send email"]')), 1) - - # we get back a warning and offer to send email, do that - mailbox_before = len(outbox) - r = self.client.post(url, - dict(email="unknown@example.com", - form_type="notexist")) - self.assertEquals(r.status_code, 200) - self.assertTrue("Email sent" in r.content) - self.assertEquals(len(outbox), mailbox_before + 3) - - def test_add_delegate(self): - make_test_data() - - url = urlreverse('manage_delegates', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form input[name=email]')), 1) - - # add existing person - history_before = GroupHistory.objects.filter(acronym="mars").count() - r = self.client.post(url, - dict(email="plain@example.com", - form_type="single")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertTrue("new delegate" in r.content) - self.assertTrue(Email.objects.get(address="plain@example.com").person.plain_name() in r.content) - self.assertEquals(Role.objects.filter(name="delegate", group__acronym="mars", email__address="plain@example.com").count(), 1) - self.assertEquals(history_before + 1, GroupHistory.objects.filter(acronym="mars").count()) - - -class ManageShepherdsTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - - def test_manage_shepherds(self): - make_test_data() - - url = urlreverse('manage_shepherds', kwargs=dict(acronym="mars")) - login_testing_unauthorized(self, "secretary", url) - - # setup test documents - group = Group.objects.get(acronym="mars") - - from ietf.doc.models import Document - common = dict(group=group, - ad=Person.objects.get(user__username="ad"), - type_id="draft") - Document.objects.create(name="test-shepherd-no", - title="No shepherd", - shepherd=None, - **common) - Document.objects.create(name="test-shepherd-me", - title="Shepherd me", - shepherd=Person.objects.get(user__username="secretary"), - **common) - Document.objects.create(name="test-shepherd-other", title="Shepherd other", - shepherd=Person.objects.get(user__username="plain"), - **common) - for d in Document.objects.filter(name__startswith="test-shepherd"): - d.set_state(State.objects.get(used=True, type="draft", slug="active")) - - # get and make sure they are divided correctly - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('div#noshepherd a:contains("No shepherd")')), 1) - self.assertEquals(len(q('div#mydocs a:contains("Shepherd me")')), 1) - self.assertEquals(len(q('div#othershepherds a:contains("Shepherd other")')), 1) - - -class ManageWorkflowTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - - def test_manage_workflows(self): - make_test_data() - - group = Group.objects.get(acronym="mars") - - url = urlreverse('manage_workflow', kwargs=dict(acronym=group.acronym)) - login_testing_unauthorized(self, "secretary", url) - - state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-lc") - self.assertTrue(state not in group.unused_states.all()) - - # get - r = self.client.get(url) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='0']")), 1) - - # deactivate state - r = self.client.post(url, - dict(action="setstateactive", - state=state.pk, - active="0")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='1']")), 1) - group = Group.objects.get(acronym=group.acronym) - self.assertTrue(state in group.unused_states.all()) - - # change next states - state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-doc") - next_states = State.objects.filter(used=True, type=b"draft-stream-ietf", slug__in=["parked", "dead", "wait-wgw", 'sub-pub']).values_list('pk', flat=True) - r = self.client.post(url, - dict(action="setnextstates", - state=state.pk, - next_states=next_states)) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q("form.set-next-states").find("input[name=state][value='%s']" % state.pk).parents('form').find("input[name=next_states][checked=checked]")), len(next_states)) - transitions = GroupStateTransitions.objects.filter(group=group, state=state) - self.assertEquals(len(transitions), 1) - self.assertEquals(set(transitions[0].next_states.values_list("pk", flat=True)), set(next_states)) - - # change them back to default - next_states = state.next_states.values_list("pk", flat=True) - r = self.client.post(url, - dict(action="setnextstates", - state=state.pk, - next_states=next_states)) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - transitions = GroupStateTransitions.objects.filter(group=group, state=state) - self.assertEquals(len(transitions), 0) - - # deactivate tag - tag = DocTagName.objects.get(slug="w-expert") - r = self.client.post(url, - dict(action="settagactive", - tag=tag.pk, - active="0")) - self.assertEquals(r.status_code, 200) - q = PyQuery(r.content) - self.assertEquals(len(q('form').find('input[name=tag][value="%s"]' % tag.pk).parents("form").find("input[name=active]")), 1) - group = Group.objects.get(acronym=group.acronym) - self.assertTrue(tag in group.unused_tags.all()) - -if not settings.USE_DB_REDESIGN_PROXY_CLASSES: - # the above tests only work with the new schema - del ManageDelegatesTestCase - del ManageShepherdsTestCase - del ManageWorkflowTestCase - del ManageWriteupCase diff --git a/ietf/wgchairs/urls.py b/ietf/wgchairs/urls.py deleted file mode 100644 index d8cadb8ee..000000000 --- a/ietf/wgchairs/urls.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright The IETF Trust 2008, All Rights Reserved - -from django.conf.urls.defaults import patterns, url - -urlpatterns = patterns('ietf.wgchairs.views', - url(r'^workflows/$', 'manage_workflow', name='manage_workflow'), - url(r'^delegates/$', 'manage_delegates', name='manage_delegates'), - url(r'^shepherds/$', 'wg_shepherd_documents', name='manage_shepherds'), -) diff --git a/ietf/wgchairs/views.py b/ietf/wgchairs/views.py deleted file mode 100644 index 3bcd6e026..000000000 --- a/ietf/wgchairs/views.py +++ /dev/null @@ -1,200 +0,0 @@ -from django.conf import settings -from ietf.idtracker.models import IETFWG, InternetDraft -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext -from django.http import HttpResponseForbidden, Http404 - -from ietf.wgchairs.forms import (RemoveDelegateForm, add_form_factory, - workflow_form_factory, TransitionFormSet, - WriteUpEditForm, assign_shepherd) -from ietf.wgchairs.accounts import (can_manage_delegates_in_group, get_person_for_user, - can_manage_shepherds_in_group, - can_manage_workflow_in_group, - can_manage_shepherd_of_a_document, - can_manage_writeup_of_a_document, - can_manage_writeup_of_a_document_no_state, - ) -from ietf.ietfworkflows.constants import REQUIRED_STATES -from ietf.ietfworkflows.utils import (get_workflow_for_wg, - get_default_workflow_for_wg, - get_state_by_name, - get_annotation_tags_for_draft, - get_state_for_draft, WAITING_WRITEUP, - FOLLOWUP_TAG) -from ietf.name.models import DocTagName -from ietf.doc.models import State -from ietf.doc.utils import get_tags_for_stream_id - -def manage_delegates(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_delegates_in_group(user, wg): - return HttpResponseForbidden('You have no permission to access this view') - delegates = wg.wgdelegate_set.all() - add_form = add_form_factory(request, wg, user) - if request.method == 'POST': - if request.POST.get('remove', None): - form = RemoveDelegateForm(wg=wg, data=request.POST.copy()) - if form.is_valid(): - form.save() - elif add_form.is_valid(): - add_form.save() - add_form = add_form.get_next_form() - max_delegates = getattr(settings, 'MAX_WG_DELEGATES', 3) - return render_to_response('wgchairs/manage_delegates.html', - {'wg': wg, - 'delegates': delegates, - 'selected': 'manage_delegates', - 'can_add': delegates.count() < max_delegates, - 'max_delegates': max_delegates, - 'add_form': add_form, - }, RequestContext(request)) - - -def manage_workflow(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_workflow_in_group(user, wg): - return HttpResponseForbidden("You don't have permission to access this view") - workflow = get_workflow_for_wg(wg) - default_workflow = get_default_workflow_for_wg() - formset = None - if request.method == 'POST': - form = workflow_form_factory(request, wg=wg, user=user) - if form.is_valid(): - form.save() - elif isinstance(form, TransitionFormSet): - formset = form - tags = workflow.selected_tags.all() - default_tags = default_workflow.annotation_tags.all() - states = workflow.selected_states.all().order_by('statedescription__order') - default_states = default_workflow.states.all().order_by('statedescription__order') - for i in default_states: - if states.filter(name=i.name).count() == 1: - i.used = True - if i.name in REQUIRED_STATES: - i.freeze = True - for i in default_tags: - if tags.filter(name=i.name).count() == 1: - i.used = True - if not formset: - formset = TransitionFormSet(queryset=workflow.transitions.all(), user=user, wg=wg) - - return render_to_response('wgchairs/manage_workflow.html', - {'wg': wg, - 'workflow': workflow, - 'default_workflow': default_workflow, - 'states': states, - 'tags': tags, - 'default_states': default_states, - 'default_tags': default_tags, - 'formset': formset, - 'selected': 'manage_workflow', - }, RequestContext(request)) - -def manage_workflowREDESIGN(request, acronym): - from ietf.doc.models import State - from ietf.group.models import GroupStateTransitions - - MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub') - - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_workflow_in_group(user, wg): - return HttpResponseForbidden("You don't have permission to access this view") - - if request.method == 'POST': - action = request.POST.get("action") - if action == "setstateactive": - active = request.POST.get("active") == "1" - try: - state = State.objects.exclude(slug__in=MANDATORY_STATES).get(pk=request.POST.get("state")) - except State.DoesNotExist: - return HttpResponse("Invalid state %s" % request.POST.get("state")) - - if active: - wg.unused_states.remove(state) - else: - wg.unused_states.add(state) - - if action == "setnextstates": - try: - state = State.objects.get(pk=request.POST.get("state")) - except State.DoesNotExist: - return HttpResponse("Invalid state %s" % request.POST.get("state")) - - next_states = State.objects.filter(used=True, type='draft-stream-ietf', pk__in=request.POST.getlist("next_states")) - unused = wg.unused_states.all() - if set(next_states.exclude(pk__in=unused)) == set(state.next_states.exclude(pk__in=unused)): - # just use the default - wg.groupstatetransitions_set.filter(state=state).delete() - else: - transitions, _ = GroupStateTransitions.objects.get_or_create(group=wg, state=state) - transitions.next_states = next_states - - if action == "settagactive": - active = request.POST.get("active") == "1" - try: - tag = DocTagName.objects.get(pk=request.POST.get("tag")) - except DocTagName.DoesNotExist: - return HttpResponse("Invalid tag %s" % request.POST.get("tag")) - - if active: - wg.unused_tags.remove(tag) - else: - wg.unused_tags.add(tag) - - - # put some info for the template on tags and states - unused_tags = wg.unused_tags.all().values_list('slug', flat=True) - tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id("ietf")) - for t in tags: - t.used = t.slug not in unused_tags - - unused_states = wg.unused_states.all().values_list('slug', flat=True) - states = State.objects.filter(used=True, type="draft-stream-ietf") - transitions = dict((o.state, o) for o in wg.groupstatetransitions_set.all()) - for s in states: - s.used = s.slug not in unused_states - s.mandatory = s.slug in MANDATORY_STATES - - default_n = s.next_states.all() - if s in transitions: - n = transitions[s].next_states.all() - else: - n = default_n - - s.next_states_checkboxes = [(x in n, x in default_n, x) for x in states] - s.used_next_states = [x for x in n if x.slug not in unused_states] - - return render_to_response('wgchairs/manage_workflowREDESIGN.html', - {'wg': wg, - 'states': states, - 'tags': tags, - 'selected': 'manage_workflow', - }, RequestContext(request)) - - -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - manage_workflow = manage_workflowREDESIGN - -def wg_shepherd_documents(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - user = request.user - if not can_manage_shepherds_in_group(user, wg): - return HttpResponseForbidden('You have no permission to access this view') - current_person = get_person_for_user(user) - - base_qs = InternetDraft.objects.filter(group=wg, states__type="draft", states__slug="active").select_related("status").order_by('title') - documents_no_shepherd = base_qs.filter(shepherd=None) - documents_my = base_qs.filter(shepherd=current_person) - documents_other = base_qs.exclude(shepherd=None).exclude(shepherd__pk__in=[current_person.pk, 0]) - context = { - 'no_shepherd': documents_no_shepherd, - 'my_documents': documents_my, - 'other_shepherds': documents_other, - 'selected': 'manage_shepherds', - 'wg': wg, - } - return render_to_response('wgchairs/wg_shepherd_documents.html', context, RequestContext(request)) - diff --git a/ietf/wgcharter/feeds.py b/ietf/wgcharter/feeds.py index 5abf7708c..1dd8bbfff 100644 --- a/ietf/wgcharter/feeds.py +++ b/ietf/wgcharter/feeds.py @@ -26,7 +26,7 @@ class GroupChanges(Feed): def link(self, obj): if not obj: raise FeedDoesNotExist - return urlreverse('wg_charter', kwargs={'acronym': obj.acronym}) + return urlreverse('group_charter', kwargs={'acronym': obj.acronym}) def description(self, obj): return self.title(obj) @@ -44,7 +44,7 @@ class GroupChanges(Feed): 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 }) + return urlreverse('group_charter', kwargs={'acronym': obj.group.acronym }) def item_pubdate(self, obj): return obj.time diff --git a/ietf/wgcharter/mails.py b/ietf/wgcharter/mails.py index 29c603b4e..0f983a017 100644 --- a/ietf/wgcharter/mails.py +++ b/ietf/wgcharter/mails.py @@ -33,7 +33,7 @@ def email_secretariat(request, group, type, text): "wgcharter/email_secretariat.txt", dict(text=text, group=group, - group_url=settings.IDTRACKER_BASE_URL + urlreverse('wg_charter', kwargs=dict(acronym=group.acronym)), + group_url=settings.IDTRACKER_BASE_URL + urlreverse('group_charter', kwargs=dict(acronym=group.acronym)), charter_url=settings.IDTRACKER_BASE_URL + urlreverse('doc_view', kwargs=dict(name=group.charter.name)), ) ) diff --git a/ietf/wgcharter/tests.py b/ietf/wgcharter/tests.py index 56f639afb..7b55d9f91 100644 --- a/ietf/wgcharter/tests.py +++ b/ietf/wgcharter/tests.py @@ -21,10 +21,7 @@ from ietf.person.models import * from ietf.iesg.models import TelechatDate from ietf.wgcharter.utils import * -class EditCharterTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class EditCharterTests(TestCase): def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) @@ -198,10 +195,7 @@ class EditCharterTestCase(TestCase): self.assertEquals(f.read(), "Windows line\nMac line\nUnix line\n" + utf_8_snippet) -class ApproveCharterTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ['names'] - +class ApproveCharterTests(TestCase): def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 89be89523..69441242c 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -1,8 +1,9 @@ # edit/create view for WGs -import re, os, string, datetime, shutil +import re, os, datetime, shutil from django.shortcuts import render_to_response, get_object_or_404, redirect +from django.http import HttpResponseForbidden from django.core.urlresolvers import reverse from django.template import RequestContext from django import forms @@ -20,15 +21,18 @@ from ietf.group.models import * from ietf.group.utils import save_group_in_history from ietf.wgcharter.mails import email_secretariat from ietf.person.forms import EmailsField +from ietf.doc.utils import get_tags_for_stream_id +MAX_GROUP_DELEGATES = 3 class WGForm(forms.Form): - name = forms.CharField(max_length=255, label="WG Name", required=True) - acronym = forms.CharField(max_length=10, label="WG Acronym", required=True) - state = forms.ModelChoiceField(GroupStateName.objects.all(), label="WG State", required=True) - chairs = EmailsField(label="WG Chairs", required=False) - secretaries = EmailsField(label="WG Secretaries", required=False) - techadv = EmailsField(label="WG Technical Advisors", required=False) + name = forms.CharField(max_length=255, label="Name", required=True) + acronym = forms.CharField(max_length=10, label="Acronym", required=True) + state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True) + chairs = EmailsField(label="Chairs", required=False) + secretaries = EmailsField(label="Secretaries", required=False) + techadv = EmailsField(label="Technical Advisors", required=False) + delegates = EmailsField(label="Delegates", required=False, help_text=mark_safe("Type in name to search for person
    Chairs can delegate the authority to update the state of group documents - max %s persons at a given time" % MAX_GROUP_DELEGATES)) ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Shepherding AD", empty_label="(None)", required=False) parent = forms.ModelChoiceField(Group.objects.filter(type="area", state="active").order_by('name'), label="IETF Area", empty_label="(None)", required=False) list_email = forms.CharField(max_length=64, required=False) @@ -100,6 +104,13 @@ class WGForm(forms.Form): def clean_urls(self): return [x.strip() for x in self.cleaned_data["urls"].splitlines() if x.strip()] + def clean_delegates(self): + if len(self.cleaned_data["delegates"]) > MAX_GROUP_DELEGATES: + raise forms.ValidationError("At most %s delegates can be appointed at the same time, please remove %s delegates." % ( + MAX_GROUP_DELEGATES, len(self.cleaned_data["delegates"]) - MAX_GROUP_DELEGATES)) + return self.cleaned_data["delegates"] + + def format_urls(urls, fs="\n"): res = [] for u in urls: @@ -224,7 +235,7 @@ def edit(request, acronym=None, action="edit"): shutil.copy(old, new) # update roles - for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors")]: + for attr, slug, title in [('chairs', 'chair', "Chairs"), ('secretaries', 'secr', "Secretaries"), ('techadv', 'techadv', "Tech Advisors"), ('delegates', 'delegate', "Delegates")]: new = clean[attr] old = Email.objects.filter(role__group=wg, role__name=slug).select_related("person") if set(new) != set(old): @@ -262,7 +273,7 @@ def edit(request, acronym=None, action="edit"): if action=="charter": return redirect('charter_submit', name=wg.charter.name, option="initcharter") - return redirect('wg_charter', acronym=wg.acronym) + return redirect('group_charter', acronym=wg.acronym) else: # form.is_valid() if not new_wg: from ietf.person.forms import json_emails @@ -272,6 +283,7 @@ def edit(request, acronym=None, action="edit"): chairs=Email.objects.filter(role__group=wg, role__name="chair"), secretaries=Email.objects.filter(role__group=wg, role__name="secr"), techadv=Email.objects.filter(role__group=wg, role__name="techadv"), + delegates=Email.objects.filter(role__group=wg, role__name="delegate"), ad=wg.ad_id if wg.ad else None, parent=wg.parent.id if wg.parent else None, list_email=wg.list_email if wg.list_email else None, @@ -316,7 +328,7 @@ def conclude(request, acronym): e.desc = "Requested closing group" e.save() - return redirect('wg_charter', acronym=wg.acronym) + return redirect('group_charter', acronym=wg.acronym) else: form = ConcludeForm() @@ -324,3 +336,89 @@ def conclude(request, acronym): dict(form=form, wg=wg), context_instance=RequestContext(request)) + + +def customize_workflow(request, acronym): + MANDATORY_STATES = ('c-adopt', 'wg-doc', 'sub-pub') + + group = get_object_or_404(Group, acronym=acronym, type="wg") + if not request.user.is_authenticated() or not (has_role(request.user, "Secretariat") or group.role_set.filter(name="chair", person__user=request.user)): + return HttpResponseForbidden("You don't have permission to access this view") + + if request.method == 'POST': + action = request.POST.get("action") + if action == "setstateactive": + active = request.POST.get("active") == "1" + try: + state = State.objects.exclude(slug__in=MANDATORY_STATES).get(pk=request.POST.get("state")) + except State.DoesNotExist: + return HttpResponse("Invalid state %s" % request.POST.get("state")) + + if active: + group.unused_states.remove(state) + else: + group.unused_states.add(state) + + # redirect so the back button works correctly, otherwise + # repeated POSTs fills up the history + return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym) + + if action == "setnextstates": + try: + state = State.objects.get(pk=request.POST.get("state")) + except State.DoesNotExist: + return HttpResponse("Invalid state %s" % request.POST.get("state")) + + next_states = State.objects.filter(used=True, type='draft-stream-ietf', pk__in=request.POST.getlist("next_states")) + unused = group.unused_states.all() + if set(next_states.exclude(pk__in=unused)) == set(state.next_states.exclude(pk__in=unused)): + # just use the default + group.groupstatetransitions_set.filter(state=state).delete() + else: + transitions, _ = GroupStateTransitions.objects.get_or_create(group=group, state=state) + transitions.next_states = next_states + + return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym) + + if action == "settagactive": + active = request.POST.get("active") == "1" + try: + tag = DocTagName.objects.get(pk=request.POST.get("tag")) + except DocTagName.DoesNotExist: + return HttpResponse("Invalid tag %s" % request.POST.get("tag")) + + if active: + group.unused_tags.remove(tag) + else: + group.unused_tags.add(tag) + + return redirect("ietf.wginfo.edit.customize_workflow", acronym=group.acronym) + + + # put some info for the template on tags and states + unused_tags = group.unused_tags.all().values_list('slug', flat=True) + tags = DocTagName.objects.filter(slug__in=get_tags_for_stream_id("ietf")) + for t in tags: + t.used = t.slug not in unused_tags + + unused_states = group.unused_states.all().values_list('slug', flat=True) + states = State.objects.filter(used=True, type="draft-stream-ietf") + transitions = dict((o.state, o) for o in group.groupstatetransitions_set.all()) + for s in states: + s.used = s.slug not in unused_states + s.mandatory = s.slug in MANDATORY_STATES + + default_n = s.next_states.all() + if s in transitions: + n = transitions[s].next_states.all() + else: + n = default_n + + s.next_states_checkboxes = [(x in n, x in default_n, x) for x in states] + s.used_next_states = [x for x in n if x.slug not in unused_states] + + return render_to_response('wginfo/customize_workflow.html', { + 'group': group, + 'states': states, + 'tags': tags, + }, RequestContext(request)) diff --git a/ietf/wginfo/mails.py b/ietf/wginfo/mails.py index 96b8bb6e8..02606d30e 100644 --- a/ietf/wginfo/mails.py +++ b/ietf/wginfo/mails.py @@ -16,7 +16,7 @@ def email_milestones_changed(request, group, changes): def wrap_up_email(to, text): text = wrap(strip_tags(text), 70) text += "\n\n" - text += u"URL: %s" % (settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym))) + text += u"URL: %s" % (settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym))) send_mail_text(request, to, None, u"Milestones changed for %s %s" % (group.acronym, group.type.name), @@ -95,7 +95,7 @@ def email_milestones_due(group, early_warning_days): milestones=milestones, today=today, early_warning_days=early_warning_days, - url=settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym)) + url=settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym)) )) def groups_needing_milestones_due_reminder(early_warning_days): @@ -120,7 +120,7 @@ def email_milestones_overdue(group): "wginfo/reminder_milestones_overdue.txt", dict(group=group, milestones=milestones, - url=settings.IDTRACKER_BASE_URL + urlreverse("wg_charter", kwargs=dict(acronym=group.acronym)) + url=settings.IDTRACKER_BASE_URL + urlreverse("group_charter", kwargs=dict(acronym=group.acronym)) )) def groups_needing_milestones_overdue_reminder(grace_period=30): diff --git a/ietf/wginfo/milestones.py b/ietf/wginfo/milestones.py index 3e7b3df64..dee1765cc 100644 --- a/ietf/wginfo/milestones.py +++ b/ietf/wginfo/milestones.py @@ -313,7 +313,7 @@ def edit_milestones(request, acronym, milestone_set="current"): if milestone_set == "charter": return redirect('doc_view', name=group.charter.canonical_name()) else: - return redirect('wg_charter', acronym=group.acronym) + return redirect('group_charter', acronym=group.acronym) else: for m in milestones: forms.append(MilestoneForm(instance=m, needs_review=needs_review)) diff --git a/ietf/wginfo/tests.py b/ietf/wginfo/tests.py index 4eaf80586..fad2fcf67 100644 --- a/ietf/wginfo/tests.py +++ b/ietf/wginfo/tests.py @@ -50,42 +50,168 @@ from ietf.name.models import * from ietf.person.models import * from ietf.wginfo.mails import * +class GroupPagesTests(TestCase): + def setUp(self): + self.charter_dir = os.path.abspath("tmp-charter-dir") + os.mkdir(self.charter_dir) + settings.CHARTER_PATH = self.charter_dir -class WgInfoUrlTestCase(SimpleUrlTestCase): - def testUrls(self): - self.doTestUrls(__file__) + def tearDown(self): + shutil.rmtree(self.charter_dir) -class WgFileTestCase(unittest.TestCase): - def testFileExistence(self): - fpath = os.path.join(settings.IETFWG_DESCRIPTIONS_PATH, "tls.desc.txt") - if not os.path.exists(fpath): - print "\nERROR: charter files not found in "+settings.IETFWG_DESCRIPTIONS_PATH - print "They are needed for testing WG charter pages." - print "Download them to a local directory with:" - print "wget -nd -nc -np -r http://www.ietf.org/wg-descriptions/" - print "And set IETFWG_DESCRIPTIONS_PATH in settings_local.py\n" + def test_active_wgs(self): + draft = make_test_data() + group = draft.group -class WgOverviewTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ["names"] + url = urlreverse('ietf.wginfo.views.active_wgs') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.parent.name in r.content) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.ad.plain_name() in r.content) - def test_overview(self): - make_test_data() + def test_wg_summaries(self): + draft = make_test_data() + group = draft.group - wg = Group.objects.get(acronym="mars") - wg.charter.set_state(State.objects.get(used=True, type="charter", slug="intrev")) + chair = Email.objects.filter(role__group=group, role__name="chair")[0] + + with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f: + f.write("This is a charter.") + + url = urlreverse('ietf.wginfo.views.wg_summary_area') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.parent.name in r.content) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(chair.address in r.content) + + url = urlreverse('ietf.wginfo.views.wg_summary_acronym') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(chair.address in r.content) + + url = urlreverse('ietf.wginfo.views.wg_charters') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.ad.plain_name() in r.content) + self.assertTrue(chair.address in r.content) + self.assertTrue("This is a charter." in r.content) + + url = urlreverse('ietf.wginfo.views.wg_charters_by_acronym') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.acronym in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.ad.plain_name() in r.content) + self.assertTrue(chair.address in r.content) + self.assertTrue("This is a charter." in r.content) + + def test_chartering_wgs(self): + draft = make_test_data() + group = draft.group + group.charter.set_state(State.objects.get(used=True, type="charter", slug="intrev")) url = urlreverse('ietf.wginfo.views.chartering_wgs') r = self.client.get(url) self.assertEquals(r.status_code, 200) q = PyQuery(r.content) - self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("mars")')), 1) + self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1) + def test_bofs(self): + draft = make_test_data() + group = draft.group + group.state_id = "bof" + group.save() -class WgEditTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ["names"] + url = urlreverse('ietf.wginfo.views.bofs') + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('table.ietf-doctable td.acronym a:contains("%s")' % group.acronym)), 1) + + def test_group_documents(self): + draft = make_test_data() + group = draft.group + draft2 = Document.objects.create( + name="draft-somebody-mars-test", + time=datetime.datetime.now(), + type_id="draft", + title="Test By Somebody", + stream_id="ietf", + group=Group.objects.get(type="individ"), + abstract="Abstract.", + rev="01", + pages=2, + intended_std_level_id="ps", + shepherd=None, + ad=None, + expires=datetime.datetime.now() + datetime.timedelta(days=10), + notify="", + note="", + ) + + draft2.set_state(State.objects.get(used=True, type="draft", slug="active")) + DocAlias.objects.create( + document=draft2, + name=draft2.name, + ) + + url = urlreverse('ietf.wginfo.views.group_documents', kwargs=dict(acronym=group.acronym)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(draft.name in r.content) + self.assertTrue(group.name in r.content) + self.assertTrue(group.acronym in r.content) + + self.assertTrue(draft2.name in r.content) + + def test_group_charter(self): + draft = make_test_data() + group = draft.group + + with open(os.path.join(self.charter_dir, "%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev)), "w") as f: + f.write("This is a charter.") + + milestone = GroupMilestone.objects.create( + group=group, + state_id="active", + desc="Get Work Done", + due=datetime.date.today() + datetime.timedelta(days=100)) + milestone.docs.add(draft) + + url = urlreverse('ietf.wginfo.views.group_charter', kwargs=dict(acronym=group.acronym)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(group.name in r.content) + self.assertTrue(group.acronym in r.content) + self.assertTrue("This is a charter." in r.content) + self.assertTrue(milestone.desc in r.content) + self.assertTrue(milestone.docs.all()[0].name in r.content) + + def test_history(self): + draft = make_test_data() + group = draft.group + + e = GroupEvent.objects.create( + group=group, + desc="Something happened.", + type="added_comment", + by=Person.objects.get(name="(System)")) + + url = urlreverse('ietf.wginfo.views.history', kwargs=dict(acronym=group.acronym)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(e.desc in r.content) + +class GroupEditTests(TestCase): def setUp(self): self.charter_dir = os.path.abspath("tmp-charter-dir") os.mkdir(self.charter_dir) @@ -180,7 +306,7 @@ class WgEditTestCase(TestCase): make_test_data() group = Group.objects.get(acronym="mars") - url = urlreverse('wg_edit', kwargs=dict(acronym=group.acronym)) + url = urlreverse('group_edit', kwargs=dict(acronym=group.acronym)) login_testing_unauthorized(self, "secretary", url) # normal get @@ -225,6 +351,7 @@ class WgEditTestCase(TestCase): chairs="aread@ietf.org, ad1@ietf.org", secretaries="aread@ietf.org, ad1@ietf.org, ad2@ietf.org", techadv="aread@ietf.org", + delegates="ad2@ietf.org", list_email="mars@mail", list_subscribe="subscribe.mars", list_archive="archive.mars", @@ -245,6 +372,7 @@ class WgEditTestCase(TestCase): 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.assertTrue(group.role_set.filter(name="delegate", email__address="ad2@ietf.org")) self.assertEquals(group.list_email, "mars@mail") self.assertEquals(group.list_subscribe, "subscribe.mars") self.assertEquals(group.list_archive, "archive.mars") @@ -281,10 +409,7 @@ class WgEditTestCase(TestCase): group = Group.objects.get(acronym=group.acronym) self.assertEquals(group.state_id, "active") -class MilestoneTestCase(TestCase): - # See ietf.utils.test_utils.TestCase for the use of perma_fixtures vs. fixtures - perma_fixtures = ["names"] - +class MilestoneTests(TestCase): def create_test_milestones(self): draft = make_test_data() @@ -688,3 +813,73 @@ class MilestoneTestCase(TestCase): self.assertTrue(group.acronym in outbox[-1]["Subject"]) self.assertTrue(m1.desc in unicode(outbox[-1])) self.assertTrue(m2.desc in unicode(outbox[-1])) + +class CustomizeWorkflowTests(TestCase): + def test_customize_workflow(self): + make_test_data() + + group = Group.objects.get(acronym="mars") + + url = urlreverse('ietf.wginfo.edit.customize_workflow', kwargs=dict(acronym=group.acronym)) + login_testing_unauthorized(self, "secretary", url) + + state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-lc") + self.assertTrue(state not in group.unused_states.all()) + + # get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='0']")), 1) + + # deactivate state + r = self.client.post(url, + dict(action="setstateactive", + state=state.pk, + active="0")) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEquals(len(q("form.set-state").find("input[name=state][value='%s']" % state.pk).parents("form").find("input[name=active][value='1']")), 1) + group = Group.objects.get(acronym=group.acronym) + self.assertTrue(state in group.unused_states.all()) + + # change next states + state = State.objects.get(used=True, type="draft-stream-ietf", slug="wg-doc") + next_states = State.objects.filter(used=True, type=b"draft-stream-ietf", slug__in=["parked", "dead", "wait-wgw", 'sub-pub']).values_list('pk', flat=True) + r = self.client.post(url, + dict(action="setnextstates", + state=state.pk, + next_states=next_states)) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEquals(len(q("form.set-next-states").find("input[name=state][value='%s']" % state.pk).parents('form').find("input[name=next_states][checked=checked]")), len(next_states)) + transitions = GroupStateTransitions.objects.filter(group=group, state=state) + self.assertEquals(len(transitions), 1) + self.assertEquals(set(transitions[0].next_states.values_list("pk", flat=True)), set(next_states)) + + # change them back to default + next_states = state.next_states.values_list("pk", flat=True) + r = self.client.post(url, + dict(action="setnextstates", + state=state.pk, + next_states=next_states)) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + transitions = GroupStateTransitions.objects.filter(group=group, state=state) + self.assertEquals(len(transitions), 0) + + # deactivate tag + tag = DocTagName.objects.get(slug="w-expert") + r = self.client.post(url, + dict(action="settagactive", + tag=tag.pk, + active="0")) + self.assertEquals(r.status_code, 302) + r = self.client.get(url) + q = PyQuery(r.content) + self.assertEquals(len(q('form').find('input[name=tag][value="%s"]' % tag.pk).parents("form").find("input[name=active]")), 1) + group = Group.objects.get(acronym=group.acronym) + self.assertTrue(tag in group.unused_tags.all()) diff --git a/ietf/wginfo/testurl.list b/ietf/wginfo/testurl.list deleted file mode 100644 index 2cf38b30a..000000000 --- a/ietf/wginfo/testurl.list +++ /dev/null @@ -1,19 +0,0 @@ -200 /wg/ -404 /wg/nosuchgroup/ -200 /wg/tls/ -200 /wg/tls/charter/ -200 /wg/mobike/ # concluded -200 /wg/mobike/charter/ -200 /wg/catnip/ # concluded very long time ago -200 /wg/catnip/charter/ # concluded very long time ago -404 /wg/saag/ # not a WG -404 /wg/saag/charter/ # not a WG - -200 /wg/1wg-summary.txt -200 /wg/1wg-summary-by-acronym.txt -301 /wg/summary.txt -301 /wg/summary-by-area.txt -301 /wg/summary-by-acronym.txt -200,heavy /wg/1wg-charters.txt -200,heavy /wg/1wg-charters-by-acronym.txt - diff --git a/ietf/wginfo/urls.py b/ietf/wginfo/urls.py index 80651befd..b0106f144 100644 --- a/ietf/wginfo/urls.py +++ b/ietf/wginfo/urls.py @@ -6,7 +6,7 @@ from django.views.generic.simple import redirect_to urlpatterns = patterns('', - (r'^$', views.wg_dir), + (r'^$', views.active_wgs), (r'^summary.txt', redirect_to, { 'url':'/wg/1wg-summary.txt' }), (r'^summary-by-area.txt', redirect_to, { 'url':'/wg/1wg-summary.txt' }), (r'^summary-by-acronym.txt', redirect_to, { 'url':'/wg/1wg-summary-by-acronym.txt' }), @@ -18,17 +18,16 @@ urlpatterns = patterns('', (r'^bofs/$', views.bofs), (r'^chartering/create/$', edit.edit, {'action': "charter"}, "wg_create"), (r'^bofs/create/$', edit.edit, {'action': "create"}, "bof_create"), - (r'^(?P[a-zA-Z0-9-]+)/documents/txt/$', views.wg_documents_txt), - (r'^(?P[a-zA-Z0-9-]+)/$', views.wg_documents_html, None, "wg_docs"), - (r'^(?P[a-zA-Z0-9-]+)/charter/$', views.wg_charter, None, 'wg_charter'), + (r'^(?P[a-zA-Z0-9-]+)/documents/txt/$', views.group_documents_txt), + (r'^(?P[a-zA-Z0-9-]+)/$', views.group_documents, None, "wg_docs"), + (r'^(?P[a-zA-Z0-9-]+)/charter/$', views.group_charter, None, 'group_charter'), (r'^(?P[a-zA-Z0-9-]+)/init-charter/', edit.submit_initial_charter, None, "wg_init_charter"), (r'^(?P[a-zA-Z0-9-]+)/history/$', views.history), - (r'^(?P[a-zA-Z0-9-]+)/edit/$', edit.edit, {'action': "edit"}, "wg_edit"), + (r'^(?P[a-zA-Z0-9-]+)/edit/$', edit.edit, {'action': "edit"}, "group_edit"), (r'^(?P[a-zA-Z0-9-]+)/conclude/$', edit.conclude, None, "wg_conclude"), (r'^(?P[a-zA-Z0-9-]+)/milestones/$', milestones.edit_milestones, {'milestone_set': "current"}, "wg_edit_milestones"), (r'^(?P[a-zA-Z0-9-]+)/milestones/charter/$', milestones.edit_milestones, {'milestone_set': "charter"}, "wg_edit_charter_milestones"), (r'^(?P[a-zA-Z0-9-]+)/milestones/charter/reset/$', milestones.reset_charter_milestones, None, "wg_reset_charter_milestones"), (r'^(?P[a-zA-Z0-9-]+)/ajax/searchdocs/$', milestones.ajax_search_docs, None, "wg_ajax_search_docs"), - (r'^(?P[^/]+)/management/', include('ietf.wgchairs.urls')), - + (r'^(?P[a-zA-Z0-9-]+)/workflow/$', edit.customize_workflow), ) diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 6caaf6f7d..5d5c12795 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -32,106 +32,126 @@ # (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 itertools + from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext, loader +from django.template import RequestContext from django.http import HttpResponse from django.conf import settings from django.core.urlresolvers import reverse as urlreverse -from ietf.idtracker.models import Area, IETFWG + from ietf.doc.views_search import SearchForm, retrieve_search_results -from ietf.idrfc.idrfc_wrapper import IdRfcWrapper -from ietf.ipr.models import IprDetail -from ietf.group.models import Group -from ietf.doc.models import State +from ietf.group.models import Group, GroupURL, Role +from ietf.doc.models import State, DocAlias, RelatedDocument from ietf.doc.utils import get_chartering_type +from ietf.group.utils import get_charter_text +from ietf.doc.templatetags.ietf_filters import clean_whitespace +from ietf.ietfauth.utils import has_role +def roles(group, role_name): + return Role.objects.filter(group=group, name=role_name).select_related("email", "person") -def fill_in_charter_info(wg, include_drafts=False): - from ietf.person.models import Email - from ietf.doc.models import DocAlias, RelatedDocument +def fill_in_charter_info(group, include_drafts=False): + group.areadirector = group.ad.role_email("ad", group.parent) if group.ad else None + group.chairs = roles(group, "chair") + group.techadvisors = roles(group, "techadv") + group.editors = roles(group, "editor") + group.secretaries = roles(group, "secr") + milestone_state = "charter" if group.state_id == "proposed" else "active" + group.milestones = group.groupmilestone_set.filter(state=milestone_state).order_by('due') - wg.areadirector = wg.ad.role_email("ad", wg.parent) if wg.ad else None - wg.chairs = Email.objects.filter(role__group=wg, role__name="chair") - wg.techadvisors = Email.objects.filter(role__group=wg, role__name="techadv") - wg.editors = Email.objects.filter(role__group=wg, role__name="editor") - wg.secretaries = Email.objects.filter(role__group=wg, role__name="secr") - milestone_state = "charter" if wg.state_id == "proposed" else "active" - wg.milestones = wg.groupmilestone_set.filter(state=milestone_state).order_by('due') + if group.charter: + group.charter_text = get_charter_text(group) + else: + group.charter_text = u"Not chartered yet." if include_drafts: - aliases = DocAlias.objects.filter(document__type="draft", document__group=wg).select_related('document').order_by("name") - wg.drafts = [] - wg.rfcs = [] + aliases = DocAlias.objects.filter(document__type="draft", document__group=group).select_related('document').order_by("name") + group.drafts = [] + group.rfcs = [] for a in aliases: if a.name.startswith("draft"): - wg.drafts.append(a) + group.drafts.append(a) else: - wg.rfcs.append(a) + group.rfcs.append(a) a.rel = RelatedDocument.objects.filter(source=a.document).distinct() a.invrel = RelatedDocument.objects.filter(target=a).distinct() -def wg_summary_acronym(request): - areas = Area.active_areas() - wgs = IETFWG.objects.filter(status=IETFWG.ACTIVE) - return HttpResponse(loader.render_to_string('wginfo/1wg-summary-by-acronym.txt', {'area_list': areas, 'wg_list': wgs}),mimetype='text/plain; charset=UTF-8') +def extract_last_name(role): + return role.person.name_parts()[3] def wg_summary_area(request): - wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False).exclude(parent=None) - return HttpResponse(loader.render_to_string('wginfo/1wg-summary.txt', {'wg_list': wgs}),mimetype='text/plain; charset=UTF-8') - -def wg_charters(request): - wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False).exclude(parent=None) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - for wg in wgs: - fill_in_charter_info(wg, include_drafts=True) - return HttpResponse(loader.render_to_string('wginfo/1wg-charters.txt', {'wg_list': wgs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}),mimetype='text/plain; charset=UTF-8') - -def wg_charters_by_acronym(request): - wgs = IETFWG.objects.filter(status='1',group_type='1',start_date__isnull=False) - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - for wg in wgs: - fill_in_charter_info(wg, include_drafts=True) - return HttpResponse(loader.render_to_string('wginfo/1wg-charters-by-acronym.txt', {'wg_list': wgs, 'USE_DB_REDESIGN_PROXY_CLASSES': settings.USE_DB_REDESIGN_PROXY_CLASSES}),mimetype='text/plain; charset=UTF-8') - -def wg_dir(request): - areas = Area.active_areas() - return render_to_response('wginfo/wg-dir.html', {'areas':areas}, RequestContext(request)) - -def wg_dirREDESIGN(request): - from ietf.group.models import Group, GroupURL - from ietf.person.models import Email - areas = Group.objects.filter(type="area", state="active").order_by("name") for area in areas: - area.ads = [] - for e in Email.objects.filter(role__group=area, role__name="ad").select_related("person"): - e.incoming = False - area.ads.append(e) + area.ads = sorted(roles(area, "ad"), key=extract_last_name) + area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") + for group in area.groups: + group.chairs = sorted(roles(group, "chair"), key=extract_last_name) - for e in Email.objects.filter(role__group=area, role__name="pre-ad").select_related("person"): - e.incoming = True - area.ads.append(e) + areas = [a for a in areas if a.groups] - area.ads.sort(key=lambda e: (e.incoming, e.person.name_parts()[3])) - area.wgs = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") + return render_to_response('wginfo/1wg-summary.txt', + { 'areas': areas }, + mimetype='text/plain; charset=UTF-8') + +def wg_summary_acronym(request): + areas = Group.objects.filter(type="area", state="active").order_by("name") + groups = Group.objects.filter(type="wg", state="active").order_by("acronym").select_related("parent") + for group in groups: + group.chairs = sorted(roles(group, "chair"), key=extract_last_name) + return render_to_response('wginfo/1wg-summary-by-acronym.txt', + { 'areas': areas, + 'groups': groups }, + mimetype='text/plain; charset=UTF-8') + +def wg_charters(request): + areas = Group.objects.filter(type="area", state="active").order_by("name") + for area in areas: + area.ads = sorted(roles(area, "ad"), key=extract_last_name) + area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("name") + for group in area.groups: + fill_in_charter_info(group, include_drafts=True) + group.area = area + return render_to_response('wginfo/1wg-charters.txt', + { 'areas': areas }, + mimetype='text/plain; charset=UTF-8') + +def wg_charters_by_acronym(request): + areas = dict((a.id, a) for a in Group.objects.filter(type="area", state="active").order_by("name")) + + for area in areas.itervalues(): + area.ads = sorted(roles(area, "ad"), key=extract_last_name) + + groups = Group.objects.filter(type="wg", state="active").exclude(parent=None).order_by("acronym") + for group in groups: + fill_in_charter_info(group, include_drafts=True) + group.area = areas.get(group.parent_id) + return render_to_response('wginfo/1wg-charters-by-acronym.txt', + { 'groups': groups }, + mimetype='text/plain; charset=UTF-8') + +def active_wgs(request): + areas = Group.objects.filter(type="area", state="active").order_by("name") + for area in areas: + # dig out information for template + area.ads = (list(sorted(roles(area, "ad"), key=extract_last_name)) + + list(sorted(roles(area, "pre-ad"), key=extract_last_name))) + + area.groups = Group.objects.filter(parent=area, type="wg", state="active").order_by("acronym") area.urls = area.groupurl_set.all().order_by("name") - for wg in area.wgs: - wg.chairs = sorted(Email.objects.filter(role__group=wg, role__name="chair").select_related("person"), key=lambda e: e.person.name_parts()[3]) - - return render_to_response('wginfo/wg-dirREDESIGN.html', {'areas':areas}, RequestContext(request)) + for group in area.groups: + group.chairs = sorted(roles(group, "chair"), key=extract_last_name) -if settings.USE_DB_REDESIGN_PROXY_CLASSES: - wg_dir = wg_dirREDESIGN + return render_to_response('wginfo/active_wgs.html', {'areas':areas}, RequestContext(request)) def bofs(request): groups = Group.objects.filter(type="wg", state="bof") - return render_to_response('wginfo/bofs.html',dict(groups=groups),RequestContext(request)) + return render_to_response('wginfo/bofs.html',dict(groups=groups), RequestContext(request)) def chartering_wgs(request): charter_states = State.objects.filter(used=True, type="charter").exclude(slug__in=("approved", "notrev")) groups = Group.objects.filter(type="wg", charter__states__in=charter_states).select_related("state", "charter") - for g in groups: g.chartering_type = get_chartering_type(g.charter) @@ -141,32 +161,56 @@ def chartering_wgs(request): RequestContext(request)) -def wg_documents(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - concluded = wg.status_id in [ 2, 3, ] - proposed = (wg.status_id == 4) - form = SearchForm({'by':'group', 'group':str(wg.group_acronym.acronym), - 'rfcs':'on', 'activedrafts':'on'}) +def construct_group_menu_context(request, group, selected, others): + """Return context with info for the group menu filled in.""" + actions = [] + + is_chair = group.has_role(request.user, "chair") + is_ad_or_secretariat = has_role(request.user, ("Area Director", "Secretariat")) + + if group.state_id != "proposed" and (is_chair or is_ad_or_secretariat): + actions.append((u"Add or edit milestones", urlreverse("wg_edit_milestones", kwargs=dict(acronym=group.acronym)))) + + if group.state_id != "conclude" and is_ad_or_secretariat: + actions.append((u"Edit group", urlreverse("group_edit", kwargs=dict(acronym=group.acronym)))) + + if is_chair or is_ad_or_secretariat: + actions.append((u"Customize workflow", urlreverse("ietf.wginfo.edit.customize_workflow", kwargs=dict(acronym=group.acronym)))) + + if group.state_id in ("active", "dormant") and is_ad_or_secretariat: + actions.append((u"Request closing group", urlreverse("wg_conclude", kwargs=dict(acronym=group.acronym)))) + + d = { + "group": group, + "selected": selected, + "menu_actions": actions, + } + + d.update(others) + + return d + +def search_for_group_documents(group): + form = SearchForm({ 'by':'group', 'group': group.acronym or "", 'rfcs':'on', 'activedrafts': 'on' }) docs, meta = retrieve_search_results(form) # get the related docs - form_related = SearchForm({'by':'group', 'name':'-'+str(wg.group_acronym.acronym)+'-', 'activedrafts':'on'}) - docs_related, meta_related = retrieve_search_results(form_related) - docs_related_pruned = [] - for d in docs_related: + form_related = SearchForm({ 'by':'group', 'name': u'-%s-' % group.acronym, 'activedrafts': 'on' }) + raw_docs_related, meta_related = retrieve_search_results(form_related) + + docs_related = [] + for d in raw_docs_related: parts = d.name.split("-", 2); # canonical form draft--wg-etc - if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(wg.group_acronym.acronym + "-"): + if len(parts) >= 3 and parts[1] != "ietf" and parts[2].startswith(group.acronym + "-"): d.search_heading = "Related Internet-Draft" - docs_related_pruned.append(d) - - docs_related = docs_related_pruned + docs_related.append(d) # move call for WG adoption to related cleaned_docs = [] docs_related_names = set(d.name for d in docs_related) for d in docs: - if d.stream_id == "ietf" and d.get_state_slug("draft-stream-ietf") in [ "c-adopt", "wg-cand" ]: + if d.stream_id and d.get_state_slug("draft-stream-%s" % d.stream_id) in ("c-adopt", "wg-cand"): if d.name not in docs_related_names: d.search_heading = "Related Internet-Draft" docs_related.append(d) @@ -177,59 +221,68 @@ def wg_documents(request, acronym): docs_related.sort(key=lambda d: d.name) - return wg, concluded, proposed, docs, meta, docs_related, meta_related + return docs, meta, docs_related, meta_related -def wg_documents_txt(request, acronym): - wg, concluded, proposed, docs, meta, docs_related, meta_related = wg_documents(request, acronym) - return HttpResponse(loader.render_to_string('wginfo/wg_documents.txt', {'wg': wg, 'concluded':concluded, 'proposed':proposed, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}),mimetype='text/plain; charset=UTF-8') +def group_documents(request, acronym): + group = get_object_or_404(Group, type="wg", acronym=acronym) -def wg_documents_html(request, acronym): - wg, concluded, proposed, docs, meta, docs_related, meta_related = wg_documents(request, acronym) - return render_to_response('wginfo/wg_documents.html', {'wg': wg, 'concluded':concluded, 'proposed':proposed, 'selected':'documents', 'docs':docs, 'meta':meta, 'docs_related':docs_related, 'meta_related':meta_related}, RequestContext(request)) + docs, meta, docs_related, meta_related = search_for_group_documents(group) -def wg_charter(request, acronym): - wg = get_object_or_404(IETFWG, group_acronym__acronym=acronym, group_type=1) - concluded = wg.status_id in [ 2, 3, ] - proposed = (wg.status_id == 4) + return render_to_response('wginfo/group_documents.html', + construct_group_menu_context(request, group, "documents", { + 'docs': docs, + 'meta': meta, + 'docs_related': docs_related, + 'meta_related': meta_related + }), RequestContext(request)) - fill_in_charter_info(wg) - actions = [] - if wg.state_id != "conclude": - actions.append(("Edit WG", urlreverse("wg_edit", kwargs=dict(acronym=wg.acronym)))) +def group_documents_txt(request, acronym): + """Return tabulator-separated rows with documents for group.""" + group = get_object_or_404(Group, type="wg", acronym=acronym) - e = wg.latest_event(type__in=("changed_state", "requested_close",)) - requested_close = wg.state_id != "conclude" and e and e.type == "requested_close" + docs, meta, docs_related, meta_related = search_for_group_documents(group) - if wg.state_id in ("active", "dormant"): - actions.append(("Request closing WG", urlreverse("wg_conclude", kwargs=dict(acronym=wg.acronym)))) + for d in docs: + d.prefix = d.get_state().name - context = get_wg_menu_context(wg, "charter") - context.update(dict( - actions=actions, - is_chair=request.user.is_authenticated() and wg.role_set.filter(name="chair", person__user=request.user), - milestones_in_review=wg.groupmilestone_set.filter(state="review"), - requested_close=requested_close, - )) + for d in docs_related: + d.prefix = u"Related %s" % d.get_state().name - return render_to_response('wginfo/wg_charter.html', - context, - RequestContext(request)) + rows = [] + for d in itertools.chain(docs, docs_related): + rfc_number = d.rfc_number() + if rfc_number != None: + name = rfc_number + else: + name = "%s-%s" % (d.name, d.rev) -def get_wg_menu_context(wg, selected): - # it would probably be better to refactor wginfo into rendering - # the menu separately instead of each view having to include the information + rows.append(u"\t".join((d.prefix, name, clean_whitespace(d.title)))) + + return HttpResponse(u"\n".join(rows), mimetype='text/plain; charset=UTF-8') + + +def group_charter(request, acronym): + group = get_object_or_404(Group, type="wg", acronym=acronym) + + fill_in_charter_info(group, include_drafts=False) + group.delegates = roles(group, "delegate") + + e = group.latest_event(type__in=("changed_state", "requested_close",)) + requested_close = group.state_id != "conclude" and e and e.type == "requested_close" + + return render_to_response('wginfo/group_charter.html', + construct_group_menu_context(request, group, "charter", { + "milestones_in_review": group.groupmilestone_set.filter(state="review"), + "requested_close": requested_close, + }), RequestContext(request)) - return dict(wg=wg, concluded=wg.state_id == "conclude", proposed=wg.state_id == "proposed", selected=selected) def history(request, acronym): - wg = get_object_or_404(Group, acronym=acronym) + group = get_object_or_404(Group, acronym=acronym) - events = wg.groupevent_set.all().select_related('by').order_by('-time', '-id') + events = group.groupevent_set.all().select_related('by').order_by('-time', '-id') - context = get_wg_menu_context(wg, "history") - context.update(dict(events=events, - )) - - wg.group_acronym = wg # hack for compatibility with old templates - - return render_to_response('wginfo/history.html', context, RequestContext(request)) + return render_to_response('wginfo/history.html', + construct_group_menu_context(request, group, "history", { + "events": events, + }), RequestContext(request)) diff --git a/permissions/.gitignore b/permissions/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/permissions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/permissions/__init__.py b/permissions/__init__.py deleted file mode 100644 index b81c7766c..000000000 --- a/permissions/__init__.py +++ /dev/null @@ -1,148 +0,0 @@ -import permissions.utils - -class PermissionBase(object): - """Mix-in class for permissions. - """ - def grant_permission(self, role, permission): - """Grants passed permission to passed role. Returns True if the - permission was able to be added, otherwise False. - - **Parameters:** - - role - The role for which the permission should be granted. - - permission - The permission which should be granted. Either a permission - object or the codename of a permission. - """ - return permissions.utils.grant_permission(self, role, permission) - - def remove_permission(self, role, permission): - """Removes passed permission from passed role. Returns True if the - permission has been removed. - - **Parameters:** - - role - The role for which a permission should be removed. - - permission - The permission which should be removed. Either a permission object - or the codename of a permission. - """ - return permissions.utils.remove_permission(self, role, permission) - - def has_permission(self, user, permission, roles=[]): - """Returns True if the passed user has passed permission for this - instance. Otherwise False. - - **Parameters:** - - permission - The permission's codename which should be checked. Must be a - string with a valid codename. - - user - The user for which the permission should be checked. - - roles - If passed, these roles will be assigned to the user temporarily - before the permissions are checked. - """ - return permissions.utils.has_permission(self, user, permission, roles) - - def check_permission(self, user, permission, roles=[]): - """Raise Unauthorized if the the passed user hasn't passed permission - for this instance. - - **Parameters:** - - permission - The permission's codename which should be checked. Must be a - string with a valid codename. - - user - The user for which the permission should be checked. - - roles - If passed, these roles will be assigned to the user temporarily - before the permissions are checked. - """ - if not self.has_permission(user, permission, roles): - raise Unauthorized("User %s doesn't have permission %s for object %s" % (user, permission, obj.slug)) - - def add_inheritance_block(self, permission): - """Adds an inheritance block for the passed permission. - - **Parameters:** - - permission - The permission for which an inheritance block should be added. - Either a permission object or the codename of a permission. - """ - return permissions.utils.add_inheritance_block(self, permission) - - def remove_inheritance_block(self, permission): - """Removes a inheritance block for the passed permission. - - **Parameters:** - - permission - The permission for which an inheritance block should be removed. - Either a permission object or the codename of a permission. - """ - return permissions.utils.remove_inheritance_block(self, permission) - - def is_inherited(self, codename): - """Returns True if the passed permission is inherited. - - **Parameters:** - - codename - The permission which should be checked. Must be the codename of - the permission. - """ - return permissions.utils.is_inherited(self, codename) - - def add_role(self, principal, role): - """Adds a local role for the principal. - - **Parameters:** - - principal - The principal (user or group) which gets the role. - - role - The role which is assigned. - """ - return permissions.utils.add_local_role(self, principal, role) - - def get_roles(self, principal): - """Returns *direct* local roles for passed principal (user or group). - """ - return permissions.utils.get_local_roles(self, principal) - - def remove_role(self, principal, role): - """Adds a local role for the principal to the object. - - **Parameters:** - - principal - The principal (user or group) from which the role is removed. - - role - The role which is removed. - """ - return permissions.utils.remove_local_role(self, principal, role) - - def remove_roles(self, principal): - """Removes all local roles for the passed principal from the object. - - **Parameters:** - - principal - The principal (user or group) from which all local roles are - removed. - """ - return permissions.utils.remove_local_roles(self, principal) \ No newline at end of file diff --git a/permissions/backend.py b/permissions/backend.py deleted file mode 100644 index a0c2d8ec6..000000000 --- a/permissions/backend.py +++ /dev/null @@ -1,45 +0,0 @@ -# permissions imports -import permissions.utils - -class ObjectPermissionsBackend(object): - """Django backend for object permissions. Needs Django 1.2. - - - Use it together with the default ModelBackend like so:: - - AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'permissions.backend.ObjectPermissionsBackend', - ) - - Then you can use it like: - - user.has_perm("view", your_object) - - """ - supports_object_permissions = True - supports_anonymous_user = True - - def authenticate(self, username, password): - return None - - def has_perm(self, user_obj, perm, obj=None): - """Checks whether the passed user has passed permission for passed - object (obj). - - This should be the primary method to check wether a user has a certain - permission. - - Parameters - ========== - - perm - The permission's codename which should be checked. - - user_obj - The user for which the permission should be checked. - - obj - The object for which the permission should be checked. - """ - return permissions.utils.has_permission(obj, user_obj, perm) \ No newline at end of file diff --git a/permissions/exceptions.py b/permissions/exceptions.py deleted file mode 100644 index 72c24a317..000000000 --- a/permissions/exceptions.py +++ /dev/null @@ -1,3 +0,0 @@ -class Unauthorized(Exception): - def __init__(self, str): - super(Unauthorized, self).__init__(str) \ No newline at end of file diff --git a/permissions/fixtures/initial.xml b/permissions/fixtures/initial.xml deleted file mode 100644 index 914abf8f1..000000000 --- a/permissions/fixtures/initial.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - View - view - - - Edit - edit - - - Delete - delete - - - Cut - cut - - - Copy - copy - - diff --git a/permissions/locale/de/LC_MESSAGES/django.mo b/permissions/locale/de/LC_MESSAGES/django.mo deleted file mode 100644 index 37b653476..000000000 Binary files a/permissions/locale/de/LC_MESSAGES/django.mo and /dev/null differ diff --git a/permissions/locale/de/LC_MESSAGES/django.po b/permissions/locale/de/LC_MESSAGES/django.po deleted file mode 100644 index 0a7e9e9cb..000000000 --- a/permissions/locale/de/LC_MESSAGES/django.po +++ /dev/null @@ -1,52 +0,0 @@ -# German translations for django-permissions -# Copyright (C) 2010 Kai Diefenbach -# This file is distributed under the same license as the PACKAGE package. -# Kai Diefenbach , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: 1.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-03-30 23:12+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: models.py:154 -msgid "Name" -msgstr "Name" - -#: models.py:155 -msgid "Codename" -msgstr "Codename" - -#: models.py:156 -msgid "Content Types" -msgstr "Inhaltstypen" - -#: models.py:175 models.py:280 -msgid "Role" -msgstr "Rolle" - -#: models.py:176 models.py:216 -msgid "Permission" -msgstr "Recht" - -#: models.py:178 models.py:218 models.py:282 -msgid "Content type" -msgstr "Inhaltstyp" - -#: models.py:179 models.py:219 models.py:283 -msgid "Content id" -msgstr "Inhalts-ID" - -#: models.py:278 -msgid "User" -msgstr "Benutzer" - -#: models.py:279 -msgid "Group" -msgstr "Gruppe" diff --git a/permissions/migrations/.gitignore b/permissions/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/permissions/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/permissions/migrations/0001_initial.py b/permissions/migrations/0001_initial.py deleted file mode 100644 index 38e36b067..000000000 --- a/permissions/migrations/0001_initial.py +++ /dev/null @@ -1,154 +0,0 @@ - -from south.db import db -from django.db import models -from permissions.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'Role' - db.create_table('permissions_role', ( - ('id', orm['permissions.Role:id']), - ('name', orm['permissions.Role:name']), - )) - db.send_create_signal('permissions', ['Role']) - - # Adding model 'ObjectPermissionInheritanceBlock' - db.create_table('permissions_objectpermissioninheritanceblock', ( - ('id', orm['permissions.ObjectPermissionInheritanceBlock:id']), - ('permission', orm['permissions.ObjectPermissionInheritanceBlock:permission']), - ('content_type', orm['permissions.ObjectPermissionInheritanceBlock:content_type']), - ('content_id', orm['permissions.ObjectPermissionInheritanceBlock:content_id']), - )) - db.send_create_signal('permissions', ['ObjectPermissionInheritanceBlock']) - - # Adding model 'ObjectPermission' - db.create_table('permissions_objectpermission', ( - ('id', orm['permissions.ObjectPermission:id']), - ('role', orm['permissions.ObjectPermission:role']), - ('permission', orm['permissions.ObjectPermission:permission']), - ('content_type', orm['permissions.ObjectPermission:content_type']), - ('content_id', orm['permissions.ObjectPermission:content_id']), - )) - db.send_create_signal('permissions', ['ObjectPermission']) - - # Adding model 'Permission' - db.create_table('permissions_permission', ( - ('id', orm['permissions.Permission:id']), - ('name', orm['permissions.Permission:name']), - ('codename', orm['permissions.Permission:codename']), - )) - db.send_create_signal('permissions', ['Permission']) - - # Adding model 'PrincipalRoleRelation' - db.create_table('permissions_principalrolerelation', ( - ('id', orm['permissions.PrincipalRoleRelation:id']), - ('user', orm['permissions.PrincipalRoleRelation:user']), - ('group', orm['permissions.PrincipalRoleRelation:group']), - ('role', orm['permissions.PrincipalRoleRelation:role']), - ('content_type', orm['permissions.PrincipalRoleRelation:content_type']), - ('content_id', orm['permissions.PrincipalRoleRelation:content_id']), - )) - db.send_create_signal('permissions', ['PrincipalRoleRelation']) - - # Adding ManyToManyField 'Permission.content_types' - db.create_table('permissions_permission_content_types', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('permission', models.ForeignKey(orm.Permission, null=False)), - ('contenttype', models.ForeignKey(orm['contenttypes.ContentType'], null=False)) - )) - - - - def backwards(self, orm): - - # Deleting model 'Role' - db.delete_table('permissions_role') - - # Deleting model 'ObjectPermissionInheritanceBlock' - db.delete_table('permissions_objectpermissioninheritanceblock') - - # Deleting model 'ObjectPermission' - db.delete_table('permissions_objectpermission') - - # Deleting model 'Permission' - db.delete_table('permissions_permission') - - # Deleting model 'PrincipalRoleRelation' - db.delete_table('permissions_principalrolerelation') - - # Dropping ManyToManyField 'Permission.content_types' - db.delete_table('permissions_permission_content_types') - - - - models = { - 'auth.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']", 'blank': 'True'}) - }, - 'auth.permission': { - 'Meta': {'unique_together': "(('content_type', 'codename'),)"}, - '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': { - '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']", 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}), - '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']", 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'permissions.objectpermission': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']", 'null': 'True', 'blank': 'True'}) - }, - 'permissions.objectpermissioninheritanceblock': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'permissions.principalrolerelation': { - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) - }, - 'permissions.role': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - } - } - - complete_apps = ['permissions'] diff --git a/permissions/migrations/__init__.py b/permissions/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/permissions/models.py b/permissions/models.py deleted file mode 100644 index 478fbdf9d..000000000 --- a/permissions/models.py +++ /dev/null @@ -1,190 +0,0 @@ -# django imports -from django.db import models -from django.contrib.auth.models import User -from django.contrib.auth.models import Group -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.utils.translation import ugettext_lazy as _ - -class Permission(models.Model): - """A permission which can be granted to users/groups and objects. - - **Attributes:** - - name - The unique name of the permission. This is displayed to users. - - codename - The unique codename of the permission. This is used internal to - identify a permission. - - content_types - The content types for which the permission is active. This can be - used to display only reasonable permissions for an object. - """ - name = models.CharField(_(u"Name"), max_length=100, unique=True) - codename = models.CharField(_(u"Codename"), max_length=100, unique=True) - content_types = models.ManyToManyField(ContentType, verbose_name=_(u"Content Types"), blank=True, null=True, related_name="content_types") - - def __unicode__(self): - return "%s (%s)" % (self.name, self.codename) - -class ObjectPermission(models.Model): - """Grants permission for specific user/group and object. - - **Attributes:** - - role - The role for which the permission is granted. - - permission - The permission which is granted. - - content - The object for which the permission is granted. - """ - role = models.ForeignKey("Role", verbose_name=_(u"Role"), blank=True, null=True) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type")) - content_id = models.PositiveIntegerField(verbose_name=_(u"Content id")) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - def __unicode__(self): - if self.role: - principal = self.role - else: - principal = self.user - - return "%s / %s / %s - %s" % (self.permission.name, principal, self.content_type, self.content_id) - - def get_principal(self): - """Returns the principal. - """ - return self.user or self.group - - def set_principal(self, principal): - """Sets the principal. - """ - if isinstance(principal, User): - self.user = principal - else: - self.group = principal - - principal = property(get_principal, set_principal) - -class ObjectPermissionInheritanceBlock(models.Model): - """Blocks the inheritance for specific permission and object. - - **Attributes:** - - permission - The permission for which inheritance is blocked. - - content - The object for which the inheritance is blocked. - """ - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type")) - content_id = models.PositiveIntegerField(verbose_name=_(u"Content id")) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - def __unicode__(self): - return "%s / %s - %s" % (self.permission, self.content_type, self.content_id) - -class Role(models.Model): - """A role gets permissions to do something. Principals (users and groups) - can only get permissions via roles. - - **Attributes:** - - name - The unique name of the role - """ - name = models.CharField(max_length=100, unique=True) - - class Meta: - ordering = ("name", ) - - def __unicode__(self): - return self.name - - def add_principal(self, principal, content=None): - """Addes the given principal (user or group) ot the Role. - """ - if isinstance(principal, User): - PrincipalRoleRelation.objects.create(user=principal, role=self) - else: - PrincipalRoleRelation.objects.create(group=principal, role=self) - - def get_groups(self, content=None): - """Returns all groups which has this role assigned. If content is given - it returns also the local roles. - """ - if content: - ctype = ContentType.objects.get_for_model(content) - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id__in = (None, content.pk), - content_type__in = (None, ctype)).exclude(group=None) - else: - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id=None, content_type=None).exclude(group=None) - - return [prr.group for prr in prrs] - - def get_users(self, content=None): - """Returns all users which has this role assigned. If content is given - it returns also the local roles. - """ - if content: - ctype = ContentType.objects.get_for_model(content) - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id__in = (None, content.pk), - content_type__in = (None, ctype)).exclude(user=None) - else: - prrs = PrincipalRoleRelation.objects.filter(role=self, - content_id=None, content_type=None).exclude(user=None) - - return [prr.user for prr in prrs] - -class PrincipalRoleRelation(models.Model): - """A role given to a principal (user or group). If a content object is - given this is a local role, i.e. the principal has this role only for this - content object. Otherwise it is a global role, i.e. the principal has - this role generally. - - user - A user instance. Either a user xor a group needs to be given. - - group - A group instance. Either a user xor a group needs to be given. - - role - The role which is given to the principal for content. - - content - The content object which gets the local role (optional). - """ - user = models.ForeignKey(User, verbose_name=_(u"User"), blank=True, null=True) - group = models.ForeignKey(Group, verbose_name=_(u"Group"), blank=True, null=True) - role = models.ForeignKey(Role, verbose_name=_(u"Role")) - - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), blank=True, null=True) - content_id = models.PositiveIntegerField(verbose_name=_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - - def get_principal(self): - """Returns the principal. - """ - return self.user or self.group - - def set_principal(self, principal): - """Sets the principal. - """ - if isinstance(principal, User): - self.user = principal - else: - self.group = principal - - principal = property(get_principal, set_principal) diff --git a/permissions/templatetags/.gitignore b/permissions/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/permissions/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/permissions/templatetags/__init__.py b/permissions/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/permissions/templatetags/permissions_tags.py b/permissions/templatetags/permissions_tags.py deleted file mode 100644 index 94116381f..000000000 --- a/permissions/templatetags/permissions_tags.py +++ /dev/null @@ -1,48 +0,0 @@ -# django imports -from django import template -from django.core.exceptions import ImproperlyConfigured -from django.contrib.auth.models import User, AnonymousUser - -import permissions.utils -register = template.Library() - -class PermissionComparisonNode(template.Node): - """Implements a node to provide an if current user has passed permission - for current object. - """ - @classmethod - def handle_token(cls, parser, token): - bits = token.contents.split() - if len(bits) != 2: - raise template.TemplateSyntaxError( - "'%s' tag takes one argument" % bits[0]) - end_tag = 'endifhasperm' - nodelist_true = parser.parse(('else', end_tag)) - token = parser.next_token() - if token.contents == 'else': # there is an 'else' clause in the tag - nodelist_false = parser.parse((end_tag,)) - parser.delete_first_token() - else: - nodelist_false = "" - - return cls(bits[1], nodelist_true, nodelist_false) - - def __init__(self, permission, nodelist_true, nodelist_false): - self.permission = permission - self.nodelist_true = nodelist_true - self.nodelist_false = nodelist_false - - def render(self, context): - obj = context.get("obj") - request = context.get("request") - if permissions.utils.has_permission(self.permission, request.user, obj): - return self.nodelist_true.render(context) - else: - return self.nodelist_false - -@register.tag -def ifhasperm(parser, token): - """This function provides functionality for the 'ifhasperm' template tag. - """ - return PermissionComparisonNode.handle_token(parser, token) - diff --git a/permissions/tests.py b/permissions/tests.py deleted file mode 100644 index 91bbf51bd..000000000 --- a/permissions/tests.py +++ /dev/null @@ -1,783 +0,0 @@ -# django imports -from django.contrib.flatpages.models import FlatPage -from django.contrib.auth.models import Group -from django.contrib.auth.models import User -from django.conf import settings -from django.core.urlresolvers import reverse -from django.test import TestCase -from django.test.client import Client - -# permissions imports -from permissions.models import Permission -from permissions.models import ObjectPermission -from permissions.models import ObjectPermissionInheritanceBlock -from permissions.models import Role - -import permissions.utils - -class BackendTestCase(TestCase): - """ - """ - def setUp(self): - """ - """ - settings.AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'permissions.backend.ObjectPermissionsBackend', - ) - - self.role_1 = permissions.utils.register_role("Role 1") - self.user = User.objects.create(username="john") - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - self.view = permissions.utils.register_permission("View", "view") - - # Add user to role - self.role_1.add_principal(self.user) - - def test_has_perm(self): - """Tests has perm of the backend. - """ - result = self.user.has_perm(self.view, self.page_1) - self.assertEqual(result, False) - - # assign view permission to role 1 - permissions.utils.grant_permission(self.page_1, self.role_1, self.view) - - result = self.user.has_perm("view", self.page_1) - self.assertEqual(result, True) - -class RoleTestCase(TestCase): - """ - """ - def setUp(self): - """ - """ - self.role_1 = permissions.utils.register_role("Role 1") - self.role_2 = permissions.utils.register_role("Role 2") - - self.user = User.objects.create(username="john") - self.group = Group.objects.create(name="brights") - - self.user.groups.add(self.group) - - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2") - - def test_getter(self): - """ - """ - result = permissions.utils.get_group(self.group.id) - self.assertEqual(result, self.group) - - result = permissions.utils.get_group(42) - self.assertEqual(result, None) - - result = permissions.utils.get_role(self.role_1.id) - self.assertEqual(result, self.role_1) - - result = permissions.utils.get_role(42) - self.assertEqual(result, None) - - result = permissions.utils.get_user(self.user.id) - self.assertEqual(result, self.user) - - result = permissions.utils.get_user(42) - self.assertEqual(result, None) - - def test_global_roles_user(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Add role 1 again - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_1]) - - # Add role 2 - result = permissions.utils.add_role(self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_role(self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_role(self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, []) - - def test_global_roles_group(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Add role 1 again - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_1]) - - # Add role 2 - result = permissions.utils.add_role(self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_role(self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_role(self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, []) - - def test_remove_roles_user(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Add role 2 - result = permissions.utils.add_role(self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove roles - result = permissions.utils.remove_roles(self.user) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.user) - self.assertEqual(result, []) - - # Remove roles - result = permissions.utils.remove_roles(self.user) - self.assertEqual(result, False) - - def test_remove_roles_group(self): - """ - """ - # Add role 1 - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Add role 2 - result = permissions.utils.add_role(self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove roles - result = permissions.utils.remove_roles(self.group) - self.assertEqual(result, True) - - result = permissions.utils.get_roles(self.group) - self.assertEqual(result, []) - - # Remove roles - result = permissions.utils.remove_roles(self.group) - self.assertEqual(result, False) - - def test_local_role_user(self): - """ - """ - # Add local role to page 1 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # Again - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_1]) - - # Add local role 2 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_local_role(self.page_1, self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, []) - - def test_local_role_group(self): - """ - """ - # Add local role to page 1 - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - # Again - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, [self.role_1]) - - # Add local role 2 - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove role 1 - result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - # Remove role 1 again - result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, False) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, [self.role_2]) - - # Remove role 2 - result = permissions.utils.remove_local_role(self.page_1, self.group, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.group) - self.assertEqual(result, []) - - def test_remove_local_roles_user(self): - """ - """ - # Add local role to page 1 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # Add local role 2 - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_2) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, [self.role_1, self.role_2]) - - # Remove all local roles - result = permissions.utils.remove_local_roles(self.page_1, self.user) - self.assertEqual(result, True) - - result = permissions.utils.get_local_roles(self.page_1, self.user) - self.assertEqual(result, []) - - # Remove all local roles again - result = permissions.utils.remove_local_roles(self.page_1, self.user) - self.assertEqual(result, False) - - def test_get_groups_1(self): - """Tests global roles for groups. - """ - result = self.role_1.get_groups() - self.assertEqual(len(result), 0) - - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - - # Add another group - self.group_2 = Group.objects.create(name="atheists") - result = permissions.utils.add_role(self.group_2, self.role_1) - - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - # Add the role to an user - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - def test_get_groups_2(self): - """Tests local roles for groups. - """ - result = self.role_1.get_groups(self.page_1) - self.assertEqual(len(result), 0) - - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - - # Add another local group - self.group_2 = Group.objects.create(name="atheists") - result = permissions.utils.add_local_role(self.page_1, self.group_2, self.role_1) - - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - - # A the global role to group - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # Nontheless there are just two groups returned (and no duplicate) - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - # Andere there should one global role - result = self.role_1.get_groups() - self.assertEqual(result[0].name, "brights") - - # Add the role to an user - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_groups(self.page_1) - self.assertEqual(result[0].name, "brights") - self.assertEqual(result[1].name, "atheists") - self.assertEqual(len(result), 2) - - def test_get_users_1(self): - """Tests global roles for users. - """ - result = self.role_1.get_users() - self.assertEqual(len(result), 0) - - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - - # Add another role to an user - self.user_2 = User.objects.create(username="jane") - result = permissions.utils.add_role(self.user_2, self.role_1) - - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - - # Add the role to an user - result = permissions.utils.add_role(self.group, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - - def test_get_users_2(self): - """Tests local roles for users. - """ - result = self.role_1.get_users(self.page_1) - self.assertEqual(len(result), 0) - - result = permissions.utils.add_local_role(self.page_1, self.user, self.role_1) - self.assertEqual(result, True) - - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - - # Add another local role to an user - self.user_2 = User.objects.create(username="jane") - result = permissions.utils.add_local_role(self.page_1, self.user_2, self.role_1) - - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - - # A the global role to user - result = permissions.utils.add_role(self.user, self.role_1) - self.assertEqual(result, True) - - # Nontheless there are just two users returned (and no duplicate) - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - - # Andere there should one user for the global role - result = self.role_1.get_users() - self.assertEqual(result[0].username, "john") - - # Add the role to an group - result = permissions.utils.add_local_role(self.page_1, self.group, self.role_1) - self.assertEqual(result, True) - - # This shouldn't have an effect on the result - result = self.role_1.get_users(self.page_1) - self.assertEqual(result[0].username, "john") - self.assertEqual(result[1].username, "jane") - self.assertEqual(len(result), 2) - -class PermissionTestCase(TestCase): - """ - """ - def setUp(self): - """ - """ - self.role_1 = permissions.utils.register_role("Role 1") - self.role_2 = permissions.utils.register_role("Role 2") - - self.user = User.objects.create(username="john") - permissions.utils.add_role(self.user, self.role_1) - self.user.save() - - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - self.page_2 = FlatPage.objects.create(url="/page-1/", title="Page 2") - - self.permission = permissions.utils.register_permission("View", "view") - - def test_add_permissions(self): - """ - """ - # Add per object - result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) - self.assertEqual(result, True) - - # Add per codename - result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - # Add ermission which does not exist - result = permissions.utils.grant_permission(self.page_1, self.role_1, "hurz") - self.assertEqual(result, False) - - def test_remove_permission(self): - """ - """ - # Add - result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - # Remove - result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - # Remove again - result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, False) - - def test_has_permission_role(self): - """ - """ - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - result = permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - result = permissions.utils.remove_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - def test_has_permission_owner(self): - """ - """ - creator = User.objects.create(username="jane") - - result = permissions.utils.has_permission(self.page_1, creator, "view") - self.assertEqual(result, False) - - owner = permissions.utils.register_role("Owner") - permissions.utils.grant_permission(self.page_1, owner, "view") - - result = permissions.utils.has_permission(self.page_1, creator, "view", [owner]) - self.assertEqual(result, True) - - def test_local_role(self): - """ - """ - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - permissions.utils.grant_permission(self.page_1, self.role_2, self.permission) - permissions.utils.add_local_role(self.page_1, self.user, self.role_2) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - def test_ineritance(self): - """ - """ - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - # per permission - permissions.utils.add_inheritance_block(self.page_1, self.permission) - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - permissions.utils.remove_inheritance_block(self.page_1, self.permission) - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - # per codename - permissions.utils.add_inheritance_block(self.page_1, "view") - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - permissions.utils.remove_inheritance_block(self.page_1, "view") - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - def test_unicode(self): - """ - """ - # Permission - self.assertEqual(self.permission.__unicode__(), "View (view)") - - # ObjectPermission - permissions.utils.grant_permission(self.page_1, self.role_1, self.permission) - opr = ObjectPermission.objects.get(permission=self.permission, role=self.role_1) - self.assertEqual(opr.__unicode__(), "View / Role 1 / flat page - 1") - - # ObjectPermissionInheritanceBlock - permissions.utils.add_inheritance_block(self.page_1, self.permission) - opb = ObjectPermissionInheritanceBlock.objects.get(permission=self.permission) - - self.assertEqual(opb.__unicode__(), "View (view) / flat page - 1") - - def test_reset(self): - """ - """ - result = permissions.utils.grant_permission(self.page_1, self.role_1, "view") - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - permissions.utils.add_inheritance_block(self.page_1, "view") - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - permissions.utils.reset(self.page_1) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, False) - - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - permissions.utils.reset(self.page_1) - -class RegistrationTestCase(TestCase): - """Tests the registration of different components. - """ - def test_group(self): - """Tests registering/unregistering of a group. - """ - # Register a group - result = permissions.utils.register_group("Brights") - self.failUnless(isinstance(result, Group)) - - # It's there - group = Group.objects.get(name="Brights") - self.assertEqual(group.name, "Brights") - - # Trying to register another group with same name - result = permissions.utils.register_group("Brights") - self.assertEqual(result, False) - - group = Group.objects.get(name="Brights") - self.assertEqual(group.name, "Brights") - - # Unregister the group - result = permissions.utils.unregister_group("Brights") - self.assertEqual(result, True) - - # It's not there anymore - self.assertRaises(Group.DoesNotExist, Group.objects.get, name="Brights") - - # Trying to unregister the group again - result = permissions.utils.unregister_group("Brights") - self.assertEqual(result, False) - - def test_role(self): - """Tests registering/unregistering of a role. - """ - # Register a role - result = permissions.utils.register_role("Editor") - self.failUnless(isinstance(result, Role)) - - # It's there - role = Role.objects.get(name="Editor") - self.assertEqual(role.name, "Editor") - - # Trying to register another role with same name - result = permissions.utils.register_role("Editor") - self.assertEqual(result, False) - - role = Role.objects.get(name="Editor") - self.assertEqual(role.name, "Editor") - - # Unregister the role - result = permissions.utils.unregister_role("Editor") - self.assertEqual(result, True) - - # It's not there anymore - self.assertRaises(Role.DoesNotExist, Role.objects.get, name="Editor") - - # Trying to unregister the role again - result = permissions.utils.unregister_role("Editor") - self.assertEqual(result, False) - - def test_permission(self): - """Tests registering/unregistering of a permission. - """ - # Register a permission - result = permissions.utils.register_permission("Change", "change") - self.failUnless(isinstance(result, Permission)) - - # Is it there? - p = Permission.objects.get(codename="change") - self.assertEqual(p.name, "Change") - - # Register a permission with the same codename - result = permissions.utils.register_permission("Change2", "change") - self.assertEqual(result, False) - - # Is it there? - p = Permission.objects.get(codename="change") - self.assertEqual(p.name, "Change") - - # Register a permission with the same name - result = permissions.utils.register_permission("Change", "change2") - self.assertEqual(result, False) - - # Is it there? - p = Permission.objects.get(codename="change") - self.assertEqual(p.name, "Change") - - # Unregister the permission - result = permissions.utils.unregister_permission("change") - self.assertEqual(result, True) - - # Is it not there anymore? - self.assertRaises(Permission.DoesNotExist, Permission.objects.get, codename="change") - - # Unregister the permission again - result = permissions.utils.unregister_permission("change") - self.assertEqual(result, False) - -# django imports -from django.core.handlers.wsgi import WSGIRequest -from django.contrib.auth.models import User -from django.contrib.sessions.backends.file import SessionStore -from django.test.client import Client - -# Taken from "http://www.djangosnippets.org/snippets/963/" -class RequestFactory(Client): - """ - Class that lets you create mock Request objects for use in testing. - - Usage: - - rf = RequestFactory() - get_request = rf.get('/hello/') - post_request = rf.post('/submit/', {'foo': 'bar'}) - - This class re-uses the django.test.client.Client interface, docs here: - http://www.djangoproject.com/documentation/testing/#the-test-client - - Once you have a request object you can pass it to any view function, - just as if that view had been hooked up using a URLconf. - - """ - def request(self, **request): - """ - Similar to parent class, but returns the request object as soon as it - has created it. - """ - environ = { - 'HTTP_COOKIE': self.cookies, - 'PATH_INFO': '/', - 'QUERY_STRING': '', - 'REQUEST_METHOD': 'GET', - 'SCRIPT_NAME': '', - 'SERVER_NAME': 'testserver', - 'SERVER_PORT': 80, - 'SERVER_PROTOCOL': 'HTTP/1.1', - } - environ.update(self.defaults) - environ.update(request) - return WSGIRequest(environ) - -def create_request(): - """ - """ - rf = RequestFactory() - request = rf.get('/') - request.session = SessionStore() - - user = User() - user.is_superuser = True - user.save() - request.user = user - - return request \ No newline at end of file diff --git a/permissions/utils.py b/permissions/utils.py deleted file mode 100644 index 06183e946..000000000 --- a/permissions/utils.py +++ /dev/null @@ -1,665 +0,0 @@ -# django imports -from django.db import IntegrityError -from django.db.models import Q -from django.db import connection -from django.contrib.auth.models import User -from django.contrib.auth.models import Group -from django.contrib.contenttypes.models import ContentType -from django.core.cache import cache -from django.core.exceptions import ObjectDoesNotExist - -# permissions imports -from permissions.exceptions import Unauthorized -from permissions.models import ObjectPermission -from permissions.models import ObjectPermissionInheritanceBlock -from permissions.models import Permission -from permissions.models import PrincipalRoleRelation -from permissions.models import Role - - -# Roles ###################################################################### - -def add_role(principal, role): - """Adds a global role to a principal. - - **Parameters:** - - principal - The principal (user or group) which gets the role added. - - role - The role which is assigned. - """ - if isinstance(principal, User): - try: - ppr = PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=None, content_type=None) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(user=principal, role=role) - return True - else: - try: - ppr = PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=None, content_type=None) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(group=principal, role=role) - return True - - return False - -def add_local_role(obj, principal, role): - """Adds a local role to a principal. - - **Parameters:** - - obj - The object for which the principal gets the role. - - principal - The principal (user or group) which gets the role. - - role - The role which is assigned. - """ - ctype = ContentType.objects.get_for_model(obj) - if isinstance(principal, User): - try: - ppr = PrincipalRoleRelation.objects.get(user=principal, role=role, content_id=obj.pk, content_type=ctype) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(user=principal, role=role, content=obj) - return True - else: - try: - ppr = PrincipalRoleRelation.objects.get(group=principal, role=role, content_id=obj.pk, content_type=ctype) - except PrincipalRoleRelation.DoesNotExist: - PrincipalRoleRelation.objects.create(group=principal, role=role, content=obj) - return True - - return False - -def remove_role(principal, role): - """Removes role from passed principal. - - **Parameters:** - - principal - The principal (user or group) from which the role is removed. - - role - The role which is removed. - """ - try: - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.get( - user=principal, role=role, content_id=None, content_type=None) - else: - ppr = PrincipalRoleRelation.objects.get( - group=principal, role=role, content_id=None, content_type=None) - - except PrincipalRoleRelation.DoesNotExist: - return False - else: - ppr.delete() - - return True - -def remove_local_role(obj, principal, role): - """Removes role from passed object and principle. - - **Parameters:** - - obj - The object from which the role is removed. - - principal - The principal (user or group) from which the role is removed. - - role - The role which is removed. - """ - try: - ctype = ContentType.objects.get_for_model(obj) - - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.get( - user=principal, role=role, content_id=obj.pk, content_type=ctype) - else: - ppr = PrincipalRoleRelation.objects.get( - group=principal, role=role, content_id=obj.pk, content_type=ctype) - - except PrincipalRoleRelation.DoesNotExist: - return False - else: - ppr.delete() - - return True - -def remove_roles(principal): - """Removes all roles passed principal (user or group). - - **Parameters:** - - principal - The principal (user or group) from which all roles are removed. - """ - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.filter( - user=principal, content_id=None, content_type=None) - else: - ppr = PrincipalRoleRelation.objects.filter( - group=principal, content_id=None, content_type=None) - - if ppr: - ppr.delete() - return True - else: - return False - -def remove_local_roles(obj, principal): - """Removes all local roles from passed object and principal (user or - group). - - **Parameters:** - - obj - The object from which the roles are removed. - - principal - The principal (user or group) from which the roles are removed. - """ - ctype = ContentType.objects.get_for_model(obj) - - if isinstance(principal, User): - ppr = PrincipalRoleRelation.objects.filter( - user=principal, content_id=obj.pk, content_type=ctype) - else: - ppr = PrincipalRoleRelation.objects.filter( - group=principal, content_id=obj.pk, content_type=ctype) - - if ppr: - ppr.delete() - return True - else: - return False - -def get_roles(user, obj=None): - """Returns *all* roles of the passed user. - - This takes direct roles and roles via the user's groups into account. - - If an object is passed local roles will also added. Then all local roles - from all ancestors and all user's groups are also taken into account. - - This is the method to use if one want to know whether the passed user - has a role in general (for the passed object). - - **Parameters:** - - user - The user for which the roles are returned. - - obj - The object for which local roles will returned. - - """ - roles = [] - groups = user.groups.all() - groups_ids_str = ", ".join([str(g.id) for g in groups]) - - # Gobal roles for user and the user's groups - cursor = connection.cursor() - cursor.execute("""SELECT role_id - FROM permissions_principalrolerelation - WHERE (user_id=%s OR group_id IN (%s)) - AND content_id is Null""" % (user.id, groups_ids_str)) - - for row in cursor.fetchall(): - roles.append(row[0]) - - # Local roles for user and the user's groups and all ancestors of the - # passed object. - while obj: - ctype = ContentType.objects.get_for_model(obj) - cursor.execute("""SELECT role_id - FROM permissions_principalrolerelation - WHERE (user_id='%s' OR group_id IN (%s)) - AND content_id='%s' - AND content_type_id='%s'""" % (user.id, groups_ids_str, obj.pk, ctype.id)) - - for row in cursor.fetchall(): - roles.append(row[0]) - - try: - obj = obj.get_parent_for_permissions() - except AttributeError: - obj = None - - return roles - -def get_global_roles(principal): - """Returns *direct* global roles of passed principal (user or group). - """ - if isinstance(principal, User): - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - user=principal, content_id=None, content_type=None)] - else: - if isinstance(principal, Group): - principal = (principal,) - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - group__in=principal, content_id=None, content_type=None)] - -def get_local_roles(obj, principal): - """Returns *direct* local roles for passed principal and content object. - """ - ctype = ContentType.objects.get_for_model(obj) - - if isinstance(principal, User): - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - user=principal, content_id=obj.pk, content_type=ctype)] - else: - return [prr.role for prr in PrincipalRoleRelation.objects.filter( - group=principal, content_id=obj.pk, content_type=ctype)] - -# Permissions ################################################################ - -def check_permission(obj, user, codename, roles=None): - """Checks whether passed user has passed permission for passed obj. - - **Parameters:** - - obj - The object for which the permission should be checked. - - codename - The permission's codename which should be checked. - - user - The user for which the permission should be checked. - - roles - If given these roles will be assigned to the user temporarily before - the permissions are checked. - """ - if not has_permission(obj, user, codename): - raise Unauthorized("User '%s' doesn't have permission '%s' for object '%s' (%s)" - % (user, codename, obj.slug, obj.__class__.__name__)) - -def grant_permission(obj, role, permission): - """Grants passed permission to passed role. Returns True if the permission - was able to be added, otherwise False. - - **Parameters:** - - obj - The content object for which the permission should be granted. - - role - The role for which the permission should be granted. - - permission - The permission which should be granted. Either a permission - object or the codename of a permission. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - try: - ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.pk, permission=permission) - except ObjectPermission.DoesNotExist: - ObjectPermission.objects.create(role=role, content=obj, permission=permission) - - return True - -def remove_permission(obj, role, permission): - """Removes passed permission from passed role and object. Returns True if - the permission has been removed. - - **Parameters:** - - obj - The content object for which a permission should be removed. - - role - The role for which a permission should be removed. - - permission - The permission which should be removed. Either a permission object - or the codename of a permission. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - - try: - op = ObjectPermission.objects.get(role=role, content_type = ct, content_id=obj.pk, permission = permission) - except ObjectPermission.DoesNotExist: - return False - - op.delete() - return True - -def has_permission(obj, user, codename, roles=None): - """Checks whether the passed user has passed permission for passed object. - - **Parameters:** - - obj - The object for which the permission should be checked. - - codename - The permission's codename which should be checked. - - request - The current request. - - roles - If given these roles will be assigned to the user temporarily before - the permissions are checked. - """ - cache_key = "%s-%s-%s" % (obj.content_type, obj.pk, codename) - result = _get_cached_permission(user, cache_key) - if result is not None: - return result - - if roles is None: - roles = [] - - if user.is_superuser: - return True - - if not user.is_anonymous(): - roles.extend(get_roles(user, obj)) - - ct = ContentType.objects.get_for_model(obj) - - result = False - while obj is not None: - p = ObjectPermission.objects.filter( - content_type=ct, content_id=obj.pk, role__in=roles, permission__codename = codename).values("id") - - if len(p) > 0: - result = True - break - - if is_inherited(obj, codename) == False: - result = False - break - - try: - obj = obj.get_parent_for_permissions() - ct = ContentType.objects.get_for_model(obj) - except AttributeError: - result = False - break - - _cache_permission(user, cache_key, result) - return result - -# Inheritance ################################################################ - -def add_inheritance_block(obj, permission): - """Adds an inheritance for the passed permission on the passed obj. - - **Parameters:** - - permission - The permission for which an inheritance block should be added. - Either a permission object or the codename of a permission. - obj - The content object for which an inheritance block should be added. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - try: - ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.pk, permission=permission) - except ObjectPermissionInheritanceBlock.DoesNotExist: - try: - result = ObjectPermissionInheritanceBlock.objects.create(content=obj, permission=permission) - except IntegrityError: - return False - return True - -def remove_inheritance_block(obj, permission): - """Removes a inheritance block for the passed permission from the passed - object. - - **Parameters:** - - obj - The content object for which an inheritance block should be added. - - permission - The permission for which an inheritance block should be removed. - Either a permission object or the codename of a permission. - """ - if not isinstance(permission, Permission): - try: - permission = Permission.objects.get(codename = permission) - except Permission.DoesNotExist: - return False - - ct = ContentType.objects.get_for_model(obj) - try: - opi = ObjectPermissionInheritanceBlock.objects.get(content_type = ct, content_id=obj.pk, permission=permission) - except ObjectPermissionInheritanceBlock.DoesNotExist: - return False - - opi.delete() - return True - -def is_inherited(obj, codename): - """Returns True if the passed permission is inherited for passed object. - - **Parameters:** - - obj - The content object for which the permission should be checked. - - codename - The permission which should be checked. Must be the codename of - the permission. - """ - ct = ContentType.objects.get_for_model(obj) - try: - ObjectPermissionInheritanceBlock.objects.get( - content_type=ct, content_id=obj.pk, permission__codename = codename) - except ObjectDoesNotExist: - return True - else: - return False - -def get_group(id): - """Returns the group with passed id or None. - """ - try: - return Group.objects.get(pk=id) - except Group.DoesNotExist: - return None - -def get_role(id): - """Returns the role with passed id or None. - """ - try: - return Role.objects.get(pk=id) - except Role.DoesNotExist: - return None - -def get_user(id): - """Returns the user with passed id or None. - """ - try: - return User.objects.get(pk=id) - except User.DoesNotExist: - return None - -def has_group(user, group): - """Returns True if passed user has passed group. - """ - if isinstance(group, str): - group = Group.objects.get(name=group) - - return group in user.groups.all() - -def reset(obj): - """Resets all permissions and inheritance blocks of passed object. - """ - ctype = ContentType.objects.get_for_model(obj) - ObjectPermissionInheritanceBlock.objects.filter(content_id=obj.pk, content_type=ctype).delete() - ObjectPermission.objects.filter(content_id=obj.pk, content_type=ctype).delete() - -# Registering ################################################################ - -def register_permission(name, codename, ctypes=[]): - """Registers a permission to the framework. Returns the permission if the - registration was successfully, otherwise False. - - **Parameters:** - - name - The unique name of the permission. This is displayed to the - customer. - codename - The unique codename of the permission. This is used internally to - identify the permission. - content_types - The content type for which the permission is active. This can be - used to display only reasonable permissions for an object. This - must be a Django ContentType - """ - try: - p = Permission.objects.create(name=name, codename=codename) - - ctypes = [ContentType.objects.get_for_model(ctype) for ctype in ctypes] - if ctypes: - p.content_types = ctypes - p.save() - except IntegrityError: - return False - return p - -def unregister_permission(codename): - """Unregisters a permission from the framework - - **Parameters:** - - codename - The unique codename of the permission. - """ - try: - permission = Permission.objects.get(codename=codename) - except Permission.DoesNotExist: - return False - permission.delete() - return True - -def register_role(name): - """Registers a role with passed name to the framework. Returns the new - role if the registration was successfully, otherwise False. - - **Parameters:** - - name - The unique role name. - """ - try: - role = Role.objects.create(name=name) - except IntegrityError: - return False - return role - -def unregister_role(name): - """Unregisters the role with passed name. - - **Parameters:** - - name - The unique role name. - """ - try: - role = Role.objects.get(name=name) - except Role.DoesNotExist: - return False - - role.delete() - return True - -def register_group(name): - """Registers a group with passed name to the framework. Returns the new - group if the registration was successfully, otherwise False. - - Actually this creates just a default Django Group. - - **Parameters:** - - name - The unique group name. - """ - try: - group = Group.objects.create(name=name) - except IntegrityError: - return False - return group - -def unregister_group(name): - """Unregisters the group with passed name. Returns True if the - unregistration was succesfull otherwise False. - - Actually this deletes just a default Django Group. - - **Parameters:** - - name - The unique role name. - """ - try: - group = Group.objects.get(name=name) - except Group.DoesNotExist: - return False - - group.delete() - return True - -def _cache_permission(user, cache_key, data): - """Stores the passed data on the passed user object. - - **Parameters:** - - user - The user on which the data is stored. - - cache_key - The key under which the data is stored. - - data - The data which is stored. - """ - if not getattr(user, "permissions", None): - user.permissions = {} - user.permissions[cache_key] = data - -def _get_cached_permission(user, cache_key): - """Returns the stored data from passed user object for passed cache_key. - - **Parameters:** - - user - The user from which the data is retrieved. - - cache_key - The key under which the data is stored. - - """ - permissions = getattr(user, "permissions", None) - if permissions: - return user.permissions.get(cache_key, None) diff --git a/static/css/base2.css b/static/css/base2.css index 53cec6094..520c1efb1 100644 --- a/static/css/base2.css +++ b/static/css/base2.css @@ -76,6 +76,7 @@ a img { border: 0; } .ietf-navset .selected { font-weight:bold; padding: 0 3px; } .ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } +.ietf-navset .actions { margin-top: 0.5em; font-style: italic; font-size: 90%; } .ietf-ballot .left { background: #edf5ff; width:160px; padding-left: 10px; } .ietf-ballot .right { padding-left: 15px; padding-right:15px; width:610px;padding-top:0px;} @@ -187,7 +188,7 @@ form table .help { .color2 { color: #00ffff; } .bgcolor1 { background-color: #ffb000; } .bgcolor2 { background-color: #00ffff; } -.square { width: 0.8ex; height: 0.8ex; margin: 0; padding: 0; display: inline-block; position: relative; top: 0.8ex; text-align: top;} +.square { width: 0.8ex; height: 0.8ex; margin: 0; padding: 0; display: inline-block; position: relative; top: 0.8ex; } .big { font-size: 109.5%; margin: 0; padding: 0; } .large { font-size: 120%; margin: 0; padding: 0; } .huge { font-size: 144%; margin: 0; padding: 0; } @@ -237,6 +238,8 @@ li.error { margin: 0.5em; background-color: #f44; } font-family: Arial, sans-serif; } +.errorlist a { color: #fff; } + .group-documents .search-results { margin-top: 1.5em; } table.milestones td.due { vertical-align: top; width: 80px; } @@ -374,6 +377,24 @@ span.fieldRequired { background-color: #ffcc66; } +.state-help-icon { + display: inline-block; + margin-left: 0.2em; + padding: 0 0.2em; + font-weight: bold; + font-style: normal; + font-size: 90%; + color: #999; + background-color: #ddd; + text-decoration: none; +} + +.state-help-icon:hover { + color: #eee; + background-color: #bbb; + transition-duration: 0.2s; +} + /* js styles */ .js-info { background-color: #FFDD88; diff --git a/static/css/submit.css b/static/css/submit.css new file mode 100644 index 000000000..052987d8c --- /dev/null +++ b/static/css/submit.css @@ -0,0 +1,32 @@ +.ietf-navset { + background:#214197 url(/images/yui/sprite.png) repeat-x left -1400px; + color:white; + border:1px solid black; + padding:4px; +} +.ietf-navset .selected { font-weight:bold; padding: 0 3px; } +.ietf-navset a, .ietf-navset a:visited { color: white; padding:0 3px; } +.cutoff-warning { border: 1px dashed red; background-color: #ffeeaa; padding: 1em 2em; margin: 1em 0px; } +.problem-reports-footer { font-style: italic; margin-top: 2em; } + +div.metadata-errors { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; max-width: 50em; } +div.info-message-error { border: 1px solid red; background-color: #ffeebb; padding: 5px 10px; margin: 1em 0px; color: red; } +div.info-message-warning { border: 1px solid orange; background-color: #ffffaa; padding: 5px 10px; margin: 1em 0px; color: black; } +div.info-message-success { border: 1px solid green; background-color: #eeffbb; padding: 5px 10px; margin: 1em 0px; color: green; } + +table.metadata-table th { white-space: nowrap; font-weight: bold; } +table.metadata-table th, table.metadata-table td { text-align: left; background: #ddddff; padding: 5px 10px; } +table.metadata-table th.author { text-align: right; } +table.metadata-table tr { vertical-align: top; } +table.metadata-table tr.error td, table.metadata-table tr.error th { background-color: #ffaaaa; } +table.metadata-table div.error-msg { color: red; } +table.metadata-table td.author-button-help { max-width: 40em; } +table.metadata-table input.name, table.metadata-table input.email { width: 25em; } + +pre.twopages { margin: 0px; } + +.idnits-popup .content, +.twopages-popup .content { background-color: #fff; width: 55em; height: 30em; overflow: auto; padding: 1em; } +a.idnits-trigger, a.twopages-trigger, a.idnits-trigger:visited, a.twopages-trigger:visited { color: #000; } + +table.history { max-width: 50em; } diff --git a/static/js/draft-submit.js b/static/js/draft-submit.js index fa9917143..0d51b657a 100644 --- a/static/js/draft-submit.js +++ b/static/js/draft-submit.js @@ -1,12 +1,60 @@ -$(function (){ +jQuery(function (){ // fill in submitter info when an author button is clicked - $("input[type=button]").click(function () { - var name = $(this).data("name"); - if (name == null) // backwards compatibility - return; - var email = $(this).data("email"); + jQuery("input[type=button].author").click(function () { + var name = jQuery(this).data("name"); + var email = jQuery(this).data("email"); - $(this).parents("form").find("input[name=name]").val(name || ""); - $(this).parents("form").find("input[name=email]").val(email || ""); + jQuery(this).parents("form").find("input[name=submitter-name]").val(name || ""); + jQuery(this).parents("form").find("input[name=submitter-email]").val(email || ""); + }); + + jQuery("form").submit(function() { + if (this.submittedAlready) + return false; + else { + this.submittedAlready = true; + return true; + } + }); + + jQuery("form#cancel-submission").submit(function () { + return confirm("Cancel this submission?"); + }); + + jQuery(".idnits-trigger").click(function (e) { + e.preventDefault(); + var popup = jQuery(".idnits-popup").clone().show(); + showModalBox(popup); + }); + + jQuery(".twopages-trigger").click(function (e) { + e.preventDefault(); + var popup = jQuery(".twopages-popup").clone().show(); + showModalBox(popup); + }); + + jQuery("form .add-author").click(function (e) { + e.preventDefault(); + + var table = jQuery("table.authors tbody"); + var row = table.find("tr.empty").clone(); + + row.removeClass("empty"); + var prefixInput = row.find('input[name=authors-prefix]'); + + // figure out a prefix + var i = 0, prefix; + do { + ++i; + prefix = prefixInput.val() + i; + } + while (table.find('input[name=authors-prefix][value="' + prefix +'"]').length > 0); + + prefixInput.val(prefix); + row.find('input').not(prefixInput).each(function () { + this.name = prefix + "-" + this.name; + }); + + table.append(row); }); }); diff --git a/static/js/iesg-discusses.js b/static/js/iesg-discusses.js new file mode 100644 index 000000000..f3b98e151 --- /dev/null +++ b/static/js/iesg-discusses.js @@ -0,0 +1,33 @@ +jQuery(function () { + var radioButtons = jQuery('input[name="discusses"]'); + + var url = window.location.hash.replace("#", ""); + if (url == "byme" || url == "forme") + radioButtons.filter("[value=" + url + "]").click(); + + function updateDisplayedRows() { + var rows = jQuery(".discuss-row"); + + var val = radioButtons.filter(":checked").val(); + + if (val == "all") { + rows.show(); + if (window.location.hash) + window.location.hash = ""; + } + else if (val == "forme" || val == "byme") { + console.log(rows.filter("." + val)) + rows.filter("." + val).show(); + rows.not("." + val).hide(); + window.location.hash = val; + } + + // odd/even are swapped because the jQuery filter is 0-indexed + rows.filter(":visible").filter(":even").removeClass("even").addClass("odd"); + rows.filter(":visible").filter(":odd").removeClass("odd").addClass("even"); + } + + radioButtons.click(updateDisplayedRows); + + updateDisplayedRows(); +}); diff --git a/static/secretariat/css/custom.css b/static/secretariat/css/custom.css index 0205d85ff..07be32d83 100644 --- a/static/secretariat/css/custom.css +++ b/static/secretariat/css/custom.css @@ -661,6 +661,10 @@ ul.session-buttons { list-style-type: circle; } +#telechat-sidebar ul.doc-list { + margin-bottom: 0.8em; +} + ul.doc-list li { list-style-type: circle; } @@ -685,6 +689,18 @@ ul.doc-list li { font-size: 110%; } +#telechat-sidebar li.level3 + li.level2 { + margin-top: 1em; +} + +#telechat-sidebar li.level3 + li.level1 { + margin-top: 1.5em; +} + +#telechat-sidebar li div { + font-style: italic; +} + #telechat-positions-table td { text-align: center; } diff --git a/workflows/.gitignore b/workflows/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/workflows/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/workflows/__init__.py b/workflows/__init__.py deleted file mode 100644 index 271167dbe..000000000 --- a/workflows/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# workflows imports -import workflows.utils - -class WorkflowBase(object): - """Mixin class to make objects workflow aware. - """ - def get_workflow(self): - """Returns the current workflow of the object. - """ - return workflows.utils.get_workflow(self) - - def remove_workflow(self): - """Removes the workflow from the object. After this function has been - called the object has no *own* workflow anymore (it might have one via - its content type). - - """ - return workflows.utils.remove_workflow_from_object(self) - - def set_workflow(self, workflow): - """Sets the passed workflow to the object. This will set the local - workflow for the object. - - If the object has already the given workflow nothing happens. - Otherwise the object gets the passed workflow and the state is set to - the workflow's initial state. - - **Parameters:** - - workflow - The workflow which should be set to the object. Can be a Workflow - instance or a string with the workflow name. - obj - The object which gets the passed workflow. - """ - return workflows.utils.set_workflow_for_object(workflow) - - def get_state(self): - """Returns the current workflow state of the object. - """ - return workflows.utils.get_state(self) - - def set_state(self, state): - """Sets the workflow state of the object. - """ - return workflows.utils.set_state(self, state) - - def set_initial_state(self): - """Sets the initial state of the current workflow to the object. - """ - return self.set_state(self.get_workflow().initial_state) - - def get_allowed_transitions(self, user): - """Returns allowed transitions for the current state. - """ - return workflows.utils.get_allowed_transitions(self, user) - - def do_transition(self, transition, user): - """Processes the passed transition (if allowed). - """ - return workflows.utils.do_transition(self, transition, user) \ No newline at end of file diff --git a/workflows/locale/de/LC_MESSAGES/django.mo b/workflows/locale/de/LC_MESSAGES/django.mo deleted file mode 100644 index e4512512f..000000000 Binary files a/workflows/locale/de/LC_MESSAGES/django.mo and /dev/null differ diff --git a/workflows/locale/de/LC_MESSAGES/django.po b/workflows/locale/de/LC_MESSAGES/django.po deleted file mode 100644 index 2b97ddef5..000000000 --- a/workflows/locale/de/LC_MESSAGES/django.po +++ /dev/null @@ -1,60 +0,0 @@ -# German translations for django-workflows -# Copyright (C) 2010 Kai Diefenbach -# This file is distributed under the same license as the PACKAGE package. -# Kai Diefenbach , 2010. -# -msgid "" -msgstr "" -"Project-Id-Version: 1.0\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-04-02 09:16+0200\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: models.py:98 models.py:199 models.py:237 -msgid "Name" -msgstr "Name" - -#: models.py:200 models.py:238 models.py:285 models.py:307 -msgid "Workflow" -msgstr "Arbeitsablauf" - -#: models.py:201 -msgid "Transitions" -msgstr "Übergänge" - -#: models.py:239 -msgid "Destination" -msgstr "Ziel" - -#: models.py:240 -msgid "Condition" -msgstr "Kondition" - -#: models.py:241 models.py:350 models.py:373 -msgid "Permission" -msgstr "Recht" - -#: models.py:258 models.py:282 -msgid "Content type" -msgstr "Inhaltstyp" - -#: models.py:259 models.py:283 -msgid "Content id" -msgstr "Inhalts-ID" - -#: models.py:261 models.py:349 models.py:372 -msgid "State" -msgstr "Status" - -#: models.py:306 -msgid "Content Type" -msgstr "Inhaltstyp" - -#: models.py:374 -msgid "Role" -msgstr "Rolle" diff --git a/workflows/migrations/.gitignore b/workflows/migrations/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/workflows/migrations/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/workflows/migrations/0001_initial.py b/workflows/migrations/0001_initial.py deleted file mode 100644 index d12f59ff3..000000000 --- a/workflows/migrations/0001_initial.py +++ /dev/null @@ -1,225 +0,0 @@ - -from south.db import db -from django.db import models -from workflows.models import * - -class Migration: - - def forwards(self, orm): - - # Adding model 'Workflow' - db.create_table('workflows_workflow', ( - ('id', orm['workflows.Workflow:id']), - ('name', orm['workflows.Workflow:name']), - ('initial_state', orm['workflows.Workflow:initial_state']), - )) - db.send_create_signal('workflows', ['Workflow']) - - # Adding model 'StatePermissionRelation' - db.create_table('workflows_statepermissionrelation', ( - ('id', orm['workflows.StatePermissionRelation:id']), - ('state', orm['workflows.StatePermissionRelation:state']), - ('permission', orm['workflows.StatePermissionRelation:permission']), - ('role', orm['workflows.StatePermissionRelation:role']), - )) - db.send_create_signal('workflows', ['StatePermissionRelation']) - - # Adding model 'StateInheritanceBlock' - db.create_table('workflows_stateinheritanceblock', ( - ('id', orm['workflows.StateInheritanceBlock:id']), - ('state', orm['workflows.StateInheritanceBlock:state']), - ('permission', orm['workflows.StateInheritanceBlock:permission']), - )) - db.send_create_signal('workflows', ['StateInheritanceBlock']) - - # Adding model 'WorkflowModelRelation' - db.create_table('workflows_workflowmodelrelation', ( - ('id', orm['workflows.WorkflowModelRelation:id']), - ('content_type', orm['workflows.WorkflowModelRelation:content_type']), - ('workflow', orm['workflows.WorkflowModelRelation:workflow']), - )) - db.send_create_signal('workflows', ['WorkflowModelRelation']) - - # Adding model 'WorkflowPermissionRelation' - db.create_table('workflows_workflowpermissionrelation', ( - ('id', orm['workflows.WorkflowPermissionRelation:id']), - ('workflow', orm['workflows.WorkflowPermissionRelation:workflow']), - ('permission', orm['workflows.WorkflowPermissionRelation:permission']), - )) - db.send_create_signal('workflows', ['WorkflowPermissionRelation']) - - # Adding model 'State' - db.create_table('workflows_state', ( - ('id', orm['workflows.State:id']), - ('name', orm['workflows.State:name']), - ('workflow', orm['workflows.State:workflow']), - )) - db.send_create_signal('workflows', ['State']) - - # Adding model 'Transition' - db.create_table('workflows_transition', ( - ('id', orm['workflows.Transition:id']), - ('name', orm['workflows.Transition:name']), - ('workflow', orm['workflows.Transition:workflow']), - ('destination', orm['workflows.Transition:destination']), - ('condition', orm['workflows.Transition:condition']), - ('permission', orm['workflows.Transition:permission']), - )) - db.send_create_signal('workflows', ['Transition']) - - # Adding model 'WorkflowObjectRelation' - db.create_table('workflows_workflowobjectrelation', ( - ('id', orm['workflows.WorkflowObjectRelation:id']), - ('content_type', orm['workflows.WorkflowObjectRelation:content_type']), - ('content_id', orm['workflows.WorkflowObjectRelation:content_id']), - ('workflow', orm['workflows.WorkflowObjectRelation:workflow']), - )) - db.send_create_signal('workflows', ['WorkflowObjectRelation']) - - # Adding model 'StateObjectRelation' - db.create_table('workflows_stateobjectrelation', ( - ('id', orm['workflows.StateObjectRelation:id']), - ('content_type', orm['workflows.StateObjectRelation:content_type']), - ('content_id', orm['workflows.StateObjectRelation:content_id']), - ('state', orm['workflows.StateObjectRelation:state']), - )) - db.send_create_signal('workflows', ['StateObjectRelation']) - - # Adding ManyToManyField 'State.transitions' - db.create_table('workflows_state_transitions', ( - ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), - ('state', models.ForeignKey(orm.State, null=False)), - ('transition', models.ForeignKey(orm.Transition, null=False)) - )) - - # Creating unique_together for [content_type, content_id] on WorkflowObjectRelation. - db.create_unique('workflows_workflowobjectrelation', ['content_type_id', 'content_id']) - - # Creating unique_together for [content_type, content_id, state] on StateObjectRelation. - db.create_unique('workflows_stateobjectrelation', ['content_type_id', 'content_id', 'state_id']) - - # Creating unique_together for [workflow, permission] on WorkflowPermissionRelation. - db.create_unique('workflows_workflowpermissionrelation', ['workflow_id', 'permission_id']) - - - - def backwards(self, orm): - - # Deleting unique_together for [workflow, permission] on WorkflowPermissionRelation. - db.delete_unique('workflows_workflowpermissionrelation', ['workflow_id', 'permission_id']) - - # Deleting unique_together for [content_type, content_id, state] on StateObjectRelation. - db.delete_unique('workflows_stateobjectrelation', ['content_type_id', 'content_id', 'state_id']) - - # Deleting unique_together for [content_type, content_id] on WorkflowObjectRelation. - db.delete_unique('workflows_workflowobjectrelation', ['content_type_id', 'content_id']) - - # Deleting model 'Workflow' - db.delete_table('workflows_workflow') - - # Deleting model 'StatePermissionRelation' - db.delete_table('workflows_statepermissionrelation') - - # Deleting model 'StateInheritanceBlock' - db.delete_table('workflows_stateinheritanceblock') - - # Deleting model 'WorkflowModelRelation' - db.delete_table('workflows_workflowmodelrelation') - - # Deleting model 'WorkflowPermissionRelation' - db.delete_table('workflows_workflowpermissionrelation') - - # Deleting model 'State' - db.delete_table('workflows_state') - - # Deleting model 'Transition' - db.delete_table('workflows_transition') - - # Deleting model 'WorkflowObjectRelation' - db.delete_table('workflows_workflowobjectrelation') - - # Deleting model 'StateObjectRelation' - db.delete_table('workflows_stateobjectrelation') - - # Dropping ManyToManyField 'State.transitions' - db.delete_table('workflows_state_transitions') - - - - models = { - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", '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'}) - }, - 'permissions.permission': { - 'codename': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'content_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'permissions.role': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}) - }, - 'workflows.state': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'transitions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['workflows.Transition']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'states'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.stateinheritanceblock': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.stateobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id', 'state'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'state_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.statepermissionrelation': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']"}), - 'role': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Role']"}), - 'state': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.State']"}) - }, - 'workflows.transition': { - 'condition': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), - 'destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destination_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['permissions.Permission']", 'null': 'True', 'blank': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transitions'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflow': { - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'initial_state': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_state'", 'null': 'True', 'to': "orm['workflows.State']"}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['permissions.Permission']", 'symmetrical': 'False'}) - }, - 'workflows.workflowmodelrelation': { - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'unique': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wmrs'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowobjectrelation': { - 'Meta': {'unique_together': "(('content_type', 'content_id'),)"}, - 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'workflow_object'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wors'", 'to': "orm['workflows.Workflow']"}) - }, - 'workflows.workflowpermissionrelation': { - 'Meta': {'unique_together': "(('workflow', 'permission'),)"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'permission': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'to': "orm['permissions.Permission']"}), - 'workflow': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['workflows.Workflow']"}) - } - } - - complete_apps = ['workflows'] diff --git a/workflows/migrations/__init__.py b/workflows/migrations/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/workflows/models.py b/workflows/models.py deleted file mode 100644 index d7b5ac68d..000000000 --- a/workflows/models.py +++ /dev/null @@ -1,357 +0,0 @@ -from django.db import models - -# django imports -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -from django.utils.translation import ugettext_lazy as _ - -# permissions imports -import permissions.utils -from permissions.models import Permission -from permissions.models import Role - -class Workflow(models.Model): - """A workflow consists of a sequence of connected (through transitions) - states. It can be assigned to a model and / or model instances. If a - model instance has a workflow it takes precendence over the model's - workflow. - - **Attributes:** - - model - The model the workflow belongs to. Can be any - - content - The object the workflow belongs to. - - name - The unique name of the workflow. - - states - The states of the workflow. - - initial_state - The initial state the model / content gets if created. - - """ - name = models.CharField(_(u"Name"), max_length=100, unique=True) - initial_state = models.ForeignKey("State", related_name="workflow_state", blank=True, null=True) - permissions = models.ManyToManyField(Permission, symmetrical=False, through="WorkflowPermissionRelation") - - def __unicode__(self): - return self.name - - def get_initial_state(self): - """Returns the initial state of the workflow. Takes the first one if - no state has been defined. - """ - if self.initial_state: - return self.initial_state - else: - try: - return self.states.all()[0] - except IndexError: - return None - - def get_objects(self): - """Returns all objects which have this workflow assigned. Globally - (via the object's content type) or locally (via the object itself). - """ - import workflows.utils - objs = [] - - # Get all objects whose content type has this workflow - for wmr in WorkflowModelRelation.objects.filter(workflow=self): - ctype = wmr.content_type - # We have also to check whether the global workflow is not - # overwritten. - for obj in ctype.model_class().objects.all(): - if workflows.utils.get_workflow(obj) == self: - objs.append(obj) - - # Get all objects whose local workflow this workflow - for wor in WorkflowObjectRelation.objects.filter(workflow=self): - if wor.content not in objs: - objs.append(wor.content) - - return objs - - def set_to(self, ctype_or_obj): - """Sets the workflow to passed content type or object. See the specific - methods for more information. - - **Parameters:** - - ctype_or_obj - The content type or the object to which the workflow should be set. - Can be either a ContentType instance or any Django model instance. - """ - if isinstance(ctype_or_obj, ContentType): - return self.set_to_model(ctype_or_obj) - else: - return self.set_to_object(ctype_or_obj) - - def set_to_model(self, ctype): - """Sets the workflow to the passed content type. If the content - type has already an assigned workflow the workflow is overwritten. - - **Parameters:** - - ctype - The content type which gets the workflow. Can be any Django model - instance. - """ - try: - wor = WorkflowModelRelation.objects.get(content_type=ctype) - except WorkflowModelRelation.DoesNotExist: - WorkflowModelRelation.objects.create(content_type=ctype, workflow=self) - else: - wor.workflow = self - wor.save() - - def set_to_object(self, obj): - """Sets the workflow to the passed object. - - If the object has already the given workflow nothing happens. Otherwise - the workflow is set to the objectthe state is set to the workflow's - initial state. - - **Parameters:** - - obj - The object which gets the workflow. - """ - import workflows.utils - - ctype = ContentType.objects.get_for_model(obj) - try: - wor = WorkflowObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - except WorkflowObjectRelation.DoesNotExist: - WorkflowObjectRelation.objects.create(content = obj, workflow=self) - workflows.utils.set_state(obj, self.initial_state) - else: - if wor.workflow != self: - wor.workflow = self - wor.save() - workflows.utils.set_state(self.initial_state) - -class State(models.Model): - """A certain state within workflow. - - **Attributes:** - - name - The unique name of the state within the workflow. - - workflow - The workflow to which the state belongs. - - transitions - The transitions of a workflow state. - - """ - name = models.CharField(_(u"Name"), max_length=100) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="states") - transitions = models.ManyToManyField("Transition", verbose_name=_(u"Transitions"), blank=True, null=True, related_name="states") - - class Meta: - ordering = ("name", ) - - def __unicode__(self): - return "%s (%s)" % (self.name, self.workflow.name) - - def get_allowed_transitions(self, obj, user): - """Returns all allowed transitions for passed object and user. - """ - transitions = [] - for transition in self.transitions.all(): - permission = transition.permission - if permission is None: - transitions.append(transition) - else: - # First we try to get the objects specific has_permission - # method (in case the object inherits from the PermissionBase - # class). - try: - if obj.has_permission(user, permission.codename): - transitions.append(transition) - except AttributeError: - if permissions.utils.has_permission(obj, user, permission.codename): - transitions.append(transition) - return transitions - -class Transition(models.Model): - """A transition from a source to a destination state. The transition can - be used from several source states. - - **Attributes:** - - name - The unique name of the transition within a workflow. - - workflow - The workflow to which the transition belongs. Must be a Workflow - instance. - - destination - The state after a transition has been processed. Must be a State - instance. - - condition - The condition when the transition is available. Can be any python - expression. - - permission - The necessary permission to process the transition. Must be a - Permission instance. - - """ - name = models.CharField(_(u"Name"), max_length=100) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="transitions") - destination = models.ForeignKey(State, verbose_name=_(u"Destination"), null=True, blank=True, related_name="destination_state") - condition = models.CharField(_(u"Condition"), blank=True, max_length=100) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission"), blank=True, null=True) - - def __unicode__(self): - return self.name - -class StateObjectRelation(models.Model): - """Stores the workflow state of an object. - - Provides a way to give any object a workflow state without changing the - object's model. - - **Attributes:** - - content - The object for which the state is stored. This can be any instance of - a Django model. - - state - The state of content. This must be a State instance. - """ - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="state_object", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - state = models.ForeignKey(State, verbose_name = _(u"State")) - - def __unicode__(self): - return "%s %s - %s" % (self.content_type.name, self.content_id, self.state.name) - - class Meta: - unique_together = ("content_type", "content_id", "state") - -class WorkflowObjectRelation(models.Model): - """Stores an workflow of an object. - - Provides a way to give any object a workflow without changing the object's - model. - - **Attributes:** - - content - The object for which the workflow is stored. This can be any instance of - a Django model. - - workflow - The workflow which is assigned to an object. This needs to be a workflow - instance. - """ - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content type"), related_name="workflow_object", blank=True, null=True) - content_id = models.PositiveIntegerField(_(u"Content id"), blank=True, null=True) - content = generic.GenericForeignKey(ct_field="content_type", fk_field="content_id") - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="wors") - - class Meta: - unique_together = ("content_type", "content_id") - - def __unicode__(self): - return "%s %s - %s" % (self.content_type.name, self.content_id, self.workflow.name) - -class WorkflowModelRelation(models.Model): - """Stores an workflow for a model (ContentType). - - Provides a way to give any object a workflow without changing the model. - - **Attributes:** - - Content Type - The content type for which the workflow is stored. This can be any - instance of a Django model. - - workflow - The workflow which is assigned to an object. This needs to be a - workflow instance. - """ - content_type = models.ForeignKey(ContentType, verbose_name=_(u"Content Type"), unique=True) - workflow = models.ForeignKey(Workflow, verbose_name=_(u"Workflow"), related_name="wmrs") - - def __unicode__(self): - return "%s - %s" % (self.content_type.name, self.workflow.name) - -# Permissions relation ####################################################### - -class WorkflowPermissionRelation(models.Model): - """Stores the permissions for which a workflow is responsible. - - **Attributes:** - - workflow - The workflow which is responsible for the permissions. Needs to be a - Workflow instance. - - permission - The permission for which the workflow is responsible. Needs to be a - Permission instance. - """ - workflow = models.ForeignKey(Workflow) - permission = models.ForeignKey(Permission, related_name="permissions") - - class Meta: - unique_together = ("workflow", "permission") - - def __unicode__(self): - return "%s %s" % (self.workflow.name, self.permission.name) - -class StateInheritanceBlock(models.Model): - """Stores inheritance block for state and permission. - - **Attributes:** - - state - The state for which the inheritance is blocked. Needs to be a State - instance. - - permission - The permission for which the instance is blocked. Needs to be a - Permission instance. - """ - state = models.ForeignKey(State, verbose_name=_(u"State")) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - - def __unicode__(self): - return "%s %s" % (self.state.name, self.permission.name) - -class StatePermissionRelation(models.Model): - """Stores granted permission for state and role. - - **Attributes:** - - state - The state for which the role has the permission. Needs to be a State - instance. - - permission - The permission for which the workflow is responsible. Needs to be a - Permission instance. - - role - The role for which the state has the permission. Needs to be a lfc - Role instance. - """ - state = models.ForeignKey(State, verbose_name=_(u"State")) - permission = models.ForeignKey(Permission, verbose_name=_(u"Permission")) - role = models.ForeignKey(Role, verbose_name=_(u"Role")) - - def __unicode__(self): - return "%s %s %s" % (self.state.name, self.role.name, self.permission.name) diff --git a/workflows/templates/workflows/transitions.html b/workflows/templates/workflows/transitions.html deleted file mode 100644 index 635cbba02..000000000 --- a/workflows/templates/workflows/transitions.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load i18n %} - - diff --git a/workflows/templatetags/.gitignore b/workflows/templatetags/.gitignore deleted file mode 100644 index a74b07aee..000000000 --- a/workflows/templatetags/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.pyc diff --git a/workflows/templatetags/__init__.py b/workflows/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/workflows/templatetags/workflows_tags.py b/workflows/templatetags/workflows_tags.py deleted file mode 100644 index e95357a32..000000000 --- a/workflows/templatetags/workflows_tags.py +++ /dev/null @@ -1,18 +0,0 @@ -# django imports -from django import template - -# workflows imports -import workflows.utils - -register = template.Library() - -@register.inclusion_tag('workflows/transitions.html', takes_context=True) -def transitions(context, obj): - """ - """ - request = context.get("request") - - return { - "transitions" : workflows.utils.get_allowed_transitions(obj, request.user), - "state" : workflows.utils.get_state(obj), - } diff --git a/workflows/tests.py b/workflows/tests.py deleted file mode 100644 index 2fda0ad4b..000000000 --- a/workflows/tests.py +++ /dev/null @@ -1,600 +0,0 @@ -# django imports -from django.contrib.contenttypes.models import ContentType -from django.contrib.flatpages.models import FlatPage -from django.test import TestCase -from django.contrib.auth.models import User -from django.contrib.sessions.backends.file import SessionStore -from django.core.handlers.wsgi import WSGIRequest -from django.test.client import Client - -# workflows import -import permissions.utils -import workflows.utils -from workflows.models import State -from workflows.models import StateInheritanceBlock -from workflows.models import StatePermissionRelation -from workflows.models import StateObjectRelation -from workflows.models import Transition -from workflows.models import Workflow -from workflows.models import WorkflowModelRelation -from workflows.models import WorkflowObjectRelation -from workflows.models import WorkflowPermissionRelation - -class WorkflowTestCase(TestCase): - """Tests a simple workflow without permissions. - """ - def setUp(self): - """ - """ - create_workflow(self) - - def test_get_states(self): - """ - """ - states = self.w.states.all() - self.assertEqual(states[0], self.private) - self.assertEqual(states[1], self.public) - - def test_unicode(self): - """ - """ - self.assertEqual(self.w.__unicode__(), u"Standard") - -class PermissionsTestCase(TestCase): - """Tests a simple workflow with permissions. - """ - def setUp(self): - """ - """ - create_workflow(self) - - # Register roles - self.anonymous = permissions.utils.register_role("Anonymous") - self.owner = permissions.utils.register_role("Owner") - - self.user = User.objects.create(username="john") - permissions.utils.add_role(self.user, self.owner) - - # Example content type - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - - # Registers permissions - self.view = permissions.utils.register_permission("View", "view") - self.edit = permissions.utils.register_permission("Edit", "edit") - - # Add all permissions which are managed by the workflow - wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.view) - wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.edit) - - # Add permissions for single states - spr = StatePermissionRelation.objects.create(state=self.public, permission=self.view, role=self.owner) - spr = StatePermissionRelation.objects.create(state=self.private, permission=self.view, role=self.owner) - spr = StatePermissionRelation.objects.create(state=self.private, permission=self.edit, role=self.owner) - - # Add inheritance block for single states - sib = StateInheritanceBlock.objects.create(state=self.private, permission=self.view) - sib = StateInheritanceBlock.objects.create(state=self.private, permission=self.edit) - sib = StateInheritanceBlock.objects.create(state=self.public, permission=self.edit) - - workflows.utils.set_workflow(self.page_1, self.w) - - def test_set_state(self): - """ - """ - # Permissions - result = permissions.utils.has_permission(self.page_1, self.user, "edit") - self.assertEqual(result, True) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - # Inheritance - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, False) - - result = permissions.utils.is_inherited(self.page_1, "edit") - self.assertEqual(result, False) - - # Change state - workflows.utils.set_state(self.page_1, self.public) - - # Permissions - result = permissions.utils.has_permission(self.page_1, self.user, "edit") - self.assertEqual(result, False) - - result = permissions.utils.has_permission(self.page_1, self.user, "view") - self.assertEqual(result, True) - - # Inheritance - result = permissions.utils.is_inherited(self.page_1, "view") - self.assertEqual(result, True) - - result = permissions.utils.is_inherited(self.page_1, "edit") - self.assertEqual(result, False) - - def test_set_initial_state(self): - """ - """ - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - workflows.utils.do_transition(self.page_1, self.make_public, self.user) - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.public.name) - - workflows.utils.set_initial_state(self.page_1) - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - def test_do_transition(self): - """ - """ - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - # by transition - workflows.utils.do_transition(self.page_1, self.make_public, self.user) - - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.public.name) - - # by name - workflows.utils.do_transition(self.page_1, "Make private", self.user) - - state = workflows.utils.get_state(self.page_1) - self.assertEqual(state.name, self.private.name) - - # name which does not exist - result = workflows.utils.do_transition(self.page_1, "Make pending", self.user) - self.assertEqual(result, False) - - wrong = Transition.objects.create(name="Wrong", workflow=self.w, destination = self.public) - - # name which does not exist - result = workflows.utils.do_transition(self.page_1, wrong, self.user) - self.assertEqual(result, False) - -class UtilsTestCase(TestCase): - """Tests various methods of the utils module. - """ - def setUp(self): - """ - """ - create_workflow(self) - self.user = User.objects.create() - - def test_workflow(self): - """ - """ - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - def test_state(self): - """ - """ - result = workflows.utils.get_state(self.user) - self.assertEqual(result, None) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_state(self.user) - self.assertEqual(result, self.w.initial_state) - - def test_set_workflow_1(self): - """Set worklow by object - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - wp = Workflow.objects.create(name="Portal") - - # Set for model - workflows.utils.set_workflow_for_model(ctype, wp) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, wp) - - # Set for object - workflows.utils.set_workflow_for_object(self.user, self.w) - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - # The model still have wp - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - def test_set_workflow_2(self): - """Set worklow by name - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - wp = Workflow.objects.create(name="Portal") - - # Set for model - workflows.utils.set_workflow_for_model(ctype, "Portal") - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, wp) - - # Set for object - workflows.utils.set_workflow_for_object(self.user, "Standard") - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - # The model still have wp - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, wp) - - # Workflow which does not exist - result = workflows.utils.set_workflow_for_model(ctype, "Wrong") - self.assertEqual(result, False) - - result = workflows.utils.set_workflow_for_object(self.user, "Wrong") - self.assertEqual(result, False) - - def test_get_objects_for_workflow_1(self): - """Workflow is added to object. - """ - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, []) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - def test_get_objects_for_workflow_2(self): - """Workflow is added to content type. - """ - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, []) - - ctype = ContentType.objects.get_for_model(self.user) - workflows.utils.set_workflow(ctype, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - def test_get_objects_for_workflow_3(self): - """Workflow is added to content type and object. - """ - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, []) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - ctype = ContentType.objects.get_for_model(self.user) - workflows.utils.set_workflow(ctype, self.w) - result = workflows.utils.get_objects_for_workflow(self.w) - self.assertEqual(result, [self.user]) - - def test_get_objects_for_workflow_4(self): - """Get workflow by name - """ - result = workflows.utils.get_objects_for_workflow("Standard") - self.assertEqual(result, []) - - workflows.utils.set_workflow(self.user, self.w) - result = workflows.utils.get_objects_for_workflow("Standard") - self.assertEqual(result, [self.user]) - - # Workflow which does not exist - result = workflows.utils.get_objects_for_workflow("Wrong") - self.assertEqual(result, []) - - def test_remove_workflow_from_model(self): - """ - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(ctype) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_model(ctype, self.w) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - workflows.utils.remove_workflow_from_model(ctype) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, None) - - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, None) - - def test_remove_workflow_from_object(self): - """ - """ - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_object(self.user, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - result = workflows.utils.remove_workflow_from_object(self.user) - self.assertEqual(result, None) - - def test_remove_workflow_1(self): - """Removes workflow from model - """ - ctype = ContentType.objects.get_for_model(self.user) - - result = workflows.utils.get_workflow(ctype) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_model(ctype, self.w) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - workflows.utils.remove_workflow(ctype) - - result = workflows.utils.get_workflow_for_model(ctype) - self.assertEqual(result, None) - - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, None) - - def test_remove_workflow_2(self): - """Removes workflow from object - """ - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - workflows.utils.set_workflow_for_object(self.user, self.w) - - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, self.w) - - result = workflows.utils.remove_workflow(self.user) - self.assertEqual(result, None) - - def test_get_allowed_transitions(self): - """Tests get_allowed_transitions method - """ - page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - role_1 = permissions.utils.register_role("Role 1") - permissions.utils.add_role(self.user, role_1) - - view = permissions.utils.register_permission("Publish", "publish") - - transitions = self.private.get_allowed_transitions(page_1, self.user) - self.assertEqual(len(transitions), 1) - - # protect the transition with a permission - self.make_public.permission = view - self.make_public.save() - - # user has no transition - transitions = self.private.get_allowed_transitions(page_1, self.user) - self.assertEqual(len(transitions), 0) - - # grant permission - permissions.utils.grant_permission(page_1, role_1, view) - - # user has transition again - transitions = self.private.get_allowed_transitions(page_1, self.user) - self.assertEqual(len(transitions), 1) - - def test_get_workflow_for_object(self): - """ - """ - result = workflows.utils.get_workflow(self.user) - self.assertEqual(result, None) - - # Set workflow for a user - workflows.utils.set_workflow_for_object(self.user, self.w) - - # Get workflow for the user - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, self.w) - - # Set workflow for a FlatPage - page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - workflows.utils.set_workflow_for_object(page_1, self.w) - - result = workflows.utils.get_workflow_for_object(self.user) - self.assertEqual(result, self.w) - - result = workflows.utils.get_workflow_for_object(page_1) - self.assertEqual(result, self.w) - -class StateTestCase(TestCase): - """Tests the State model - """ - def setUp(self): - """ - """ - create_workflow(self) - self.user = User.objects.create() - self.role_1 = permissions.utils.register_role("Role 1") - permissions.utils.add_role(self.user, self.role_1) - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - - def test_unicode(self): - """ - """ - self.assertEqual(self.private.__unicode__(), u"Private (Standard)") - - def test_transitions(self): - """ - """ - transitions = self.public.transitions.all() - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_private) - - transitions = self.private.transitions.all() - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_public) - - def test_get_transitions(self): - """ - """ - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_public) - - transitions = self.public.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - self.assertEqual(transitions[0], self.make_private) - - def test_get_allowed_transitions(self): - """ - """ - self.view = permissions.utils.register_permission("Publish", "publish") - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - - # protect the transition with a permission - self.make_public.permission = self.view - self.make_public.save() - - # user has no transition - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 0) - - # grant permission - permissions.utils.grant_permission(self.page_1, self.role_1, self.view) - - # user has transition again - transitions = self.private.get_allowed_transitions(self.page_1, self.user) - self.assertEqual(len(transitions), 1) - -class TransitionTestCase(TestCase): - """Tests the Transition model - """ - def setUp(self): - """ - """ - create_workflow(self) - - def test_unicode(self): - """ - """ - self.assertEqual(self.make_private.__unicode__(), u"Make private") - -class RelationsTestCase(TestCase): - """Tests various Relations models. - """ - def setUp(self): - """ - """ - create_workflow(self) - self.page_1 = FlatPage.objects.create(url="/page-1/", title="Page 1") - - def test_unicode(self): - """ - """ - # WorkflowObjectRelation - workflows.utils.set_workflow(self.page_1, self.w) - wor = WorkflowObjectRelation.objects.filter()[0] - self.assertEqual(wor.__unicode__(), "flat page 1 - Standard") - - # StateObjectRelation - workflows.utils.set_state(self.page_1, self.public) - sor = StateObjectRelation.objects.filter()[0] - self.assertEqual(sor.__unicode__(), "flat page 1 - Public") - - # WorkflowModelRelation - ctype = ContentType.objects.get_for_model(self.page_1) - workflows.utils.set_workflow(ctype, self.w) - wmr = WorkflowModelRelation.objects.filter()[0] - self.assertEqual(wmr.__unicode__(), "flat page - Standard") - - # WorkflowPermissionRelation - self.view = permissions.utils.register_permission("View", "view") - wpr = WorkflowPermissionRelation.objects.create(workflow=self.w, permission=self.view) - self.assertEqual(wpr.__unicode__(), "Standard View") - - # StatePermissionRelation - self.owner = permissions.utils.register_role("Owner") - spr = StatePermissionRelation.objects.create(state=self.public, permission=self.view, role=self.owner) - self.assertEqual(spr.__unicode__(), "Public Owner View") - -# Helpers #################################################################### - -def create_workflow(self): - self.w = Workflow.objects.create(name="Standard") - - self.private = State.objects.create(name="Private", workflow= self.w) - self.public = State.objects.create(name="Public", workflow= self.w) - - self.make_public = Transition.objects.create(name="Make public", workflow=self.w, destination = self.public) - self.make_private = Transition.objects.create(name="Make private", workflow=self.w, destination = self.private) - - self.private.transitions.add(self.make_public) - self.public.transitions.add(self.make_private) - - self.w.initial_state = self.private - self.w.save() - -# Taken from "http://www.djangosnippets.org/snippets/963/" -class RequestFactory(Client): - """ - Class that lets you create mock Request objects for use in testing. - - Usage: - - rf = RequestFactory() - get_request = rf.get('/hello/') - post_request = rf.post('/submit/', {'foo': 'bar'}) - - This class re-uses the django.test.client.Client interface, docs here: - http://www.djangoproject.com/documentation/testing/#the-test-client - - Once you have a request object you can pass it to any view function, - just as if that view had been hooked up using a URLconf. - - """ - def request(self, **request): - """ - Similar to parent class, but returns the request object as soon as it - has created it. - """ - environ = { - 'HTTP_COOKIE': self.cookies, - 'PATH_INFO': '/', - 'QUERY_STRING': '', - 'REQUEST_METHOD': 'GET', - 'SCRIPT_NAME': '', - 'SERVER_NAME': 'testserver', - 'SERVER_PORT': 80, - 'SERVER_PROTOCOL': 'HTTP/1.1', - } - environ.update(self.defaults) - environ.update(request) - return WSGIRequest(environ) - -def create_request(): - """ - """ - rf = RequestFactory() - request = rf.get('/') - request.session = SessionStore() - - user = User() - user.is_superuser = True - user.save() - request.user = user - - return request diff --git a/workflows/urls.py b/workflows/urls.py deleted file mode 100644 index 949c2346c..000000000 --- a/workflows/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.conf.urls.defaults import * - -# URL patterns for django-workflows - -urlpatterns = patterns('django-workflows.views', - # Add url patterns here -) diff --git a/workflows/utils.py b/workflows/utils.py deleted file mode 100644 index ef2d32596..000000000 --- a/workflows/utils.py +++ /dev/null @@ -1,351 +0,0 @@ -# django imports -from django.contrib.contenttypes.models import ContentType - -# workflows imports -from permissions.models import ObjectPermission -from permissions.models import ObjectPermissionInheritanceBlock -from workflows.models import StateInheritanceBlock -from workflows.models import StateObjectRelation -from workflows.models import StatePermissionRelation -from workflows.models import Transition -from workflows.models import Workflow -from workflows.models import WorkflowModelRelation -from workflows.models import WorkflowObjectRelation -from workflows.models import WorkflowPermissionRelation - -# permissions imports -import permissions.utils - -def get_objects_for_workflow(workflow): - """Returns all objects which have passed workflow. - - **Parameters:** - - workflow - The workflow for which the objects are returned. Can be a Workflow - instance or a string with the workflow name. - """ - if not isinstance(workflow, Workflow): - try: - workflow = Workflow.objects.get(name=workflow) - except Workflow.DoesNotExist: - return [] - - return workflow.get_objects() - -def remove_workflow(ctype_or_obj): - """Removes the workflow from the passed content type or object. After this - function has been called the content type or object has no workflow - anymore. - - If ctype_or_obj is an object the workflow is removed from the object not - from the belonging content type. - - If ctype_or_obj is an content type the workflow is removed from the - content type not from instances of the content type (if they have an own - workflow) - - ctype_or_obj - The content type or the object to which the passed workflow should be - set. Can be either a ContentType instance or any LFC Django model - instance. - """ - if isinstance(ctype_or_obj, ContentType): - remove_workflow_from_model(ctype_or_obj) - else: - remove_workflow_from_object(ctype_or_obj) - -def remove_workflow_from_model(ctype): - """Removes the workflow from passed content type. After this function has - been called the content type has no workflow anymore (the instances might - have own ones). - - ctype - The content type from which the passed workflow should be removed. - Must be a ContentType instance. - """ - # First delete all states, inheritance blocks and permissions from ctype's - # instances which have passed workflow. - workflow = get_workflow_for_model(ctype) - for obj in get_objects_for_workflow(workflow): - try: - ctype = ContentType.objects.get_for_model(obj) - sor = StateObjectRelation.objects.get(content_id=obj.pk, content_type=ctype) - except StateObjectRelation.DoesNotExist: - pass - else: - sor.delete() - - # Reset all permissions - permissions.utils.reset(obj) - - try: - wmr = WorkflowModelRelation.objects.get(content_type=ctype) - except WorkflowModelRelation.DoesNotExist: - pass - else: - wmr.delete() - -def remove_workflow_from_object(obj): - """Removes the workflow from the passed object. After this function has - been called the object has no *own* workflow anymore (it might have one - via its content type). - - obj - The object from which the passed workflow should be set. Must be a - Django Model instance. - """ - try: - wor = WorkflowObjectRelation.objects.get(content_type=obj) - except WorkflowObjectRelation.DoesNotExist: - pass - else: - wor.delete() - - # Reset all permissions - permissions.utils.reset(obj) - - # Set initial of object's content types workflow (if there is one) - set_initial_state(obj) - -def set_workflow(ctype_or_obj, workflow): - """Sets the workflow for passed content type or object. See the specific - methods for more information. - - **Parameters:** - - workflow - The workflow which should be set to the object or model. - - ctype_or_obj - The content type or the object to which the passed workflow should be - set. Can be either a ContentType instance or any Django model - instance. - """ - return workflow.set_to(ctype_or_obj) - -def set_workflow_for_object(obj, workflow): - """Sets the passed workflow to the passed object. - - If the object has already the given workflow nothing happens. Otherwise - the object gets the passed workflow and the state is set to the workflow's - initial state. - - **Parameters:** - - workflow - The workflow which should be set to the object. Can be a Workflow - instance or a string with the workflow name. - - obj - The object which gets the passed workflow. - """ - if isinstance(workflow, Workflow) == False: - try: - workflow = Workflow.objects.get(name=workflow) - except Workflow.DoesNotExist: - return False - - workflow.set_to_object(obj) - -def set_workflow_for_model(ctype, workflow): - """Sets the passed workflow to the passed content type. If the content - type has already an assigned workflow the workflow is overwritten. - - The objects which had the old workflow must updated explicitely. - - **Parameters:** - - workflow - The workflow which should be set to passend content type. Must be a - Workflow instance. - - ctype - The content type to which the passed workflow should be assigned. Can - be any Django model instance - """ - if isinstance(workflow, Workflow) == False: - try: - workflow = Workflow.objects.get(name=workflow) - except Workflow.DoesNotExist: - return False - - workflow.set_to_model(ctype) - -def get_workflow(obj): - """Returns the workflow for the passed object. It takes it either from - the passed object or - if the object doesn't have a workflow - from the - passed object's ContentType. - - **Parameters:** - - object - The object for which the workflow should be returend. Can be any - Django model instance. - """ - workflow = get_workflow_for_object(obj) - if workflow is not None: - return workflow - - ctype = ContentType.objects.get_for_model(obj) - return get_workflow_for_model(ctype) - -def get_workflow_for_object(obj): - """Returns the workflow for the passed object. - - **Parameters:** - - obj - The object for which the workflow should be returned. Can be any - Django model instance. - """ - try: - ctype = ContentType.objects.get_for_model(obj) - wor = WorkflowObjectRelation.objects.get(content_id=obj.pk, content_type=ctype) - except WorkflowObjectRelation.DoesNotExist: - return None - else: - return wor.workflow - -def get_workflow_for_model(ctype): - """Returns the workflow for the passed model. - - **Parameters:** - - ctype - The content type for which the workflow should be returned. Must be - a Django ContentType instance. - """ - try: - wor = WorkflowModelRelation.objects.get(content_type=ctype) - except WorkflowModelRelation.DoesNotExist: - return None - else: - return wor.workflow - -def get_state(obj): - """Returns the current workflow state for the passed object. - - **Parameters:** - - obj - The object for which the workflow state should be returned. Can be any - Django model instance. - """ - ctype = ContentType.objects.get_for_model(obj) - try: - sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - except StateObjectRelation.DoesNotExist: - return None - else: - return sor.state - -def set_state(obj, state): - """Sets the state for the passed object to the passed state and updates - the permissions for the object. - - **Parameters:** - - obj - The object for which the workflow state should be set. Can be any - Django model instance. - - state - The state which should be set to the passed object. - """ - if not state: - remove_state(obj) - else: - ctype = ContentType.objects.get_for_model(obj) - try: - sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - except StateObjectRelation.DoesNotExist: - sor = StateObjectRelation.objects.create(content=obj, state=state) - else: - sor.state = state - sor.save() - update_permissions(obj) - -def remove_state(obj): - """Removes the current state for the passed object. - - **Parameters:** - - obj - The object for which the workflow state should be set. Can be any - Django model instance. - - """ - ctype = ContentType.objects.get_for_model(obj) - try: - sor = StateObjectRelation.objects.get(content_type=ctype, content_id=obj.pk) - sor.delete() - except StateObjectRelation.DoesNotExist: - pass - update_permissions(obj) - -def set_initial_state(obj): - """Sets the initial state to the passed object. - """ - wf = get_workflow(obj) - if wf is not None: - set_state(obj, wf.get_initial_state()) - -def get_allowed_transitions(obj, user): - """Returns all allowed transitions for passed object and user. Takes the - current state of the object into account. - - **Parameters:** - - obj - The object for which the transitions should be returned. - - user - The user for which the transitions are allowed. - """ - state = get_state(obj) - if state is None: - return [] - - return state.get_allowed_transitions(obj, user) - -def do_transition(obj, transition, user): - """Processes the passed transition to the passed object (if allowed). - """ - if not isinstance(transition, Transition): - try: - transition = Transition.objects.get(name=transition) - except Transition.DoesNotExist: - return False - - transitions = get_allowed_transitions(obj, user) - if transition in transitions: - set_state(obj, transition.destination) - return True - else: - return False - -def update_permissions(obj): - """Updates the permissions of the passed object according to the object's - current workflow state. - """ - workflow = get_workflow(obj) - state = get_state(obj) - - # Remove all permissions for the workflow - ct = ContentType.objects.get_for_model(obj) - ps = [wpr.permission for wpr in WorkflowPermissionRelation.objects.filter(workflow=workflow)] - - ObjectPermission.objects.filter(content_type = ct, content_id=obj.pk, permission__in=ps).delete() - - # Grant permission for the state - for spr in StatePermissionRelation.objects.filter(state=state): - permissions.utils.grant_permission(obj, spr.role, spr.permission) - - # Remove all inheritance blocks from the object - ObjectPermissionInheritanceBlock.objects.filter( - content_type = ct, content_id=obj.pk, permission__in=ps).delete() - - # Add inheritance blocks of this state to the object - for sib in StateInheritanceBlock.objects.filter(state=state): - permissions.utils.add_inheritance_block(obj, sib.permission) diff --git a/workflows/views.py b/workflows/views.py deleted file mode 100644 index e69de29bb..000000000