From d0214d3b259270dfc5112f332ee5e8d94475afc2 Mon Sep 17 00:00:00 2001 From: Henrik Levkowetz Date: Sun, 21 Oct 2012 14:40:02 +0000 Subject: [PATCH] Merged [4806] from rjsparks@nostrum.com: Better handling for BoFs, particularly those that won't create WGs - Legacy-Id: 4939 --- ietf/group/proxy.py | 2 +- .../migrations/0004_add_wg_state_abandon.py | 157 ++++++++++++++++++ ietf/name/models.py | 2 +- ietf/templates/base_leftmenu.html | 1 + ietf/templates/wginfo/bofs.html | 40 +++++ ietf/templates/wginfo/edit.html | 23 ++- ietf/utils/test_runner.py | 2 + ietf/wgcharter/views.py | 3 +- ietf/wginfo/edit.py | 29 ++-- ietf/wginfo/urls.py | 4 +- ietf/wginfo/views.py | 4 + 11 files changed, 244 insertions(+), 23 deletions(-) create mode 100644 ietf/name/migrations/0004_add_wg_state_abandon.py create mode 100644 ietf/templates/wginfo/bofs.html diff --git a/ietf/group/proxy.py b/ietf/group/proxy.py index 5b03fa570..79d2fbfec 100644 --- a/ietf/group/proxy.py +++ b/ietf/group/proxy.py @@ -131,7 +131,7 @@ class IETFWG(Group): #status = models.ForeignKey(WGStatus) @property def status_id(self): - return { "active": 1, "dormant": 2, "conclude": 3, "proposed": 4, "bof": 4, }[self.state_id] + return { "active": 1, "dormant": 2, "conclude": 3, "proposed": 4, "bof": 4, "abandon": 4 }[self.state_id] #area_director = models.ForeignKey(AreaDirector, null=True) #meeting_scheduled = models.CharField(blank=True, max_length=3) @property diff --git a/ietf/name/migrations/0004_add_wg_state_abandon.py b/ietf/name/migrations/0004_add_wg_state_abandon.py new file mode 100644 index 000000000..bdc29876b --- /dev/null +++ b/ietf/name/migrations/0004_add_wg_state_abandon.py @@ -0,0 +1,157 @@ +# 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): + GroupStateName(slug='abandon',name='Abandonded',desc='Formation of the group (most likely a BoF or Proposed WG) was abandoned',used=True).save() + + + def backwards(self, orm): + pass + + + models = { + 'name.ballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'BallotPositionName'}, + 'blocking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.constraintname': { + 'Meta': {'ordering': "['order']", 'object_name': 'ConstraintName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docrelationshipname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocRelationshipName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.docremindertypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocReminderTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctagname': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTagName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.doctypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'DocTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupballotpositionname': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupBallotPositionName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.groupstatename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupStateName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.grouptypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'GroupTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.intendedstdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'IntendedStdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.liaisonstatementpurposename': { + 'Meta': {'ordering': "['order']", 'object_name': 'LiaisonStatementPurposeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.meetingtypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'MeetingTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.rolename': { + 'Meta': {'ordering': "['order']", 'object_name': 'RoleName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.sessionstatusname': { + 'Meta': {'ordering': "['order']", 'object_name': 'SessionStatusName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.stdlevelname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StdLevelName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.streamname': { + 'Meta': {'ordering': "['order']", 'object_name': 'StreamName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'name.timeslottypename': { + 'Meta': {'ordering': "['order']", 'object_name': 'TimeSlotTypeName'}, + 'desc': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'slug': ('django.db.models.fields.CharField', [], {'max_length': '8', 'primary_key': 'True'}), + 'used': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + } + } + + complete_apps = ['name'] diff --git a/ietf/name/models.py b/ietf/name/models.py index 672b57004..9568ed078 100644 --- a/ietf/name/models.py +++ b/ietf/name/models.py @@ -17,7 +17,7 @@ class NameModel(models.Model): ordering = ['order'] class GroupStateName(NameModel): - """BOF, Proposed, Active, Dormant, Concluded""" + """BOF, Proposed, Active, Dormant, Concluded, Abandoned""" class GroupTypeName(NameModel): """IETF, Area, WG, RG, Team, etc.""" class RoleName(NameModel): diff --git a/ietf/templates/base_leftmenu.html b/ietf/templates/base_leftmenu.html index f1d7919e6..b0ef3b80e 100644 --- a/ietf/templates/base_leftmenu.html +++ b/ietf/templates/base_leftmenu.html @@ -74,6 +74,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  • Active WGs
  • Chartering WGs
  • +
  • BoFs
  • Concluded WGs
  • Non-WG Lists
  • diff --git a/ietf/templates/wginfo/bofs.html b/ietf/templates/wginfo/bofs.html new file mode 100644 index 000000000..bfd4f5baf --- /dev/null +++ b/ietf/templates/wginfo/bofs.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} + +{% block title %}BoFs{% endblock %} + +{% block content %} +{% load ietf_filters %} +{% load ballot_icon %} + +

    Bofs

    + +

    Groups in the BoF state

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

    Create a new BoF

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

    No groups found.

    +{% else %} + + + + + + +{% for g in groups %} + + + + + +{% endfor %} +
    GroupNameDate
    + {{ g.acronym }} + + {{ g.name }} + {{ g.time|date:"Y-m-d" }}
    +{% endif %} + +{% endblock %} diff --git a/ietf/templates/wginfo/edit.html b/ietf/templates/wginfo/edit.html index 5a740b3de..683467eb9 100644 --- a/ietf/templates/wginfo/edit.html +++ b/ietf/templates/wginfo/edit.html @@ -26,11 +26,16 @@ ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; } {% block content %} {% load ietf_filters %} -

    {% if wg %} +

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

    @@ -60,12 +65,16 @@ Start chartering new WG - {% if wg %} + {% ifequal action "edit" %} Back - {% else %} - - {% endif %} + {% else %} + {% ifequal action "charter" %} + + {% else %} + + {% endifequal %} + {% endifequal %} diff --git a/ietf/utils/test_runner.py b/ietf/utils/test_runner.py index 195f68cc4..4374a4619 100644 --- a/ietf/utils/test_runner.py +++ b/ietf/utils/test_runner.py @@ -38,6 +38,8 @@ from django.conf import settings from django.template import TemplateDoesNotExist from django.test.simple import run_tests as django_run_tests +import debug + import ietf.utils.mail loaded_templates = set() diff --git a/ietf/wgcharter/views.py b/ietf/wgcharter/views.py index 753fc4e7b..339689099 100644 --- a/ietf/wgcharter/views.py +++ b/ietf/wgcharter/views.py @@ -75,6 +75,7 @@ def change_state(request, name, option=None): elif option == "abandon": if wg.state_id in ("proposed","bof","unknown"): charter_state = State.objects.get(type="charter", slug="notrev") + #TODO : set an abandoned state and leave some comments here else: charter_state = State.objects.get(type="charter", slug="approved") charter_rev = approved_revision(charter.rev) @@ -160,7 +161,7 @@ def change_state(request, name, option=None): "abandon": "Abandon effort on WG %s" % wg.acronym, }.get(option) if not title: - title = "Change state of WG %s" % wg.acronym + title = "Change chartering state of WG %s" % wg.acronym def state_pk(slug): return State.objects.get(type="charter", slug=slug).pk diff --git a/ietf/wginfo/edit.py b/ietf/wginfo/edit.py index 899e834db..f80bdf3d2 100644 --- a/ietf/wginfo/edit.py +++ b/ietf/wginfo/edit.py @@ -9,6 +9,8 @@ from django import forms from django.utils import simplejson from django.utils.html import mark_safe +import debug + from ietf.ietfauth.decorators import role_required, has_role from ietf.doc.models import * @@ -23,6 +25,7 @@ from ietf.person.forms import EmailsField 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) @@ -109,7 +112,7 @@ def edit(request, acronym=None, action="edit"): if action == "edit": wg = get_object_or_404(Group, acronym=acronym) new_wg = False - elif action == "create": + elif action in ("create","charter"): wg = None new_wg = True else: @@ -122,30 +125,29 @@ def edit(request, acronym=None, action="edit"): if form.is_valid(): clean = form.cleaned_data if new_wg: - # get ourselves a proposed WG try: wg = Group.objects.get(acronym=clean["acronym"]) - save_group_in_history(wg) - wg.state = GroupStateName.objects.get(slug="proposed") wg.time = datetime.datetime.now() wg.save() except Group.DoesNotExist: wg = Group.objects.create(name=clean["name"], acronym=clean["acronym"], type=GroupTypeName.objects.get(slug="wg"), - state=GroupStateName.objects.get(slug="proposed")) + state=clean["state"] + ) e = ChangeStateGroupEvent(group=wg, type="changed_state") e.time = wg.time e.by = login - e.state_id = "proposed" - e.desc = "Proposed group" + e.state_id = clean["state"].slug + e.desc = clean["state"].name e.save() else: save_group_in_history(wg) - if not wg.charter: # make sure we have a charter + + if action=="charter" and not wg.charter: # make sure we have a charter try: charter = Document.objects.get(docalias__name="charter-ietf-%s" % wg.acronym) except Document.DoesNotExist: @@ -159,8 +161,8 @@ def edit(request, acronym=None, action="edit"): ) charter.save() charter.set_state(State.objects.get(type="charter", slug="notrev")) - - # Create an alias as well + + # Create an alias as well DocAlias.objects.create( name=charter.name, document=charter @@ -188,13 +190,14 @@ def edit(request, acronym=None, action="edit"): # update the attributes, keeping track of what we're doing diff('name', "Name") diff('acronym', "Acronym") + diff('state', "State") diff('ad', "Shepherding AD") diff('parent', "IETF Area") diff('list_email', "Mailing list email") diff('list_subscribe', "Mailing list subscribe address") diff('list_archive', "Mailing list archive") - if not new_wg and wg.acronym != prev_acronym: + if not new_wg and wg.acronym != prev_acronym and wg.charter: save_document_in_history(wg.charter) DocAlias.objects.get_or_create( name="charter-ietf-%s" % wg.acronym, @@ -241,7 +244,7 @@ def edit(request, acronym=None, action="edit"): wg.save() - if new_wg: + if action=="charter": return redirect('charter_submit', name=wg.charter.name, option="initcharter") return redirect('wg_charter', acronym=wg.acronym) @@ -250,6 +253,7 @@ def edit(request, acronym=None, action="edit"): from ietf.person.forms import json_emails init = dict(name=wg.name, acronym=wg.acronym, + state=wg.state, 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"), @@ -268,6 +272,7 @@ def edit(request, acronym=None, action="edit"): return render_to_response('wginfo/edit.html', dict(wg=wg, form=form, + action=action, user=request.user, login=login), context_instance=RequestContext(request)) diff --git a/ietf/wginfo/urls.py b/ietf/wginfo/urls.py index 4416f4e63..2f6a56e99 100644 --- a/ietf/wginfo/urls.py +++ b/ietf/wginfo/urls.py @@ -14,8 +14,10 @@ urlpatterns = patterns('', (r'^1wg-summary-by-acronym.txt', views.wg_summary_acronym), (r'^1wg-charters.txt', views.wg_charters), (r'^1wg-charters-by-acronym.txt', views.wg_charters_by_acronym), - (r'^chartering/create/$', edit.edit, {'action': "create"}, "wg_create"), (r'^chartering/$', views.chartering_wgs), + (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'), diff --git a/ietf/wginfo/views.py b/ietf/wginfo/views.py index 277f1f952..f4eb302e9 100644 --- a/ietf/wginfo/views.py +++ b/ietf/wginfo/views.py @@ -122,6 +122,10 @@ def wg_dirREDESIGN(request): if settings.USE_DB_REDESIGN_PROXY_CLASSES: wg_dir = wg_dirREDESIGN +def bofs(request): + groups = Group.objects.filter(type="wg", state="bof") + return render_to_response('wginfo/bofs.html',dict(groups=groups),RequestContext(request)) + def chartering_wgs(request): charter_states = State.objects.filter(type="charter").exclude(slug__in=("approved", "notrev")) groups = Group.objects.filter(type="wg", charter__states__in=charter_states).select_related("state", "charter")