Import IRTF groups, chairs and other roles, port notify-expirations script (and add test)

- Legacy-Id: 3068
This commit is contained in:
Ole Laursen 2011-04-27 13:06:14 +00:00
parent 8762d77fd8
commit 920b2d25c3
12 changed files with 301 additions and 90 deletions

View file

@ -6,56 +6,11 @@ from ietf import settings
from django.core import management
management.setup_environ(settings)
from ietf.idtracker.models import InternetDraft,IDAuthor,WGChair
from ietf.utils.mail import send_mail_subj
notify_days = 14 # notify about documents that expire within the
# next 2 weeks
start_date = datetime.date.today() - datetime.timedelta(InternetDraft.DAYS_TO_EXPIRE - 1)
end_date = start_date + datetime.timedelta(notify_days - 1)
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id
matches = InternetDraft.objects.filter(revision_date__gte=start_date,revision_date__lte=end_date,status__status='Active')
# notify about documents that expire within the next 2 weeks
notify_days = 14
#For development - focus on one draft
#matches = InternetDraft.objects.filter(filename__icontains='geopriv-http-location-delivery')
# Todo:
#second_cutoff = IDDates.objects.get(date_id=2)
#ietf_monday = IDDates.objects.get(date_id=3)
#freeze_delta = ietf_monday - second_cutoff
for draft in matches:
if not draft.can_expire():
# debugging
#print "%s can't expire, skipping" % draft
continue
expiration = draft.expiration()
# # The I-D expiration job doesn't run while submissions are frozen.
# if ietf_monday > expiration > second_cutoff:
# expiration += freeze_delta
authors = draft.authors.all()
to_addrs = [author.email() for author in authors if author.email()]
cc_addrs = None
if draft.group.acronym != 'none':
cc_addrs = [chair.person.email() for chair in WGChair.objects.filter(group_acronym=draft.group)]
#For development debugging
"""
print "filename: "+draft.filename
print "to: ", to_addrs
print "cc: ", cc_addrs
print "expires: ", expiration
print "status: ", draft.status.status, "/", draft.idstate()
print
continue
"""
if to_addrs or cc_addrs:
send_mail_subj(None, to_addrs, None, 'notify_expirations/subject.txt', 'notify_expirations/body.txt',
{
'draft':draft,
'expiration':expiration,
},
cc_addrs)
for doc in get_soon_to_expire_ids(notify_days):
send_expire_warning_for_id(doc)

View file

