From 7d3bef3db41df5e5de1bdf6cd8fa84fe192dff55 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Mon, 2 May 2011 15:58:36 +0000 Subject: [PATCH] Import announcements, port NomCom views - Legacy-Id: 3077 --- ietf/announcements/urls.py | 6 +- ietf/announcements/views.py | 60 +++++++++++++ ietf/settings.py | 1 + .../announcements/message_detail.html | 18 ++++ .../announcements/nomcomREDESIGN.html | 76 ++++++++++++++++ redesign/announcements/__init__.py | 0 redesign/announcements/admin.py | 20 +++++ redesign/announcements/models.py | 37 ++++++++ redesign/doc/admin.py | 5 -- redesign/doc/models.py | 13 --- redesign/import-announcements.py | 86 +++++++++++++++++++ redesign/import-document-state.py | 13 +-- redesign/import-roles.py | 37 ++++---- redesign/importing/__init__.py | 0 redesign/importing/utils.py | 14 +++ 15 files changed, 338 insertions(+), 48 deletions(-) create mode 100644 ietf/templates/announcements/message_detail.html create mode 100644 ietf/templates/announcements/nomcomREDESIGN.html create mode 100644 redesign/announcements/__init__.py create mode 100644 redesign/announcements/admin.py create mode 100644 redesign/announcements/models.py create mode 100755 redesign/import-announcements.py create mode 100644 redesign/importing/__init__.py create mode 100644 redesign/importing/utils.py diff --git a/ietf/announcements/urls.py b/ietf/announcements/urls.py index 78842bf35..8489b5fcc 100644 --- a/ietf/announcements/urls.py +++ b/ietf/announcements/urls.py @@ -3,12 +3,14 @@ from django.conf.urls.defaults import patterns from ietf.announcements.models import Announcement +from django.conf import settings + nomcom_dict = { 'queryset': Announcement.objects.all().filter(nomcom=True) -} + } urlpatterns = patterns('', # (r'^nomcom/$', 'django.views.generic.simple.redirect_to', {'url': 'http://www.ietf.org/nomcom/index.html'} ), (r'^nomcom/$', 'ietf.announcements.views.nomcom'), - (r'^nomcom/(?P\d+)/$', 'django.views.generic.list_detail.object_detail', nomcom_dict) + (r'^nomcom/(?P\d+)/$', 'ietf.announcements.views.message_detail' if settings.USE_DB_REDESIGN_PROXY_CLASSES else 'django.views.generic.list_detail.object_detail', nomcom_dict) ) diff --git a/ietf/announcements/views.py b/ietf/announcements/views.py index abd1b0440..286b6182c 100644 --- a/ietf/announcements/views.py +++ b/ietf/announcements/views.py @@ -1,6 +1,11 @@ # Copyright The IETF Trust 2007, All Rights Reserved from django.views.generic.simple import direct_to_template +from django.shortcuts import get_object_or_404 +from django.conf import settings +from django.db.models import Q + +import re from ietf.idtracker.models import ChairsHistory from ietf.idtracker.models import Role @@ -29,3 +34,58 @@ def nomcom(request): { 'curr_chair' : curr_chair, 'regimes' : regimes }) +def nomcomREDESIGN(request): + from person.models import Email + from group.models import Group + from redesign.announcements.models import Message + + address_re = re.compile("<.*>") + + nomcoms = list(Group.objects.filter(acronym__startswith="nomcom").exclude(name="nomcom")) + + regimes = [] + + for n in nomcoms: + e = n.latest_event(type="started") + n.start_year = e.time.year if e else 0 + if n.start_year <= 2003: + continue + e = n.latest_event(type="concluded") + n.end_year = e.time.year if e else "" + + chair = n.role_set.get(name="chair").email + announcements = Message.objects.filter(related_groups=n).order_by('-time') + for a in announcements: + a.to_name = address_re.sub("", a.to) + + regimes.append(dict(chair=chair, + announcements=announcements, + group=n)) + + regimes.sort(key=lambda x: x["group"].start_year, reverse=True) + + return direct_to_template(request, + "announcements/nomcomREDESIGN.html", + { 'curr_chair' : regimes[0]["chair"], + 'regimes' : regimes }) + + +if settings.USE_DB_REDESIGN_PROXY_CLASSES: + nomcom = nomcomREDESIGN + + +def message_detail(request, object_id, queryset): + from person.models import Email + from group.models import Group + from redesign.announcements.models import Message + + # restrict to nomcom announcements for the time being + nomcoms = Group.objects.filter(acronym__startswith="nomcom").exclude(acronym="nomcom") + m = get_object_or_404(Message, id=object_id, + related_groups__in=nomcoms) + + return direct_to_template(request, + "announcements/message_detail.html", + dict(message=m)) + + diff --git a/ietf/settings.py b/ietf/settings.py index 110773850..7089cf5f4 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -125,6 +125,7 @@ INSTALLED_APPS = ( 'redesign.name', 'redesign.group', 'redesign.doc', + 'redesign.announcements', 'redesign.issue', 'ietf.announcements', 'ietf.idindex', diff --git a/ietf/templates/announcements/message_detail.html b/ietf/templates/announcements/message_detail.html new file mode 100644 index 000000000..606d84046 --- /dev/null +++ b/ietf/templates/announcements/message_detail.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% load ietf_filters %} + +{% block title %}Announcement: {{ message.time|date:"F j, Y" }} -- {{ message.subject|escape }}{% endblock %} + +{% block content %} +

NomCom Message

+

+From: {{ message.frm|escape }}
+To: {{ message.to|escape }}
+Date: {{ message.time|date:"F j, Y" }}
+Subject: {{ message.subject|escape }} +

+
+
+{{ message.text|escape }}
+
+{% endblock %} diff --git a/ietf/templates/announcements/nomcomREDESIGN.html b/ietf/templates/announcements/nomcomREDESIGN.html new file mode 100644 index 000000000..7870db472 --- /dev/null +++ b/ietf/templates/announcements/nomcomREDESIGN.html @@ -0,0 +1,76 @@ +{% extends "base.html" %} +{% load ietf_filters %} +{% block title %}IAB/IESG Nominating Committee{% endblock %} +{% block content %} + +

IAB/IESG Nominating Committee

+ +

Current Committee Chair: {{ curr_chair.get_name }}

+ +{% for regime in regimes %} +
+

Messages from {{ regime.group.start_year }} - {{ regime.group.end_year }}

+

Committee Chair: {{ regime.chair.get_name }}

+ + + + + + + {% for ann in regime.announcements %} + + + + + + {% endfor %} +
DateSubjectSent To
{{ ann.time|date:"Y-M-d" }}{{ ann.subject|escape }}{{ ann.to_name }}
+{% endfor %} + +
+ +{# somebody ought to import these announcements in the DB instead of this mess #} + +

Messages from 2003-2004 NomCom

+Committee Chair: Rich Draves +

  • IETF Nominations Committee Chair Announcement August 25, 2003 +
  • NomCom call for volunteers September 22, 2003 +
  • Selection of the Nominations Committee +
  • NomCom Volunteer List October 06, 2004 +
  • NomCom Selection October 10, 2003 +
  • Call for Nominees October 17, 2003 +
  • NomCom members October 24, 2003 +
  • NomCom at IETF November 07, 2003 +
  • NomCom News November 14, 2003 +
  • Reminder - nominations to replace Randy Bush November 26, 2003 +
  • Randy Bush replacement schedule December 01, 2003 +
  • Randy Bush replacement January 14, 2004 +
  • NomCom results February 13, 2004 +
  • Call for Security AD nominations September 28, 2004 +
  • Steve Bellovin replacement November 07, 2004 + +

    Messages from 2002-2003 NomCom

    + +Committee Chair: Phil Roberts +

    +
  • First Call for Volunteers July 30, 2002 +
  • Selection of the Nominations Committee +
  • Announcement of the Nominations Committee September 18, 2002 +
  • Announcement of IESG and IAB Nominations Requests October 21, 2002 +
  • Announcement of IESG and IAB Nominations Requests November 5, 2002 +
  • Announcement of IESG and IAB Nominations Requests November 12, 2002 +
  • IETF Nomcom Announcement February 27, 2003 +
  • Announcement of IESG and IAB Nominations Request June 11, 2003 +
  • Nomcom result announcement July 15, 2003 + +

    Historical Information

    + +
  • IAB/IESG Nominating Committee Members (by year) + +

    References

    + +
  • The Internet Standards Process (RFC 2026) +
  • IAB and IESG Selection, Confirmation, and Recall Process: Operation of the Nominating and Recall Committees (RFC 3777) (Also BCP10) +
  • Publicly Verifiable Nominations Committee (NomCom) Random Selection (RFC 3797) + +{% endblock %} diff --git a/redesign/announcements/__init__.py b/redesign/announcements/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/redesign/announcements/admin.py b/redesign/announcements/admin.py new file mode 100644 index 000000000..f7ee41c2b --- /dev/null +++ b/redesign/announcements/admin.py @@ -0,0 +1,20 @@ +from django.contrib import admin +from models import * + +class MessageAdmin(admin.ModelAdmin): + list_display = ["time", "by", "subject", "groups"] + search_fields = ["text"] + raw_id_fields = ["by"] + + def groups(self, instance): + return ", ".join(g.acronym for g in related_groups.all()) + +admin.site.register(Message, MessageAdmin) + +class SendQueueAdmin(admin.ModelAdmin): + list_display = ["time", "by", "message", "send_at", "sent_at"] + list_filter = ["time", "send_at", "sent_at"] + search_fields = ["message__text"] + raw_id_fields = ["by"] + +admin.site.register(SendQueue, SendQueueAdmin) diff --git a/redesign/announcements/models.py b/redesign/announcements/models.py new file mode 100644 index 000000000..03c67dbe6 --- /dev/null +++ b/redesign/announcements/models.py @@ -0,0 +1,37 @@ +from django.db import models + +import datetime + +from person.models import Email +from group.models import Group + +class Message(models.Model): + time = models.DateTimeField(default=datetime.datetime.now) + by = models.ForeignKey(Email) + + subject = models.CharField(max_length=255) + frm = models.CharField(max_length=255) + to = models.CharField(max_length=255) + cc = models.CharField(max_length=255, blank=True) + bcc = models.CharField(max_length=255, blank=True) + reply_to = models.CharField(max_length=255, blank=True) + text = models.TextField() + + related_groups = models.ManyToManyField(Group, blank=True) + + class Meta: + ordering = ['time'] + + def __unicode__(self): + return "'%s' %s -> %s" % (self.subject, self.frm, self.to) + +class SendQueue(models.Model): + time = models.DateTimeField(default=datetime.datetime.now) + by = models.ForeignKey(Email) + comment = models.TextField() + message = models.ForeignKey(Message) + send_at = models.DateTimeField(blank=True, null=True) + sent_at = models.DateTimeField(blank=True, null=True) + + class Meta: + ordering = ['time'] diff --git a/redesign/doc/admin.py b/redesign/doc/admin.py index 184d942b7..d3ac4bff3 100644 --- a/redesign/doc/admin.py +++ b/redesign/doc/admin.py @@ -21,10 +21,6 @@ class DocAliasAdmin(admin.ModelAdmin): raw_id_fields = ['document'] admin.site.register(DocAlias, DocAliasAdmin) -class SendQueueAdmin(admin.ModelAdmin): - pass -admin.site.register(SendQueue, SendQueueAdmin) - # events @@ -38,7 +34,6 @@ class EventAdmin(admin.ModelAdmin): admin.site.register(Event, EventAdmin) -admin.site.register(Message, EventAdmin) admin.site.register(NewRevisionEvent, EventAdmin) admin.site.register(WriteupEvent, EventAdmin) admin.site.register(StatusDateEvent, EventAdmin) diff --git a/redesign/doc/models.py b/redesign/doc/models.py index 06f4eb349..4615b1959 100644 --- a/redesign/doc/models.py +++ b/redesign/doc/models.py @@ -169,15 +169,6 @@ class DocAlias(models.Model): verbose_name = "document alias" verbose_name_plural = "document aliases" -class SendQueue(models.Model): - time = models.DateTimeField() # Scheduled at this time - agent = models.ForeignKey(Email) # Scheduled by this person - comment = models.TextField() - # - msg = models.ForeignKey('Message') - to = models.ForeignKey(Email, related_name='to_messages') - cc = models.ManyToManyField(Email, related_name='cc_messages') - send = models.DateTimeField() # Send message at this time # class Ballot(models.Model): # A collection of ballot positions # """A collection of ballot positions, and the actions taken during the @@ -243,10 +234,6 @@ class Event(models.Model): class Meta: ordering = ['-time', 'id'] -class Message(Event): - subj = models.CharField(max_length=255) - body = models.TextField() - class NewRevisionEvent(Event): rev = models.CharField(max_length=16) diff --git a/redesign/import-announcements.py b/redesign/import-announcements.py new file mode 100755 index 000000000..b465a1c22 --- /dev/null +++ b/redesign/import-announcements.py @@ -0,0 +1,86 @@ +#!/usr/bin/python + +import sys, os, re, datetime + +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +sys.path = [ basedir ] + sys.path + +from ietf import settings +settings.USE_DB_REDESIGN_PROXY_CLASSES = False + +from django.core import management +management.setup_environ(settings) + +from redesign.person.models import * +from redesign.group.models import * +from redesign.announcements.models import * +from ietf.announcements.models import Announcement, PersonOrOrgInfo, AnnouncedTo, AnnouncedFrom +from importing.utils import * + +# assumptions: +# - nomcom groups have been imported +# - persons have been imported + +# imports Announcements + +# FIXME: should import ScheduledAnnouncements + +system_email, _ = Email.objects.get_or_create(address="(System)") + +# Announcement +for o in Announcement.objects.all().select_related('announced_to', 'announced_from').order_by('announcement_id').iterator(): + try: + message = Message.objects.get(id=o.announcement_id) + except Message.DoesNotExist: + message = Message(id=o.announcement_id) + + message.time = datetime.datetime.combine(o.announced_date, + datetime.time(*(int(x) for x in o.announced_time.split(":")))) + + try: + x = o.announced_by + except PersonOrOrgInfo.DoesNotExist: + message.by = system_email + else: + if not o.announced_by.first_name and o.announced_by.last_name == 'None': + message.by = system_email + else: + message.by = Email.objects.get(address=person_email(o.announced_by)) + + message.subject = o.subject.strip() + if o.announced_from_id == 99: + message.frm = o.other_val or "" + elif o.announced_from_id == 18 and o.nomcom_chair_id != 0: + message.frm = u"%s <%s>" % o.nomcom_chair.person.email() + else: + if '<' in o.announced_from.announced_from: + message.frm = o.announced_from.announced_from + else: + message.frm = u"%s <%s>" % (o.announced_from.announced_from, o.announced_from.email) + if o.announced_to_id == 99: + message.to = o.other_val or "" + else: + try: + message.to = u"%s <%s>" % (o.announced_to.announced_to, o.announced_to.email) + except AnnouncedTo.DoesNotExist: + message.to = "" + + message.cc = o.cc or "" + for l in (o.extra or "").strip().replace("^", "\n").replace("\r", "").split("\n"): + l = l.strip() + if l.lower().startswith("bcc:"): + message.bcc = l[len("bcc:"):].strip() + elif l.lower().startswith("reply-to:"): + message.reply_to = l[len("reply-to:"):].strip() + message.text = o.text + message.save() + + message.related_groups.clear() + + if o.nomcom: + nomcom = Group.objects.filter(role__name="chair", + role__email__person__id=o.nomcom_chair.person.pk, + acronym__startswith="nomcom").exclude(acronym="nomcom").get() + + message.related_groups.add(nomcom) + diff --git a/redesign/import-document-state.py b/redesign/import-document-state.py index 7d55a22ca..05c712267 100755 --- a/redesign/import-document-state.py +++ b/redesign/import-document-state.py @@ -17,6 +17,8 @@ from redesign.name.models import * from ietf.idtracker.models import InternetDraft, IDInternal, IESGLogin, DocumentComment, PersonOrOrgInfo, Rfc, IESGComment, IESGDiscuss, BallotInfo, Position from ietf.idrfc.models import RfcIndex, DraftVersions +from importing.utils import * + import sys document_name_to_import = None @@ -43,15 +45,6 @@ connection.queries = DummyQueries() # IESGComment, IESGDiscuss, DocumentComment, IDAuthor, idrfc.RfcIndex, # idrfc.DraftVersions -def name(name_class, slug, name, desc="", order=0): - # create if it doesn't exist, set name and desc - obj, _ = name_class.objects.get_or_create(slug=slug) - obj.name = name - obj.desc = desc - obj.order = order - obj.save() - return obj - def alias_doc(name, doc): DocAlias.objects.filter(name=name).exclude(document=doc).delete() alias, _ = DocAlias.objects.get_or_create(name=name, document=doc) @@ -208,7 +201,7 @@ def iesg_login_to_email(l): l = buggy_iesg_logins_cache[l.id] try: - return Email.objects.get(address=l.person.email()[1]) + return Email.objects.get(address=person_email(l.person)) except Email.DoesNotExist: try: return Email.objects.get(person__name="%s %s" % (l.person.first_name, l.person.last_name)) diff --git a/redesign/import-roles.py b/redesign/import-roles.py index 30e7e34c9..2ddbca008 100755 --- a/redesign/import-roles.py +++ b/redesign/import-roles.py @@ -17,27 +17,22 @@ from redesign.group.models import * from redesign.name.models import * from ietf.idtracker.models import IESGLogin, AreaDirector, IDAuthor, PersonOrOrgInfo, WGChair, WGEditor, WGSecretary, WGTechAdvisor, ChairsHistory, Role as OldRole, Acronym, IRTFChair +from importing.utils import * + # assumptions: # - groups have been imported # PersonOrOrgInfo/PostalAddress/EmailAddress/PhoneNumber are not # imported, although some information is retrieved from those -# imports IESGLogin, AreaDirector, WGEditor, persons from IDAuthor, -# NomCom chairs from ChairsHistory, WGChair, IRTFChair, WGSecretary, -# WGTechAdvisor +# imports IESGLogin, AreaDirector, WGEditor, WGChair, IRTFChair, +# WGSecretary, WGTechAdvisor, NomCom chairs from ChairsHistory, +# +# also imports persons from IDAuthor, announcement originators from +# Announcements # FIXME: should probably import Role -# make sure names exist -def name(name_class, slug, name, desc=""): - # create if it doesn't exist, set name - obj, _ = name_class.objects.get_or_create(slug=slug) - obj.name = name - obj.desc = desc - obj.save() - return obj - area_director_role = name(RoleName, "ad", "Area Director") inactive_area_director_role = name(RoleName, "ex-ad", "Ex-Area Director", desc="Inactive Area Director") chair_role = name(RoleName, "chair", "Chair") @@ -47,18 +42,16 @@ techadvisor_role = name(RoleName, "techadv", "Tech Advisor") # helpers for creating the objects def get_or_create_email(o, create_fake): - hardcoded_emails = { 'Dinara Suleymanova': "dinaras@ietf.org" } - - email = o.person.email()[1] or hardcoded_emails.get("%s %s" % (o.person.first_name, o.person.last_name)) + email = person_email(o.person) if not email: if create_fake: email = u"unknown-email-%s-%s" % (o.person.first_name, o.person.last_name) print ("USING FAKE EMAIL %s for %s %s %s" % (email, o.person.pk, o.person.first_name, o.person.last_name)).encode('utf-8') else: - print ("NO EMAIL FOR %s %s %s %s %s" % (o.__class__, o.id, o.person.pk, o.person.first_name, o.person.last_name)).encode('utf-8') + print ("NO EMAIL FOR %s %s %s %s %s" % (o.__class__, o.pk, o.person.pk, o.person.first_name, o.person.last_name)).encode('utf-8') return None - e, _ = Email.objects.get_or_create(address=email) + e, _ = Email.objects.select_related("person").get_or_create(address=email) if not e.person: n = u"%s %s" % (o.person.first_name, o.person.last_name) asciified = unaccent.asciify(n) @@ -66,7 +59,7 @@ def get_or_create_email(o, create_fake): if aliases: p = aliases[0].person else: - p = Person.objects.create(name=n, ascii=asciified) + p = Person.objects.create(id=o.person.pk, name=n, ascii=asciified) # FIXME: fill in address? Alias.objects.create(name=n, person=p) if asciified != n: @@ -203,6 +196,14 @@ for o in AreaDirector.objects.all(): Role.objects.get_or_create(name=role_type, group=area, email=email) +# Announcement persons +for o in PersonOrOrgInfo.objects.filter(announcement__announcement_id__gte=1).distinct(): + print "importing Announcement originator", o.person_or_org_tag, o.first_name.encode('utf-8'), o.last_name.encode('utf-8') + + o.person = o # satisfy the get_or_create_email interface + + email = get_or_create_email(o, create_fake=False) + # IDAuthor persons for o in IDAuthor.objects.all().order_by('id').select_related('person'): print "importing IDAuthor", o.id, o.person_id, o.person.first_name.encode('utf-8'), o.person.last_name.encode('utf-8') diff --git a/redesign/importing/__init__.py b/redesign/importing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/redesign/importing/utils.py b/redesign/importing/utils.py new file mode 100644 index 000000000..29ae77809 --- /dev/null +++ b/redesign/importing/utils.py @@ -0,0 +1,14 @@ +def name(name_class, slug, name, desc="", order=0): + # create if it doesn't exist, set name and desc + obj, _ = name_class.objects.get_or_create(slug=slug) + obj.name = name + obj.desc = desc + obj.order = order + obj.save() + return obj + +def person_email(person): + hardcoded_emails = { 'Dinara Suleymanova': "dinaras@ietf.org" } + + return person.email()[1] or hardcoded_emails.get("%s %s" % (person.first_name, person.last_name)) +