fix: Link to reviews from search status column (#3955)

* fix: Link to reviews from search status column

Fixes #3953

* fix: Make the notices about exceeded state times less obtrusive

* Fix tests
This commit is contained in:
Lars Eggert 2022-05-13 22:05:42 +03:00 committed by GitHub
parent b265bac1fe
commit 75a32d2210
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 28 deletions

View file

@ -204,18 +204,18 @@ def state_age_colored(doc):
goal1 = 14
goal2 = 28
if days > goal2:
class_name = "badge bg-danger"
class_name = "text-danger"
elif days > goal1:
class_name = "badge bg-warning"
class_name = "text-warning"
else:
class_name = "badge bg-success"
# don't show a badge when things are in the green; clutters display
# class_name = "text-success"
return ""
if days > goal1:
title = ' title="Goal is <%d days"' % (goal1,)
title = ' title="In state for %d day%s; goal is <%d days."' % (days, 's' if days != 1 else '', goal1,)
else:
title = ''
return mark_safe('<span class="%s"%s>for %d day%s</span>' % (
class_name, title, days,
's' if days != 1 else ''))
return mark_safe('<span class="%s"><i class="bi bi-hourglass-split"%s></i></span>' % (class_name, title))
else:
return ""
@ -233,4 +233,4 @@ def auth48_alert_badge(doc):
if rfced_state == 'auth48':
return mark_safe('<span class="badge bg-info" title="AUTH48">AUTH48</span>')
return ''
return ''

View file

@ -719,20 +719,20 @@ def action_holder_badge(action_holder):
''
>>> action_holder_badge(DocumentActionHolderFactory(time_added=datetime.datetime.now() - datetime.timedelta(days=16)))
'<span class="badge bg-danger" title="Goal is &lt;15 days">for 16 days</span>'
'<span class="text-danger"><i class="bi bi-hourglass-split" title="In state for 16 days; goal is &lt;15 days."></i></span>'
>>> action_holder_badge(DocumentActionHolderFactory(time_added=datetime.datetime.now() - datetime.timedelta(days=30)))
'<span class="badge bg-danger" title="Goal is &lt;15 days">for 30 days</span>'
'<span class="text-danger"><i class="bi bi-hourglass-split" title="In state for 30 days; goal is &lt;15 days."></i></span>'
>>> settings.DOC_ACTION_HOLDER_AGE_LIMIT_DAYS = old_limit
"""
age_limit = settings.DOC_ACTION_HOLDER_AGE_LIMIT_DAYS
age = (datetime.datetime.now() - action_holder.time_added).days
if age > age_limit:
return mark_safe('<span class="badge bg-danger" title="Goal is &lt;%d days">for %d day%s</span>' % (
age_limit,
return mark_safe('<span class="text-danger"><i class="bi bi-hourglass-split" title="In state for %d day%s; goal is &lt;%d days."></i></span>' % (
age,
's' if age != 1 else ''))
's' if age != 1 else '',
age_limit))
else:
return '' # no alert needed

View file

@ -356,8 +356,8 @@ class SearchTests(TestCase):
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('td.doc')),3)
self.assertEqual(q('td.status span.badge.bg-warning').text(),"for 15 days")
self.assertEqual(q('td.status span.badge.bg-danger').text(),"for 29 days")
self.assertTrue(q('td.status span.text-warning *[title*="%s"]' % "for 15 days"))
self.assertTrue(q('td.status span.text-danger *[title*="%s"]' % "for 29 days"))
for ah in [draft.action_holders.first() for draft in drafts]:
self.assertContains(r, escape(ah.name))
@ -1297,8 +1297,8 @@ Man Expires September 22, 2015 [Page 3]
@staticmethod
def _pyquery_select_action_holder_string(q, s):
"""Helper to use PyQuery to find an action holder in the draft HTML"""
# selector grabs the action holders heading and finds siblings with a div containing the search string
return q('th:contains("Action Holder") ~ td>div:contains("%s")' % s)
# selector grabs the action holders heading and finds siblings with a div containing the search string (also in any title attribute)
return q('th:contains("Action Holder") ~ td>div:contains("%s"), th:contains("Action Holder") ~ td>div *[title*="%s"]' % (s, s))
@mock.patch.object(Document, 'action_holders_enabled', return_value=False, new_callable=mock.PropertyMock)
def test_document_draft_hides_action_holders(self, mock_method):
@ -2852,4 +2852,4 @@ class PdfizedTests(TestCase):
self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}'))
for ext in ('pdf','txt','html','anythingatall'):
self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}',ext=ext))
self.should_404(dict(name=rfc.name,rev='02'))
self.should_404(dict(name=rfc.name,rev='02'))

View file

@ -9,6 +9,7 @@ from ietf.doc.models import Document, DocAlias, RelatedDocument, DocEvent, Telec
from ietf.doc.expire import expirable_drafts
from ietf.doc.utils import augment_docs_and_user_with_user_info
from ietf.meeting.models import SessionPresentation, Meeting, Session
from ietf.review.utils import review_assignments_to_list_for_docs
def wrap_value(v):
return lambda: v
@ -112,7 +113,7 @@ def fill_in_document_table_attributes(docs, have_telechat_date=False):
if d.get_state_slug() != "rfc":
d.milestones = [ m for (t, s, v, m) in sorted(((m.time, m.state.slug, m.desc, m) for m in d.groupmilestone_set.all() if m.state_id == "active")) ]
d.reviewed_by_teams = sorted(set(r.team.acronym for r in d.reviewrequest_set.filter(state__in=["assigned","accepted","part-completed","completed"]).distinct().select_related('team')))
d.review_assignments = review_assignments_to_list_for_docs([d]).get(d.name, [])
e = d.latest_event_cache.get('started_iesg_process', None)
d.balloting_started = e.time if e else datetime.datetime.min
@ -255,4 +256,4 @@ def prepare_document_table(request, docs, query=None, max_results=200):
else:
d["sort"] = h["key"]
return (docs, meta)
return (docs, meta)

View file

@ -46,14 +46,27 @@
<br>
Due date: {{ doc.duedate }}
{% endif %}
{% if doc.reviewed_by_teams %}
{% if doc.review_assignments %}
<br>
Review{{ doc.reviewed_by_teams|pluralize }}:
{% spaceless %}
{% for acronym in doc.reviewed_by_teams %}
<a href="{% url "ietf.group.views.group_home" acronym=acronym %}">{{ acronym }}</a>{% if not forloop.last %},{% endif %}
{% endfor %}
{% endspaceless %}
Review{{ doc.review_assignments|pluralize }}:
{% for review_assignment in doc.review_assignments %}
{% if review_assignment.state_id == "completed" or review_assignment.state_id == "part-completed" %}
<span title="{{ review_assignment.review_request.team.acronym|upper }} {{ review_assignment.review_request.type.name }} review {% if review_assignment.reviewed_rev and review_assignment.reviewed_rev != current_rev or review_assignment.review_request.doc.name != current_doc_name %}of {% if review_assignment.review_request.doc.name != current_doc_name %}{{ review_assignment.review_request.doc.name }}{% endif %}-{{ review_assignment.reviewed_rev }}{% endif %}: {{review_assignment.result}}"
class="badge {% if review_assignment.result.name|slice:5|slugify == 'ready' %}bg-success{% elif review_assignment.result.name|slice:9|slugify == 'not-ready' %}bg-danger{% elif review_assignment.result.name|slice:10|slugify == 'has-issues' %}bg-warning{% else %}bg-info{% endif %}">
{% else %}
<span title="Incomplete, due {{ review_assignment.review_request.deadline|date:"Y-m-d" }}" class="badge bg-secondary">
{% endif %}
<a class="text-reset text-decoration-none" href="{% if review_assignment.review %}{% url 'ietf.doc.views_doc.document_main' review_assignment.review.name %}{% else %}{% url 'ietf.doc.views_review.review_request' review_assignment.review_request.doc.name review_assignment.review_request.pk %}{% endif %}">
{{ review_assignment.review_request.team.acronym }}
{% if review_assignment.review_request.type|slugify == "last-call" %}
LC
{% elif review_assignment.review_request.type|slugify != "telechat" %}
{{ review_assignment.review_request.type }}
{% endif %}
</a>
</span>
{% endfor %}
{% endif %}
{% for m in doc.milestones %}
{% if forloop.first %}<br>{% endif %}
@ -79,4 +92,4 @@
<span class="text-muted">Updated by {{ doc.updated_by_list|join:", "|urlize_ietf_docs }}</span>
{% endif %}
{% endif %}
</td>
</td>