@ -6,8 +6,8 @@ from django.db.models import Q
import datetime, os, shutil, glob, re
from ietf.idtracker.models import InternetDraft, IDDates, IDStatus, IDState, DocumentComment
from ietf.utils.mail import send_mail
from ietf.idtracker.models import InternetDraft, IDDates, IDStatus, IDState, DocumentComment, IDAuthor,WGChair
from ietf.utils.mail import send_mail, send_mail_subj
from ietf.idrfc.utils import log_state_changed, add_document_comment
from doc.models import Document, Event, save_document_in_history
from name.models import IesgDocStateName, DocStateName, DocInfoTagName
@ -28,6 +28,33 @@ def in_id_expire_freeze(when=None):
return second_cut_off <= when < ietf_monday
def document_expires(doc):
e = doc.latest_event(type__in=("completed_resurrect", "new_revision"))
if e:
return e.time + datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE)
else:
return None
def expirable_documents():
return Document.objects.filter(state="active").exclude(tags="rfc-rev").filter(Q(iesg_state=None) | Q(iesg_state__order__gte=42))
def get_soon_to_expire_ids(days):
start_date = datetime.date.today() - datetime.timedelta(InternetDraft.DAYS_TO_EXPIRE - 1)
end_date = start_date + datetime.timedelta(days - 1)
for d in InternetDraft.objects.filter(revision_date__gte=start_date,revision_date__lte=end_date,status__status='Active'):
if d.can_expire():
yield d
def get_soon_to_expire_idsREDESIGN(days):
start_date = datetime.date.today() - datetime.timedelta(1)
end_date = start_date + datetime.timedelta(days - 1)
for d in expirable_documents():
e = document_expires(d)
if e and start_date <= e.date() <= end_date:
yield d
def get_expired_ids():
cut_off = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE)
@ -38,14 +65,59 @@ def get_expired_ids():
Q(idinternal=None) | Q(idinternal__cur_state__document_state_id__gte=42))
def get_expired_idsREDESIGN():
cut_off = datetime.date.today() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE)
today = datetime.date.today()
docs = Document.objects.filter(state="active").exclude(tags="rfc-rev").filter(Q(iesg_state=None) | Q(iesg_state__order__gte=42))
for d in docs:
e = d.latest_event(type="new_revision")
if e and e.time.date() <= cut_off:
for d in expirable_documents():
e = document_expires(d)
if e and e.time.date() <= today:
yield d
def send_expire_warning_for_id(doc):
expiration = doc.expiration()
# Todo:
#second_cutoff = IDDates.objects.get(date_id=2)
#ietf_monday = IDDates.objects.get(date_id=3)
#freeze_delta = ietf_monday - second_cutoff
# # The I-D expiration job doesn't run while submissions are frozen.
# if ietf_monday > expiration > second_cutoff:
# expiration += freeze_delta
authors = doc.authors.all()
to_addrs = [author.email() for author in authors if author.email()]
cc_addrs = None
if doc.group.acronym != 'none':
cc_addrs = [chair.person.email() for chair in WGChair.objects.filter(group_acronym=doc.group)]
if to_addrs or cc_addrs:
send_mail_subj(None, to_addrs, None, 'notify_expirations/subject.txt', 'notify_expirations/body.txt',
{
'draft':doc,
'expiration':expiration,
},
cc_addrs)
def send_expire_warning_for_idREDESIGN(doc):
expiration = document_expires(doc).date()
to = [e.formatted_email() for e in doc.authors.all() if not e.address.startswith("unknown-email")]
cc = None
if doc.group.type_id != "individ":
cc = [e.formatted_email() for e in Email.objects.filter(role__group=doc.group, role__name="chair") if not e.address.startswith("unknown-email")]
state = doc.iesg_state.name if doc.iesg_state else "I-D Exists"
frm = None
request = None
if to or cc:
send_mail(request, to, frm,
u"Expiration impending: %s" % doc.file_tag(),
"idrfc/expire_warning_email.txt",
dict(doc=doc,
state=state,
expiration=expiration
),
cc=cc)
def send_expire_notice_for_id(doc):
doc.dunn_sent_date = datetime.date.today()
doc.save()
@ -242,7 +314,9 @@ def clean_up_id_filesREDESIGN():
move_file_to("unknown_ids")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
get_soon_to_expire_ids = get_soon_to_expire_idsREDESIGN
get_expired_ids = get_expired_idsREDESIGN
send_expire_warning_for_id = send_expire_warning_for_idREDESIGN
send_expire_notice_for_id = send_expire_notice_for_idREDESIGN
expire_id = expire_idREDESIGN
clean_up_id_files = clean_up_id_filesREDESIGN

View file

@ -446,12 +446,30 @@
</object>
<object pk="ex-ad" model="name.rolename">
<field type="CharField" name="name">Ex-Area Director</field>
<field type="TextField" name="desc">In-active Area Director</field>
<field type="TextField" name="desc">Inactive Area Director</field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="wgeditor" model="name.rolename">
<field type="CharField" name="name">Working Group Editor</field>
<object pk="chair" model="name.rolename">
<field type="CharField" name="name">Chair</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="editor" model="name.rolename">
<field type="CharField" name="name">Editor</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="secr" model="name.rolename">
<field type="CharField" name="name">Secretary</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>
</object>
<object pk="techadv" model="name.rolename">
<field type="CharField" name="name">Tech Advisor</field>
<field type="TextField" name="desc"></field>
<field type="BooleanField" name="used">1</field>
<field type="IntegerField" name="order">0</field>

View file

