Merged in [16875] from rjsparks@nostrum.com:

Adds docs with blocking positions to the ADs my-documents page. Fixes #2800.
 - Legacy-Id: 16906
Note: SVN reference [16875] has been migrated to Git commit f7d4600371b3f2a3c92aa184b517ed4ebee3a118
This commit is contained in:
Henrik Levkowetz 2019-10-22 17:43:54 +00:00
parent 04e342b3ce
commit 6688d37bb4
4 changed files with 123 additions and 10 deletions

View file

@ -13,7 +13,7 @@ if six.PY3:
from django.conf import settings
from ietf.doc.models import Document, DocEvent, NewRevisionDocEvent, DocAlias, State, DocumentAuthor, StateDocEvent
from ietf.doc.models import Document, DocEvent, NewRevisionDocEvent, DocAlias, State, DocumentAuthor, StateDocEvent, BallotPositionDocEvent, BallotDocEvent, BallotType
from ietf.group.models import Group
def draft_name_generator(type_id,group,n):
@ -287,3 +287,34 @@ class StateDocEventFactory(DocEventFactory):
else:
obj.state = State.objects.get(type_id='draft-iesg',slug='ad-eval')
obj.save()
# All of these Ballot* factories are extremely skeletal. Flesh them out as needed by tests.
class BallotTypeFactory(factory.DjangoModelFactory):
class Meta:
model = BallotType
doc_type_id = 'draft'
slug = 'approve'
class BallotDocEventFactory(DocEventFactory):
class Meta:
model = BallotDocEvent
ballot_type = factory.SubFactory(BallotTypeFactory)
type = 'created_ballot'
class BallotPositionDocEventFactory(DocEventFactory):
class Meta:
model = BallotPositionDocEvent
type = 'changed_ballot_position'
# This isn't right - it needs to build a ballot for the same doc as this position
# For now, deal with this in test code by building BallotDocEvent and BallotPositionDocEvent
# separately and passing the same doc into thier factories.
ballot = factory.SubFactory(BallotDocEventFactory)
ad = factory.SubFactory('ietf.person.factories.PersonFactory')
pos_id = 'discuss'

View file

@ -29,15 +29,18 @@ from tastypie.test import ResourceTestCaseMixin
import debug # pyflakes:ignore
from ietf.doc.models import ( Document, DocAlias, DocRelationshipName, RelatedDocument, State,
DocEvent, BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent, NewRevisionDocEvent )
from ietf.doc.factories import DocumentFactory, DocEventFactory, CharterFactory, ConflictReviewFactory, WgDraftFactory, IndividualDraftFactory, WgRfcFactory, IndividualRfcFactory, StateDocEventFactory
DocEvent, BallotPositionDocEvent, LastCallDocEvent, WriteupDocEvent, NewRevisionDocEvent, BallotType )
from ietf.doc.factories import ( DocumentFactory, DocEventFactory, CharterFactory,
ConflictReviewFactory, WgDraftFactory, IndividualDraftFactory, WgRfcFactory,
IndividualRfcFactory, StateDocEventFactory, BallotPositionDocEventFactory,
BallotDocEventFactory )
from ietf.doc.utils import create_ballot_if_not_open
from ietf.group.models import Group
from ietf.group.factories import GroupFactory
from ietf.group.factories import GroupFactory, RoleFactory
from ietf.ipr.factories import HolderIprDisclosureFactory
from ietf.meeting.models import Meeting, Session, SessionPresentation
from ietf.meeting.factories import MeetingFactory, SessionFactory
from ietf.name.models import SessionStatusName
from ietf.name.models import SessionStatusName, BallotPositionName
from ietf.person.models import Person
from ietf.person.factories import PersonFactory
from ietf.utils.mail import outbox
@ -219,7 +222,7 @@ class SearchTests(TestCase):
self.assertContains(r, "Document Search")
def test_docs_for_ad(self):
ad = PersonFactory()
ad = RoleFactory(name_id='ad',group__type_id='area',group__state_id='active').person
draft = IndividualDraftFactory(ad=ad)
draft.set_state(State.objects.get(type='draft-iesg', slug='lc'))
rfc = IndividualDraftFactory(ad=ad)
@ -231,7 +234,12 @@ class SearchTests(TestCase):
statchg.set_state(State.objects.get(type='statchg', slug='iesgeval'))
charter = CharterFactory(ad=ad)
charter.set_state(State.objects.get(type='charter', slug='iesgrev'))
ballot_type = BallotType.objects.get(doc_type_id='draft',slug='approve')
ballot = BallotDocEventFactory(ballot_type=ballot_type, doc__states=[('draft-iesg','iesg-eva')])
discuss_pos = BallotPositionName.objects.get(slug='discuss')
discuss_other = BallotPositionDocEventFactory(ballot=ballot, doc=ballot.doc, ad=ad, pos=discuss_pos)
r = self.client.get(urlreverse('ietf.doc.views_search.docs_for_ad', kwargs=dict(name=ad.full_name_as_key())))
self.assertEqual(r.status_code, 200)
self.assertContains(r, draft.name)
@ -239,6 +247,8 @@ class SearchTests(TestCase):
self.assertContains(r, conflrev.name)
self.assertContains(r, statchg.name)
self.assertContains(r, charter.name)
self.assertContains(r, discuss_other.doc.name)
def test_drafts_in_last_call(self):

