diff --git a/.gitignore b/.gitignore
index c25e6b5bf..84bc800e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ datatracker.sublime-workspace
/docker/docker-compose.extend-custom.yml
/env
/ghostdriver.log
+/geckodriver.log
/htmlcov
/ietf/static/dist-neue
/latest-coverage.json
diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py
index 2ef4ee83e..528fb05a2 100644
--- a/ietf/doc/views_search.py
+++ b/ietf/doc/views_search.py
@@ -485,6 +485,29 @@ def ad_workload(request):
)
ad.buckets = copy.deepcopy(bucket_template)
+ # https://github.com/ietf-tools/datatracker/issues/4577
+ docs_via_group_ad = Document.objects.exclude(
+ group__acronym="none"
+ ).filter(
+ group__role__name="ad",
+ group__role__person=ad
+ ).filter(
+ states__type="draft-stream-ietf",
+ states__slug__in=["wg-doc","wg-lc","waiting-for-implementation","chair-w","writeupw"]
+ )
+
+ doc_for_ad = Document.objects.filter(ad=ad)
+
+ ad.pre_pubreq = (docs_via_group_ad | doc_for_ad).filter(
+ type="draft"
+ ).filter(
+ states__type="draft",
+ states__slug="active"
+ ).filter(
+ states__type="draft-iesg",
+ states__slug="idexists"
+ ).distinct().count()
+
for doc in Document.objects.exclude(type_id="rfc").filter(ad=ad):
dt = doc_type(doc)
state = doc_state(doc)
diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py
index 4579316f2..8438cb44d 100644
--- a/ietf/iesg/tests.py
+++ b/ietf/iesg/tests.py
@@ -18,7 +18,7 @@ import debug # pyflakes:ignore
from ietf.doc.models import DocEvent, BallotPositionDocEvent, TelechatDocEvent
from ietf.doc.models import Document, State, RelatedDocument
-from ietf.doc.factories import WgDraftFactory, IndividualDraftFactory, ConflictReviewFactory, BaseDocumentFactory, CharterFactory, WgRfcFactory, IndividualRfcFactory
+from ietf.doc.factories import BallotDocEventFactory, BallotPositionDocEventFactory, TelechatDocEventFactory, WgDraftFactory, IndividualDraftFactory, ConflictReviewFactory, BaseDocumentFactory, CharterFactory, WgRfcFactory, IndividualRfcFactory
from ietf.doc.utils import create_ballot_if_not_open
from ietf.group.factories import RoleFactory, GroupFactory, DatedGroupMilestoneFactory, DatelessGroupMilestoneFactory
from ietf.group.models import Group, GroupMilestone, Role
@@ -30,7 +30,6 @@ from ietf.utils.test_utils import TestCase, login_testing_unauthorized, uniconte
from ietf.iesg.factories import IESGMgmtItemFactory, TelechatAgendaContentFactory
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
-
class IESGTests(TestCase):
def test_feed(self):
draft = WgDraftFactory(states=[('draft','active'),('draft-iesg','iesg-eva')],ad=Person.objects.get(user__username='ad'))
@@ -509,12 +508,13 @@ class IESGAgendaTests(TestCase):
def test_agenda_documents(self):
url = urlreverse("ietf.iesg.views.agenda_documents")
r = self.client.get(url)
+
self.assertEqual(r.status_code, 200)
for k, d in self.telechat_docs.items():
self.assertContains(r, d.name, msg_prefix="%s '%s' not in response" % (k, d.name, ))
- self.assertContains(r, d.title, msg_prefix="%s '%s' title not in response" % (k, d.title, ))
-
+ self.assertContains(r, d.title, msg_prefix="%s '%s' not in response" % (k, d.title, ))
+
def test_past_documents(self):
url = urlreverse("ietf.iesg.views.past_documents")
# We haven't put any documents on past telechats, so this should be empty
@@ -589,6 +589,66 @@ class IESGAgendaTests(TestCase):
draft = Document.objects.get(name="draft-ietf-mars-test")
self.assertEqual(draft.telechat_date(),today)
+class IESGAgendaTelechatPagesTests(TestCase):
+ def setUp(self):
+ super().setUp()
+ # make_immutable_test_data made a set of future telechats - only need one
+ # We'll take the "next" one
+ self.telechat_date = get_agenda_date()
+ # make_immutable_test_data made and area with only one ad - give it another
+ ad = Person.objects.get(user__username="ad")
+ adrole = Role.objects.get(person=ad, name="ad")
+ ad2 = RoleFactory(group=adrole.group, name_id="ad").person
+ self.ads=[ad,ad2]
+
+ # Make some drafts
+ docs = [
+ WgDraftFactory(pages=2, states=[('draft-iesg','iesg-eva'),]),
+ IndividualDraftFactory(pages=20, states=[('draft-iesg','iesg-eva'),]),
+ WgDraftFactory(pages=200, states=[('draft-iesg','iesg-eva'),]),
+ ]
+ # Put them on the telechat
+ for doc in docs:
+ TelechatDocEventFactory(doc=doc, telechat_date=self.telechat_date)
+ # Give them ballots
+ ballots = [BallotDocEventFactory(doc=doc) for doc in docs]
+
+ # Give the "ad" Area-Director a discuss on one
+ BallotPositionDocEventFactory(balloter=ad, doc=docs[0], pos_id="discuss", ballot=ballots[0])
+ # and a "norecord" position on another
+ BallotPositionDocEventFactory(balloter=ad, doc=docs[1], pos_id="norecord", ballot=ballots[1])
+ # Now "ad" should have 220 pages left to ballot on.
+ # Every other ad should have 222 pages left to ballot on.
+
+ def test_ad_pages_left_to_ballot_on(self):
+ url = urlreverse("ietf.iesg.views.agenda_documents")
+
+ # A non-AD user won't get "pages left"
+ response = self.client.get(url)
+ telechat = response.context["telechats"][0]
+ self.assertEqual(telechat["date"], self.telechat_date)
+ self.assertEqual(telechat["ad_pages_left_to_ballot_on"],0)
+ self.assertNotContains(response,"pages left to ballot on")
+
+ username=self.ads[0].user.username
+ self.assertTrue(self.client.login(username=username, password=f"{username}+password"))
+
+ response = self.client.get(url)
+ telechat = response.context["telechats"][0]
+ self.assertEqual(telechat["ad_pages_left_to_ballot_on"],220)
+ self.assertContains(response,"220 pages left to ballot on")
+
+ self.client.logout()
+ username=self.ads[1].user.username
+ self.assertTrue(self.client.login(username=username, password=f"{username}+password"))
+
+ response = self.client.get(url)
+ telechat = response.context["telechats"][0]
+ self.assertEqual(telechat["ad_pages_left_to_ballot_on"],222)
+
+
+
+
class RescheduleOnAgendaTests(TestCase):
def test_reschedule(self):
draft = WgDraftFactory()
diff --git a/ietf/iesg/utils.py b/ietf/iesg/utils.py
index 3f4883798..a56fa72ce 100644
--- a/ietf/iesg/utils.py
+++ b/ietf/iesg/utils.py
@@ -7,11 +7,11 @@ from ietf.doc.utils_search import fill_in_telechat_date
from ietf.iesg.agenda import get_doc_section
-TelechatPageCount = namedtuple('TelechatPageCount',['for_approval','for_action','related'])
+TelechatPageCount = namedtuple('TelechatPageCount',['for_approval','for_action','related','ad_pages_left_to_ballot_on'])
-def telechat_page_count(date=None, docs=None):
+def telechat_page_count(date=None, docs=None, ad=None):
if not date and not docs:
- return TelechatPageCount(0, 0, 0)
+ return TelechatPageCount(0, 0, 0, 0)
if not docs:
candidates = Document.objects.filter(docevent__telechatdocevent__telechat_date=date).distinct()
@@ -24,7 +24,18 @@ def telechat_page_count(date=None, docs=None):
drafts = [d for d in for_approval if d.type_id == 'draft']
- pages_for_approval = sum([d.pages or 0 for d in drafts])
+ ad_pages_left_to_ballot_on = 0
+ pages_for_approval = 0
+
+ for draft in drafts:
+ pages_for_approval += draft.pages or 0
+ if ad:
+ ballot = draft.active_ballot()
+ if ballot:
+ positions = ballot.active_balloter_positions()
+ ad_position = positions[ad]
+ if ad_position is None or ad_position.pos_id == "norecord":
+ ad_pages_left_to_ballot_on += draft.pages or 0
pages_for_action = 0
for d in for_action:
@@ -53,4 +64,5 @@ def telechat_page_count(date=None, docs=None):
return TelechatPageCount(for_approval=pages_for_approval,
for_action=pages_for_action,
- related=related_pages)
+ related=related_pages,
+ ad_pages_left_to_ballot_on=ad_pages_left_to_ballot_on)
diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py
index a92d617ac..df02754f2 100644
--- a/ietf/iesg/views.py
+++ b/ietf/iesg/views.py
@@ -360,6 +360,8 @@ def handle_reschedule_form(request, doc, dates, status):
return form
def agenda_documents(request):
+ ad = request.user.person if has_role(request.user, "Area Director") else None
+
dates = list(TelechatDate.objects.active().order_by('date').values_list("date", flat=True)[:4])
docs_by_date = dict((d, []) for d in dates)
@@ -389,11 +391,13 @@ def agenda_documents(request):
# the search_result_row view to display them (which expects them)
fill_in_document_table_attributes(docs_by_date[date], have_telechat_date=True)
fill_in_agenda_docs(date, sections, docs_by_date[date])
- pages = telechat_page_count(docs=docs_by_date[date]).for_approval
-
+ page_count = telechat_page_count(docs=docs_by_date[date], ad=ad)
+ pages = page_count.for_approval
+
telechats.append({
"date": date,
"pages": pages,
+ "ad_pages_left_to_ballot_on": page_count.ad_pages_left_to_ballot_on,
"sections": sorted((num, section) for num, section in sections.items()
if "2" <= num < "5")
})
diff --git a/ietf/templates/doc/ad_list.html b/ietf/templates/doc/ad_list.html
index cfc8830e5..a73264c0f 100644
--- a/ietf/templates/doc/ad_list.html
+++ b/ietf/templates/doc/ad_list.html
@@ -35,9 +35,12 @@
Area Director
+ {% if dt.type.1 == "Internet-Draft" %}
+ Pre pubreq
+ {% endif %}
{% for state, state_name in dt.states %}
-
+
{{ state_name|split:'/'|join:'/
{{ ad.name }}
+ {% if dt.type.1 == "Internet-Draft" %}
+
+ {{ ad.pre_pubreq }}
+
+ {% endif %}
{% for state, state_name in dt.states %}
@@ -63,6 +77,16 @@