Skip to content

Commit bd34270

Browse files
committed
Port IESG agenda pages to new schema, add tests for them, move agenda
utilities to agenda.py, fix a couple of things, remove obsolete test versions of agenda view, add support for viewing future IESG agendas through /agenda/YYYY-MM-DD/* (useful when testing) - Legacy-Id: 6395
1 parent 53ec62a commit bd34270

21 files changed

Lines changed: 523 additions & 470 deletions

ietf/iesg/agenda.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# utilities for constructing agendas for IESG telechats
2+
3+
import codecs, re, os, datetime
4+
5+
from django.http import Http404
6+
from django.conf import settings
7+
8+
from ietf.iesg.models import TelechatDate, TelechatAgendaItem
9+
from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent
10+
from ietf.group.models import Group, GroupMilestone
11+
12+
def get_agenda_date(date=None):
13+
if not date:
14+
try:
15+
return TelechatDate.objects.active().order_by('date')[0].date
16+
except IndexError:
17+
return datetime.date.today()
18+
else:
19+
try:
20+
return TelechatDate.objects.active().get(date=datetime.datetime.strptime(date, "%Y-%m-%d").date()).date
21+
except (ValueError, TelechatDate.DoesNotExist):
22+
raise Http404
23+
24+
def get_doc_section(doc):
25+
if doc.type_id == 'draft':
26+
if doc.intended_std_level_id in ["bcp", "ds", "ps", "std"]:
27+
s = "2"
28+
else:
29+
s = "3"
30+
31+
g = doc.group_acronym()
32+
if g and str(g) != 'none':
33+
s = s + "1"
34+
elif (s == "3") and doc.stream_id in ("ise","irtf"):
35+
s = s + "3"
36+
else:
37+
s = s + "2"
38+
if doc.get_state_slug() != "rfc" and doc.get_state_slug('draft-iesg') not in ("lc", "writeupw", "goaheadw", "iesg-eva", "defer"):
39+
s = s + "3"
40+
elif doc.returning_item():
41+
s = s + "2"
42+
else:
43+
s = s + "1"
44+
elif doc.type_id == 'charter':
45+
s = get_wg_section(doc.group)
46+
elif doc.type_id == 'statchg':
47+
protocol_action = False
48+
for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')):
49+
if relation.relationship.slug in ('tops','tois') or relation.target.document.std_level.slug in ('std','ds','ps'):
50+
protocol_action = True
51+
if protocol_action:
52+
s="23"
53+
else:
54+
s="33"
55+
if doc.get_state_slug() not in ("iesgeval", "defer", "appr-pr", "appr-pend", "appr-sent"):
56+
s = s + "3"
57+
elif doc.returning_item():
58+
s = s + "2"
59+
else:
60+
s = s + "1"
61+
elif doc.type_id == 'conflrev':
62+
if doc.get_state('conflrev').slug not in ('adrev','iesgeval','appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent','defer'):
63+
s = "343"
64+
elif doc.returning_item():
65+
s = "342"
66+
else:
67+
s = "341"
68+
69+
return s
70+
71+
def get_wg_section(wg):
72+
s = ""
73+
charter_slug = None
74+
if wg.charter:
75+
charter_slug = wg.charter.get_state_slug()
76+
if wg.state_id in ['active','dormant']:
77+
if charter_slug in ['extrev','iesgrev']:
78+
s = '422'
79+
else:
80+
s = '421'
81+
else:
82+
if charter_slug in ['extrev','iesgrev']:
83+
s = '412'
84+
else:
85+
s = '411'
86+
return s
87+
88+
def agenda_docs(date):
89+
matches = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).select_related("stream").distinct()
90+
91+
docmatches = []
92+
93+
for doc in matches:
94+
if doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
95+
continue
96+
97+
e = doc.latest_event(type="started_iesg_process")
98+
doc.balloting_started = e.time if e else datetime.datetime.min
99+
100+
if doc.type_id == "draft":
101+
s = doc.get_state("draft-iana-review")
102+
if s: # and s.slug in ("not-ok", "changed", "need-rev"):
103+
doc.iana_review_state = str(s)
104+
105+
if doc.get_state_slug("draft-iesg") == "lc":
106+
e = doc.latest_event(LastCallDocEvent, type="sent_last_call")
107+
if e:
108+
doc.lastcall_expires = e.expires
109+
110+
if doc.stream_id in ("ietf", "irtf", "iab"):
111+
doc.consensus = "Unknown"
112+
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
113+
if e:
114+
doc.consensus = "Yes" if e.consensus else "No"
115+
elif doc.type_id=='conflrev':
116+
doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
117+
118+
docmatches.append(doc)
119+
120+
# Be careful to keep this the same as what's used in agenda_documents
121+
docmatches.sort(key=lambda d: d.balloting_started)
122+
123+
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))
124+
for k in range(1,4):
125+
res['s34%d'%k]=[]
126+
for doc in docmatches:
127+
section_key = "s" + get_doc_section(doc)
128+
if section_key not in res:
129+
res[section_key] = []
130+
res[section_key].append(doc)
131+
return res
132+
133+
def agenda_wg_actions(date):
134+
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))
135+
charters = Document.objects.filter(type="charter", docevent__telechatdocevent__telechat_date=date).select_related("group").distinct()
136+
charters = charters.filter(group__state__slug__in=["proposed","active"])
137+
for c in charters:
138+
if c.latest_event(TelechatDocEvent, type="scheduled_for_telechat").telechat_date != date:
139+
continue
140+
141+
c.group.txt_link = settings.CHARTER_TXT_URL + "%s-%s.txt" % (c.canonical_name(), c.rev)
142+
143+
section_key = "s" + get_wg_section(c.group)
144+
if section_key not in res:
145+
res[section_key] = []
146+
res[section_key].append(c)
147+
return res
148+
149+
def agenda_management_issues(date):
150+
return TelechatAgendaItem.objects.filter(type=3).order_by('id')
151+
152+
def agenda_data(request, date=None):
153+
"""Return a dict with the different IESG telechat agenda components."""
154+
date = get_agenda_date(date)
155+
docs = agenda_docs(date)
156+
mgmt = agenda_management_issues(date)
157+
wgs = agenda_wg_actions(date)
158+
data = {'date':str(date), 'docs':docs,'mgmt':mgmt,'wgs':wgs}
159+
for key, filename in {'action_items':settings.IESG_TASK_FILE,
160+
'roll_call':settings.IESG_ROLL_CALL_FILE,
161+
'minutes':settings.IESG_MINUTES_FILE}.items():
162+
try:
163+
f = codecs.open(filename, 'r', 'utf-8', 'replace')
164+
text = f.read().strip()
165+
f.close()
166+
data[key] = text
167+
except IOError:
168+
data[key] = "(Error reading "+key+")"
169+
return data
170+

ietf/iesg/tests.py

Lines changed: 102 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import os, shutil
1+
import os, shutil, json
22

33
import django.test
44
from django.core.urlresolvers import reverse as urlreverse
55
from django.conf import settings
66

77
from pyquery import PyQuery
88

9+
from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized
910
from ietf.utils.test_data import make_test_data
1011
from ietf.doc.models import Document, DocEvent, TelechatDocEvent, State
1112
from ietf.person.models import Person
1213
from ietf.group.models import Group
14+
from ietf.name.models import StreamName
1315
from ietf.iesg.models import *
14-
from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest, canonicalize_feed, login_testing_unauthorized
16+
from ietf.iesg.agenda import get_agenda_date
1517

1618
class ReviewDecisionsTests(django.test.TestCase):
1719
def test_review_decisions(self):
@@ -30,27 +32,113 @@ def test_review_decisions(self):
3032

3133

3234
class IESGAgendaTests(django.test.TestCase):
33-
def test_feed(self):
34-
draft = make_test_data()
35+
def setUp(self):
36+
make_test_data()
37+
38+
ise_draft = Document.objects.get(name="draft-imaginary-independent-submission")
39+
ise_draft.stream = StreamName.objects.get(slug="ise")
40+
ise_draft.save()
41+
42+
self.telechat_docs = {
43+
"ietf_draft": Document.objects.get(name="draft-ietf-mars-test"),
44+
"ise_draft": ise_draft,
45+
"conflrev": Document.objects.get(name="conflict-review-imaginary-irtf-submission"),
46+
"statusch": Document.objects.get(name="status-change-imaginary-mid-review"),
47+
"charter": Document.objects.filter(type="charter")[0],
48+
}
49+
50+
by = Person.objects.get(name="Aread Irector")
51+
date = get_agenda_date()
52+
53+
for d in self.telechat_docs.values():
54+
TelechatDocEvent.objects.create(type="scheduled_for_telechat",
55+
doc=d,
56+
by=by,
57+
telechat_date=date,
58+
returning_item=True)
3559

60+
def test_feed(self):
3661
url = "/feed/iesg-agenda/"
3762

3863
r = self.client.get(url)
3964
self.assertEquals(r.status_code, 200)
40-
self.assertTrue(draft.name not in r.content)
4165

42-
# add to schedule
43-
e = TelechatDocEvent(type="scheduled_for_telechat")
44-
e.doc = draft
45-
e.by = Person.objects.get(name="Aread Irector")
46-
e.telechat_date = TelechatDate.objects.active()[0].date
47-
e.returning_item = True
48-
e.save()
66+
for d in self.telechat_docs.values():
67+
self.assertTrue(d.name in r.content)
68+
self.assertTrue(d.title in r.content)
69+
70+
def test_agenda_json(self):
71+
r = self.client.get(urlreverse("ietf.iesg.views.agenda_json"))
72+
self.assertEquals(r.status_code, 200)
4973

74+
for k, d in self.telechat_docs.iteritems():
75+
if d.type_id == "charter":
76+
self.assertTrue(d.group.name in r.content, "%s not in response" % k)
77+
self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k)
78+
else:
79+
self.assertTrue(d.name in r.content, "%s not in response" % k)
80+
self.assertTrue(d.title in r.content, "%s title not in response" % k)
81+
82+
self.assertTrue(json.loads(r.content))
83+
84+
def test_agenda(self):
85+
r = self.client.get(urlreverse("ietf.iesg.views.agenda"))
86+
self.assertEquals(r.status_code, 200)
87+
88+
for k, d in self.telechat_docs.iteritems():
89+
self.assertTrue(d.name in r.content, "%s not in response" % k)
90+
self.assertTrue(d.title in r.content, "%s title not in response" % k)
91+
92+
def test_agenda_txt(self):
93+
r = self.client.get(urlreverse("ietf.iesg.views.agenda_txt"))
94+
self.assertEquals(r.status_code, 200)
95+
96+
for k, d in self.telechat_docs.iteritems():
97+
if d.type_id == "charter":
98+
self.assertTrue(d.group.name in r.content, "%s not in response" % k)
99+
self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k)
100+
else:
101+
self.assertTrue(d.name in r.content, "%s not in response" % k)
102+
self.assertTrue(d.title in r.content, "%s title not in response" % k)
103+
104+
def test_agenda_scribe_template(self):
105+
r = self.client.get(urlreverse("ietf.iesg.views.agenda_scribe_template"))
106+
self.assertEquals(r.status_code, 200)
107+
108+
for k, d in self.telechat_docs.iteritems():
109+
if d.type_id == "charter":
110+
continue # scribe template doesn't contain chartering info
111+
112+
self.assertTrue(d.name in r.content, "%s not in response" % k)
113+
self.assertTrue(d.title in r.content, "%s title not in response" % k)
114+
115+
def test_agenda_moderator_package(self):
116+
url = urlreverse("ietf.iesg.views.agenda_moderator_package")
117+
login_testing_unauthorized(self, "secretary", url)
50118
r = self.client.get(url)
51119
self.assertEquals(r.status_code, 200)
52-
self.assertTrue(draft.name in r.content)
53-
self.assertTrue(draft.title in r.content)
120+
121+
for k, d in self.telechat_docs.iteritems():
122+
if d.type_id == "charter":
123+
self.assertTrue(d.group.name in r.content, "%s not in response" % k)
124+
self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k)
125+
else:
126+
self.assertTrue(d.name in r.content, "%s not in response" % k)
127+
self.assertTrue(d.title in r.content, "%s title not in response" % k)
128+
129+
def test_agenda_package(self):
130+
url = urlreverse("ietf.iesg.views.agenda_package")
131+
login_testing_unauthorized(self, "secretary", url)
132+
r = self.client.get(url)
133+
self.assertEquals(r.status_code, 200)
134+
135+
for k, d in self.telechat_docs.iteritems():
136+
if d.type_id == "charter":
137+
self.assertTrue(d.group.name in r.content, "%s not in response" % k)
138+
self.assertTrue(d.group.acronym in r.content, "%s acronym not in response" % k)
139+
else:
140+
self.assertTrue(d.name in r.content, "%s not in response" % k)
141+
self.assertTrue(d.title in r.content, "%s title not in response" % k)
54142

55143
class RescheduleOnAgendaTestCase(django.test.TestCase):
56144
def test_reschedule(self):

ietf/iesg/urls.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,22 @@
3838

3939
urlpatterns = patterns('',
4040
(r'^telechat/.*$', 'django.views.generic.simple.redirect_to', { 'url': 'http://www.ietf.org/iesg/minutes.html' }),
41-
(r'^decisions/(?:(?P<year>[0-9]{4})/)?$', views.review_decisions),
4241
(r'^ann/(?:ind|new|prev)/$', 'django.views.generic.simple.redirect_to', { 'url': "/iesg/decisions/", 'permanent': True }),
43-
(r'^agenda/$', views.agenda),
44-
(r'^agenda/agenda.txt$', views.agenda_txt),
45-
(r'^agenda/agenda.json$', views.agenda_json),
46-
(r'^agenda/scribe_template.html$', views.agenda_scribe_template),
47-
(r'^agenda/moderator_package.html$', views.agenda_moderator_package),
48-
(r'^agenda/agenda_package.txt$', views.agenda_package),
42+
43+
(r'^decisions/(?:(?P<year>[0-9]{4})/)?$', views.review_decisions),
44+
(r'^agenda/(?:(?P<date>\d{4}-\d{2}-\d{2})/)?$', views.agenda),
45+
(r'^agenda/(?:(?P<date>\d{4}-\d{2}-\d{2})/)?agenda.txt$', views.agenda_txt),
46+
(r'^agenda/(?:(?P<date>\d{4}-\d{2}-\d{2})/)?agenda.json$', views.agenda_json),
47+
(r'^agenda/(?:(?P<date>\d{4}-\d{2}-\d{2})/)?scribe_template.html$', views.agenda_scribe_template),
48+
(r'^agenda/(?:(?P<date>\d{4}-\d{2}-\d{2})/)?moderator_package.html$', views.agenda_moderator_package),
49+
(r'^agenda/(?:(?P<date>\d{4}-\d{2}-\d{2})/)?agenda_package.txt$', views.agenda_package),
4950
(r'^agenda/documents.txt$', views.agenda_documents_txt),
5051
(r'^agenda/documents/$', views.agenda_documents),
5152
(r'^agenda/telechat-(?P<year>\d+)-(?P<month>\d+)-(?P<day>\d+)-docs.tgz', views.telechat_docs_tarfile),
5253
(r'^discusses/$', views.discusses),
53-
(r'^milestones', views.milestones_needing_review),
54+
(r'^milestones$', views.milestones_needing_review),
5455
(r'^telechatdates/$', 'django.views.generic.simple.redirect_to', { 'url': '/admin/iesg/telechatdate/' }),
5556
url(r'^wgactions/$', views.working_group_actions, name="iesg_working_group_actions"),
5657
url(r'^wgactions/add/$', views.edit_working_group_action, { 'wga_id': None }, name="iesg_add_working_group_action"),
5758
url(r'^wgactions/(?P<wga_id>\d+)/$', views.edit_working_group_action, name="iesg_edit_working_group_action"),
5859
)
59-
60-
if settings.SERVER_MODE != 'production':
61-
urlpatterns += patterns('',
62-
(r'^agenda/(?P<date>\d{4}-\d\d-\d\d)/$', views.agenda),
63-
(r'^_test/moderator_package.html$', views.agenda_moderator_package_test),
64-
(r'^_test/agenda_package.txt', views.agenda_package_test),
65-
)

0 commit comments

Comments
 (0)