View file

@ -52,9 +52,9 @@ from django.utils.cache import _generate_cache_key # type: ignore (FIXME: remove
import debug # pyflakes:ignore
from ietf.doc.models import ( Document, DocHistory, DocAlias, State,
LastCallDocEvent, NewRevisionDocEvent, IESG_SUBSTATE_TAGS )
LastCallDocEvent, NewRevisionDocEvent, IESG_SUBSTATE_TAGS, IESG_BALLOT_ACTIVE_STATES )
from ietf.doc.fields import select2_id_doc_name_json
from ietf.doc.utils import get_search_cache_key
from ietf.doc.utils import get_search_cache_key, augment_events_with_revision
from ietf.group.models import Group
from ietf.idindex.index import active_drafts_index_by_group
from ietf.name.models import DocTagName, DocTypeName, StreamName
@ -415,8 +415,40 @@ def docs_for_ad(request, name):
for d in results:
d.search_heading = ad_dashboard_group(d)
#
# Additional content showing docs with blocking positions by this ad
blocked_docs = []
if ad in get_active_ads():
possible_docs = Document.objects.filter(Q(states__type="draft-iesg",
states__slug__in=IESG_BALLOT_ACTIVE_STATES) |
Q(states__type="charter",
states__slug__in=("intrev", "iesgrev")) |
Q(states__type__in=("statchg", "conflrev"),
states__slug__in=("iesgeval", "defer")),
docevent__ballotpositiondocevent__pos__blocking=True,
docevent__ballotpositiondocevent__ad=ad)
for doc in possible_docs:
ballot = doc.active_ballot()
if not ballot:
continue
blocking_positions = [p for p in ballot.all_positions() if p.pos.blocking]
if not blocking_positions or not any( p.ad==ad for p in blocking_positions ):
continue
augment_events_with_revision(doc, blocking_positions)
doc.blocking_positions = blocking_positions
doc.ballot = ballot
blocked_docs.append(doc)
# latest first
if blocked_docs:
blocked_docs.sort(key=lambda d: min(p.time for p in d.blocking_positions if p.ad==ad), reverse=True)
return render(request, 'doc/drafts_for_ad.html', {
'form':form, 'docs':results, 'meta':meta, 'ad_name': ad.plain_name()
'form':form, 'docs':results, 'meta':meta, 'ad_name': ad.plain_name(), 'blocked_docs': blocked_docs
})
def drafts_in_last_call(request):

View file

@ -1,6 +1,7 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin staticfiles %}
{% load ietf_filters %}
{% block pagehead %}
<link rel="stylesheet" href="{% static "jquery.tablesorter/css/theme.bootstrap.min.css" %}">
@ -10,10 +11,49 @@
{% block content %}
{% origin %}
{% if blocked_docs %}
<h1>Blocking positions held by {{ ad_name }}</h1>
<table class="table table-condensed table-striped tablesorter">
<thead>
<tr>
<th>Document</th>
<th>Status</th>
<th>Responsible AD</th>
<th>Discusses</th>
</tr>
</thead>
<tbody>
{% for doc in blocked_docs %}
<tr>
<td>{{ doc.displayname_with_link }}</td>
{% include "doc/search/status_columns.html" %}
<td>{{ doc.ad|default:"" }}</td>
<td>
{% for p in doc.blocking_positions %}
{{ p.ad }}
({% if p.discuss_time %}{{ p.discuss_time|timesince_days }}{% endif %}
days ago{% if doc.get_state_url != "rfc" and p.rev != doc.rev %}
for -{{ p.rev }}{% endif %})<br>
{% if p.old_ad %}
</span>
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<h1>Documents for {{ ad_name }}</h1>
{% include "doc/search/search_results.html" %}
{% endblock %}
{% block morecss %}
.is-blocking {box-shadow: 0 0 0 0 #000000;}
{% endblock %}
{% block js %}
<script src="{% static "jquery.tablesorter/js/jquery.tablesorter.combined.min.js" %}"></script>
{% endblock %}