@ -774,6 +774,34 @@ class ExpireIDsTestCase(django.test.TestCase):
self.assertTrue(in_id_expire_freeze(datetime.datetime(2010, 7, 12, 10, 0)))
self.assertTrue(in_id_expire_freeze(datetime.datetime(2010, 7, 25, 0, 0)))
self.assertTrue(not in_id_expire_freeze(datetime.datetime(2010, 7, 26, 0, 0)))
def test_warn_expirable_ids(self):
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id
# hack into almost expirable state
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft.status = IDStatus.objects.get(status="Active")
draft.review_by_rfc_editor = 0
draft.revision_date = datetime.date.today() - datetime.timedelta(days=InternetDraft.DAYS_TO_EXPIRE - 7)
draft.idinternal.cur_state_id = IDState.AD_WATCHING
draft.idinternal.save()
draft.save()
author = PersonOrOrgInfo.objects.all()[0]
IDAuthor.objects.create(document=draft, person=author, author_order=1)
EmailAddress.objects.create(person_or_org=author, type="I-D", priority=draft.pk, address="author@example.com")
# test query
documents = list(get_soon_to_expire_ids(14))
self.assertEquals(len(documents), 1)
# test send warning
mailbox_before = len(mail_outbox)
send_expire_warning_for_id(documents[0])
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("author@example.com" in str(mail_outbox[-1]))
def test_expire_ids(self):
from ietf.idrfc.expire import get_expired_ids, send_expire_notice_for_id, expire_id

View file

@ -74,7 +74,8 @@ def make_test_data():
# persons
Email.objects.get_or_create(address="(System)")
# ad
p = Person.objects.create(
name="Aread Irector",
ascii="Aread Irector",
@ -137,6 +138,21 @@ def make_test_data():
person=porg,
)
# group chair
p = Person.objects.create(
name="WG Chair Man",
ascii="WG Chair Man",
)
wgchair = Email.objects.create(
address="wgchairman@ietf.org",
person=p)
Role.objects.create(
name=RoleName.objects.get(slug="chair"),
group=group,
email=wgchair,
)
# secretary
p = Person.objects.create(
name="Sec Retary",
ascii="Sec Retary",
@ -187,6 +203,12 @@ def make_test_data():
name=draft.name,
)
DocumentAuthor.objects.create(
document=draft,
author=Email.objects.get(address="aread@ietf.org"),
order=1
)
# draft has only one event
Event.objects.create(
type="started_iesg_process",
@ -194,7 +216,7 @@ def make_test_data():
doc=draft,
desc="Added draft",
)
# telechat dates
t = datetime.date.today()
dates = TelechatDates(date1=t,
@ -1002,6 +1024,38 @@ class ExpireIDsTestCase(django.test.TestCase):
self.assertTrue(in_id_expire_freeze(datetime.datetime(2010, 7, 25, 0, 0)))
self.assertTrue(not in_id_expire_freeze(datetime.datetime(2010, 7, 26, 0, 0)))
def test_warn_expirable_ids(self):
from ietf.idrfc.expire import get_soon_to_expire_ids, send_expire_warning_for_id, INTERNET_DRAFT_DAYS_TO_EXPIRE
draft = make_test_data()
self.assertEquals(len(list(get_soon_to_expire_ids(14))), 0)
# hack into expirable state
draft.iesg_state = None
draft.save()
NewRevisionEvent.objects.create(
type="new_revision",
by=Email.objects.get(address="aread@ietf.org"),
doc=draft,
desc="New revision",
time=datetime.datetime.now() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE - 7),
rev="01"
)
self.assertEquals(len(list(get_soon_to_expire_ids(14))), 1)
# test send warning
mailbox_before = len(mail_outbox)
send_expire_warning_for_id(draft)
print mail_outbox[-1]
self.assertEquals(len(mail_outbox), mailbox_before + 1)
self.assertTrue("aread@ietf.org" in str(mail_outbox[-1])) # author
self.assertTrue("wgchairman@ietf.org" in str(mail_outbox[-1]))
def test_expire_ids(self):
from ietf.idrfc.expire import get_expired_ids, send_expire_notice_for_id, expire_id, INTERNET_DRAFT_DAYS_TO_EXPIRE

View file

@ -441,7 +441,7 @@ def get_initial_notify(doc):
receivers.append(e.address)
else:
receivers.append("%s-chairs@%s" % (doc.group.acronym, settings.TOOLS_SERVER))
for editor in Email.objects.filter(role__name="wgeditor", role__group=doc.group):
for editor in Email.objects.filter(role__name="editor", role__group=doc.group):
receivers.append(e.address)
receivers.append("%s@%s" % (doc.name, settings.TOOLS_SERVER))

View file

@ -0,0 +1,6 @@
The following draft will expire soon:
Name: {{ doc.name }}
Title: {{ doc.title}}
State: {{ state }}
Expires: {{ expiration }} (in {{ expiration|timeuntil }})

View file

@ -128,7 +128,7 @@ def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=N
else:
msg = MIMEText(txt)
send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=None, bcc=None)
send_mail_mime(request, to, frm, subject, msg, cc, extra, toUser, bcc)
def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=None, bcc=None):
"""Send MIME message with content already filled in."""
@ -143,6 +143,7 @@ def send_mail_mime(request, to, frm, subject, msg, cc=None, extra=None, toUser=N
msg['To'] = to
if cc:
msg['Cc'] = cc
print cc
msg['Subject'] = subject
msg['X-Test-IDTracker'] = (settings.SERVER_MODE == 'production') and 'no' or 'yes'
if extra:

View file

@ -209,7 +209,6 @@ class InternetDraft(Document):
# reverse relationship
@property
def authors(self):
from person.models import Person
return IDAuthor.objects.filter(document=self)
# methods from InternetDraft

View file

@ -16,12 +16,10 @@ from redesign.group.models import *
from redesign.name.models import *
from ietf.idtracker.models import AreaGroup, IETFWG, Area, AreaGroup, Acronym, AreaWGURL, IRTF, ChairsHistory, Role
# imports IETFWG, Area, AreaGroup, Acronym
# imports IETFWG, Area, AreaGroup, Acronym, IRTF
# also creates nomcom groups
# FIXME: should also import IRTF
# make sure we got the names
def name(name_class, slug, name, desc=""):
# create if it doesn't exist, set name and desc
@ -60,11 +58,11 @@ system_email, _ = Email.objects.get_or_create(address="(System)")
# NomCom
Group.objects.filter(acronym="nomcom").delete()
Group.objects.filter(acronym="nominatingcom").delete()
for o in ChairsHistory.objects.filter(chair_type=Role.NOMCOM_CHAIR).order_by("start_year"):
group = Group()
group.acronym = "nomcom"
group.acronym = "nominatingcom"
group.name = "IAB/IESG Nominating Committee %s/%s" % (o.start_year, o.end_year)
if o.chair_type.person == o.person:
s = state_names["active"]
@ -118,6 +116,28 @@ for o in Area.objects.all():
# FIXME: missing fields from old: last_modified_date, extra_email_addresses
# IRTF
for o in IRTF.objects.all():
try:
group = Group.objects.get(acronym=o.acronym.lower())
except Group.DoesNotExist:
group = Group(acronym=o.acronym.lower())
group.name = o.name
group.state = state_names["active"] # we assume all to be active
group.type = type_names["rg"]
# FIXME: who is the parent?
# group.parent = Group.objects.get(acronym=)
print "no parent for", group.acronym, group.name, group.type, group.state
group.comments = o.charter_text or ""
group.save()
# FIXME: missing fields from old: meeting_scheduled
# IETFWG, AreaGroup
for o in IETFWG.objects.all():
try:

View file

@ -15,7 +15,7 @@ management.setup_environ(settings)
from redesign.person.models import *
from redesign.group.models import *
from redesign.name.models import *
from ietf.idtracker.models import IESGLogin, AreaDirector, IDAuthor, PersonOrOrgInfo, WGEditor, ChairsHistory, Role as OldRole
from ietf.idtracker.models import IESGLogin, AreaDirector, IDAuthor, PersonOrOrgInfo, WGChair, WGEditor, WGSecretary, WGTechAdvisor, ChairsHistory, Role as OldRole, Acronym, IRTFChair
# assumptions:
# - groups have been imported
@ -24,10 +24,10 @@ from ietf.idtracker.models import IESGLogin, AreaDirector, IDAuthor, PersonOrOrg
# imported, although some information is retrieved from those
# imports IESGLogin, AreaDirector, WGEditor, persons from IDAuthor,
# NomCom chairs from ChairsHistory
# NomCom chairs from ChairsHistory, WGChair, IRTFChair, WGSecretary,
# WGTechAdvisor
# should probably import WGChair, WGSecretary,
# WGTechAdvisor, Role, IRTFChair
# FIXME: should probably import Role
# make sure names exist
def name(name_class, slug, name, desc=""):
@ -40,8 +40,10 @@ def name(name_class, slug, name, desc=""):
area_director_role = name(RoleName, "ad", "Area Director")
inactive_area_director_role = name(RoleName, "ex-ad", "Ex-Area Director", desc="Inactive Area Director")
wg_editor_role = name(RoleName, "wgeditor", "Working Group Editor")
chair_role = name(RoleName, "chair", "Chair")
editor_role = name(RoleName, "editor", "Editor")
secretary_role = name(RoleName, "secr", "Secretary")
techadvisor_role = name(RoleName, "techadv", "Tech Advisor")
# helpers for creating the objects
def get_or_create_email(o, create_fake):
@ -75,7 +77,74 @@ def get_or_create_email(o, create_fake):
return e
nomcom_groups = list(Group.objects.filter(acronym="nomcom"))
# WGEditor
for o in WGEditor.objects.all():
acronym = Acronym.objects.get(acronym_id=o.group_acronym_id).acronym
print "importing WGEditor", acronym, o.person
email = get_or_create_email(o, create_fake=True)
group = Group.objects.get(acronym=acronym)
Role.objects.get_or_create(name=editor_role, group=group, email=email)
# WGSecretary
for o in WGSecretary.objects.all():
acronym = Acronym.objects.get(acronym_id=o.group_acronym_id).acronym
print "importing WGSecretary", acronym, o.person
email = get_or_create_email(o, create_fake=True)
group = Group.objects.get(acronym=acronym)
Role.objects.get_or_create(name=secretary_role, group=group, email=email)
# WGTechAdvisor
for o in WGTechAdvisor.objects.all():
acronym = Acronym.objects.get(acronym_id=o.group_acronym_id).acronym
print "importing WGTechAdvisor", acronym, o.person
email = get_or_create_email(o, create_fake=True)
group = Group.objects.get(acronym=acronym)
Role.objects.get_or_create(name=techadvisor_role, group=group, email=email)
# WGChair
for o in WGChair.objects.all():
# there's some garbage in this table, so wear double safety belts
try:
acronym = Acronym.objects.get(acronym_id=o.group_acronym_id).acronym
except Acronym.DoesNotExist:
print "SKIPPING WGChair with unknown acronym id", o.group_acronym_id
continue
try:
person = o.person
except PersonOrOrgInfo.DoesNotExist:
print "SKIPPING WGChair", acronym, "with invalid person id", o.person_id
continue
if acronym in ("apples", "apptsv", "usac", "null", "dirdir"):
print "SKIPPING WGChair", acronym, o.person
continue
print "importing WGChair", acronym, o.person
email = get_or_create_email(o, create_fake=True)
group = Group.objects.get(acronym=acronym)
Role.objects.get_or_create(name=chair_role, group=group, email=email)
# IRTFChair
for o in IRTFChair.objects.all():
acronym = o.irtf.acronym.lower()
print "importing IRTFChair", acronym, o.person
email = get_or_create_email(o, create_fake=True)
group = Group.objects.get(acronym=acronym)
Role.objects.get_or_create(name=chair_role, group=group, email=email)
# NomCom chairs
nomcom_groups = list(Group.objects.filter(acronym="nominatingcom"))
for o in ChairsHistory.objects.filter(chair_type=OldRole.NOMCOM_CHAIR):
print "importing NOMCOM chair", o
for g in nomcom_groups:
@ -86,7 +155,6 @@ for o in ChairsHistory.objects.filter(chair_type=OldRole.NOMCOM_CHAIR):
Role.objects.get_or_create(name=chair_role, group=g, email=email)
# IESGLogin
for o in IESGLogin.objects.all():
print "importing IESGLogin", o.id, o.first_name, o.last_name
@ -134,18 +202,6 @@ for o in AreaDirector.objects.all():
else:
Role.objects.get_or_create(name=role_type, group=area, email=email)
# WGEditor
for o in WGEditor.objects.all():
# if not o.group_acronym:
# print "NO GROUP", o.person, o.group_acronym_id
# continue
print "importing WGEditor", o.group_acronym, o.person
email = get_or_create_email(o, create_fake=False)
group = Group.objects.get(acronym=o.group_acronym.group_acronym.acronym)
Role.objects.get_or_create(name=wg_editor_role, group=group, email=email)
# IDAuthor persons
for o in IDAuthor.objects.all().order_by('id').select_related('person'):

View file

@ -68,7 +68,7 @@ class Email(models.Model):
def formatted_email(self):
if self.person and self.person.name:
return u"%s <%s>" % (self.person.name, self.address)
return u'"%s" <%s>' % (self.person.name, self.address)
else:
return self.address