Skip to content

Commit

Permalink
feat: Reclassify nomcom feedback (ietf-tools#4669)
Browse files Browse the repository at this point in the history
- There's a new `Chair/Advisor Tasks` menu item `Reclassify feedback`.

- I overloaded `view_feedback*` URLs with a `?reclassify` parameter.

- This adds a checkbox to each feedback message, and a `Reclassify` button
at the bottom of each feedback page.

- "Reclassifying" basically de-classifies the feedback, and punts it back
to the "Pending emails" view for reclassification.

- If a feedback has been applied to multiple nominees, declassifying it
from one nominee removes it from all.
  • Loading branch information
pselkirk committed Jul 21, 2023
1 parent 2ede70e commit 3a6d70c
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 18 deletions.
64 changes: 58 additions & 6 deletions ietf/nomcom/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright The IETF Trust 2012-2020, All Rights Reserved
# Copyright The IETF Trust 2012-2023, All Rights Reserved
# -*- coding: utf-8 -*-


Expand Down Expand Up @@ -828,6 +828,8 @@ def nominee_staterank(nominee):
topic_feedback.append( (ft.name,count,newflag) )
topics_feedback.append ( {'topic':topic, 'feedback':topic_feedback} )

reclassify = request.GET.get('reclassify') is not None

return render(request, 'nomcom/view_feedback.html',
{'year': year,
'selected': 'view_feedback',
Expand All @@ -838,7 +840,9 @@ def nominee_staterank(nominee):
'topics_feedback': topics_feedback,
'independent_feedback': independent_feedback,
'nominees_feedback': nominees_feedback,
'nomcom': nomcom})
'nomcom': nomcom,
'reclassify': reclassify,
})


@role_required("Nomcom Chair", "Nomcom Advisor")
Expand Down Expand Up @@ -956,15 +960,31 @@ def view_feedback_unrelated(request, year):
feedback_types.append({'ft': ft,
'feedback': ft.feedback_set.get_by_nomcom(nomcom)})

reclassify = request.GET.get('reclassify') is not None
is_chair = nomcom.group.has_role(request.user, "chair")
if reclassify and is_chair and request.method == 'POST':
action = request.POST.get('action')
feedback_to_modify = request.POST.getlist('selected')
if feedback_to_modify:
for ft in feedback_types:
feedback = ft['feedback'].filter(id__in=feedback_to_modify)
feedback.update(type=None)
messages.success(request, 'The selected feedback has been de-classified. Please reclassify it in the Pending emails tab.')
else:
messages.warning(request, "Please select some feedback to work with")

return render(request, 'nomcom/view_feedback_unrelated.html',
{'year': year,
'selected': 'view_feedback',
'feedback_types': feedback_types,
'nomcom': nomcom})
'nomcom': nomcom,
'is_chair': is_chair,
'reclassify': reclassify,
})

@role_required("Nomcom")
@nomcom_private_key_required
def view_feedback_topic(request, year, topic_id):
def view_feedback_topic(request, year, topic_id, reflassify=False):
nomcom = get_nomcom_by_year(year)
topic = get_object_or_404(Topic, id=topic_id)
feedback_types = FeedbackTypeName.objects.filter(slug__in=['comment',])
Expand All @@ -976,13 +996,28 @@ def view_feedback_topic(request, year, topic_id):
else:
TopicFeedbackLastSeen.objects.create(reviewer=request.user.person,topic=topic)

reclassify = request.GET.get('reclassify') is not None
is_chair = nomcom.group.has_role(request.user, "chair")
if reclassify and is_chair and request.method == 'POST':
action = request.POST.get('action')
feedback_to_modify = request.POST.getlist('selected')
if feedback_to_modify:
feedback = topic.feedback_set.filter(id__in=feedback_to_modify)
feedback.update(type=None)
messages.success(request, 'The selected feedback has been de-classified. Please reclassify it in the Pending emails tab.')
else:
messages.warning(request, "Please select some feedback to work with")

return render(request, 'nomcom/view_feedback_topic.html',
{'year': year,
'selected': 'view_feedback',
'topic': topic,
'feedback_types': feedback_types,
'last_seen_time' : last_seen_time,
'nomcom': nomcom})
'nomcom': nomcom,
'is_chair': is_chair,
'reclassify': reclassify,
})

@role_required("Nomcom")
@nomcom_private_key_required
Expand All @@ -998,13 +1033,30 @@ def view_feedback_nominee(request, year, nominee_id):
else:
FeedbackLastSeen.objects.create(reviewer=request.user.person,nominee=nominee)

reclassify = request.GET.get('reclassify') is not None
is_chair = nomcom.group.has_role(request.user, "chair")
if reclassify and is_chair and request.method == 'POST':
action = request.POST.get('action')
feedback_to_modify = request.POST.getlist('selected')
if feedback_to_modify:
feedback = nominee.feedback_set.filter(id__in=feedback_to_modify)
feedback.update(type=None)
for fb in feedback:
fb.nominees.clear()
messages.success(request, 'The selected feedback has been de-classified. Please reclassify it in the Pending emails tab.')
else:
messages.warning(request, "Please select some feedback to work with")

return render(request, 'nomcom/view_feedback_nominee.html',
{'year': year,
'selected': 'view_feedback',
'nominee': nominee,
'feedback_types': feedback_types,
'last_seen_time' : last_seen_time,
'nomcom': nomcom})
'nomcom': nomcom,
'is_chair': is_chair,
'reclassify': reclassify,
})


@role_required("Nomcom Chair", "Nomcom Advisor")
Expand Down
5 changes: 4 additions & 1 deletion ietf/templates/nomcom/nomcom_private_base.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2015-2023, All Rights Reserved #}
{% load origin %}
{% load nomcom_tags %}
{% load ietf_filters %}
Expand Down Expand Up @@ -84,6 +84,9 @@ <h1>
<li class="dropdown-item">
<a href="{% url "ietf.nomcom.views.private_merge_nominee" year %}">Merge nominee records</a>
</li>
<li class="dropdown-item">
<a href="{% url "ietf.nomcom.views.view_feedback" year %}?reclassify">Reclassify feedback</a>
</li>
{% endif %}
<li role="presentation" class="dropdown-header">NomCom configuration</li>
<li class="dropdown-item">
Expand Down
10 changes: 5 additions & 5 deletions ietf/templates/nomcom/view_feedback.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ <h3 class="mt-5" id="declined">Declined each nominated position</h3>
{% for fb_dict in staterank.list %}
<tr>
<td>
<a href="{% url 'ietf.nomcom.views.view_feedback_nominee' year=year nominee_id=fb_dict.nominee.id %}">
<a href="{% url 'ietf.nomcom.views.view_feedback_nominee' year=year nominee_id=fb_dict.nominee.id %}{% if reclassify %}?reclassify{% endif %}">
{{ fb_dict.nominee.person.first_name }}
</a>
</td>
<td>
<a href="{% url 'ietf.nomcom.views.view_feedback_nominee' year=year nominee_id=fb_dict.nominee.id %}">
<a href="{% url 'ietf.nomcom.views.view_feedback_nominee' year=year nominee_id=fb_dict.nominee.id %}{% if reclassify %}?reclassify{% endif %}">
{{ fb_dict.nominee.person.last_name }}
</a>
</td>
<td>
<a href="{% url 'ietf.nomcom.views.view_feedback_nominee' year=year nominee_id=fb_dict.nominee.id %}">
<a href="{% url 'ietf.nomcom.views.view_feedback_nominee' year=year nominee_id=fb_dict.nominee.id %}{% if reclassify %}?reclassify{% endif %}">
{{ fb_dict.nominee.email.address }}
</a>
</td>
Expand Down Expand Up @@ -80,7 +80,7 @@ <h2 class="mt-5" id="topics">Feedback related to topics</h2>
{% for fb_dict in topics_feedback %}
<tr>
<td>
<a href="{% url 'ietf.nomcom.views.view_feedback_topic' year=year topic_id=fb_dict.topic.id %}">
<a href="{% url 'ietf.nomcom.views.view_feedback_topic' year=year topic_id=fb_dict.topic.id %}{% if reclassify %}?reclassify{% endif %}">
{{ fb_dict.topic.subject }}
</a>
</td>
Expand All @@ -106,7 +106,7 @@ <h2 class="mt-5" id="misc">Feedback not related to Nominees</h2>
<tbody>
<tr>
<td>
<a href="{% url 'ietf.nomcom.views.view_feedback_unrelated' year %}">View feedback not related to nominees</a>
<a href="{% url 'ietf.nomcom.views.view_feedback_unrelated' year %}{% if reclassify %}?reclassify{% endif %}">View feedback not related to nominees</a>
</td>
{% for count in independent_feedback %}<td>{{ count }}</td>{% endfor %}
</tr>
Expand Down
24 changes: 22 additions & 2 deletions ietf/templates/nomcom/view_feedback_nominee.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "nomcom/nomcom_private_base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2015-2023, All Rights Reserved #}
{% load origin %}
{% load nomcom_tags textfilters %}
{% block subtitle %}- View feedback about {{ nominee.email.person.name }}{% endblock %}
Expand All @@ -22,9 +22,24 @@ <h2>Feedback about {{ nominee }}</h2>
{% for ft in feedback_types %}
<div role="tabpanel" class="tab-pane {% if forloop.first %} active{% endif %}"
id="{{ ft.slug }}">
{% if is_chair and nomcom.group.state_id == 'active' and reclassify %}
<form class="form-inline" id="batch-action-form" method="post">
{% csrf_token %}
{% endif %}
{% for feedback in nominee.feedback_set.all %}
{% if feedback.type.slug == ft.slug %}
<dl class="row">
{% if is_chair and nomcom.group.state_id == 'active' and reclassify %}
<dt class="col-sm-2">
<input class="batch-select form-check-input"
type="checkbox"
value="{{ feedback.id }}"
id="id-{{ feedback.id }}"
aria-label="feedback.id"
name="selected">
</dt>
<dd/>
{% endif %}
<dt class="col-sm-2">
{% if feedback.time > last_seen_time %}<span class="badge rounded-pill bg-success">New</span>{% endif %}
From
Expand Down Expand Up @@ -85,11 +100,16 @@ <h2>Feedback about {{ nominee }}</h2>
{% if not forloop.last %}<hr>{% endif %}
{% endif %}
{% endfor %}
{% if is_chair and nomcom.group.state_id == 'active' and reclassify and nominee.feedback_set.all|length > 0 %}
<p><button class="btn btn-warning" type="submit" title="Run action">
Reclassify
</button></p>
{% endif %}
</div>
{% endfor %}
</div>
<a class="btn btn-secondary"
href="{% url 'ietf.nomcom.views.view_feedback' year %}">Back</a>
href="{% url 'ietf.nomcom.views.view_feedback' year %}{% if reclassify %}?reclassify{% endif %}">Back</a>
{% endblock %}
{% block js %}
<script>
Expand Down
24 changes: 22 additions & 2 deletions ietf/templates/nomcom/view_feedback_topic.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "nomcom/nomcom_private_base.html" %}
{# Copyright The IETF Trust 2017, All Rights Reserved #}
{# Copyright The IETF Trust 2017-2023, All Rights Reserved #}
{% load origin %}
{% load nomcom_tags textfilters %}
{% block subtitle %}- View feedback about {{ topic.subject }}{% endblock %}
Expand All @@ -22,9 +22,24 @@ <h2>Feedback about {{ topic.subject }}</h2>
{% for ft in feedback_types %}
<div role="tabpanel" class="tab-pane {% if forloop.first %} active{% endif %}"
id="{{ ft.slug }}">
{% if is_chair and nomcom.group.state_id == 'active' and reclassify %}
<form class="form-inline" id="batch-action-form" method="post">
{% csrf_token %}
{% endif %}
{% for feedback in topic.feedback_set.all %}
{% if feedback.type.slug == ft.slug %}
<dl class="row">
{% if is_chair and nomcom.group.state_id == 'active' and reclassify %}
<dt class="col-sm-2">
<input class="batch-select form-check-input"
type="checkbox"
value="{{ feedback.id }}"
id="id-{{ feedback.id }}"
aria-label="feedback.id"
name="selected">
</dt>
<dd/>
{% endif %}
<dt class="col-sm-2">
{% if feedback.time > last_seen_time %}<span class="badge rounded-pill bg-success">New</span>{% endif %}
From
Expand All @@ -48,11 +63,16 @@ <h2>Feedback about {{ topic.subject }}</h2>
{% if not forloop.last %}<hr>{% endif %}
{% endif %}
{% endfor %}
{% if is_chair and nomcom.group.state_id == 'active' and reclassify and topic.feedback_set.all|length > 0 %}
<p><button class="btn btn-warning" type="submit" title="Run action">
Reclassify
</button></p>
{% endif %}
</div>
{% endfor %}
</div>
<a class="btn btn-secondary"
href="{% url 'ietf.nomcom.views.view_feedback' year %}">Back</a>
href="{% url 'ietf.nomcom.views.view_feedback' year %}{% if reclassify %}?reclassify{% endif %}">Back</a>
{% endblock %}
{% block js %}
<script>
Expand Down
32 changes: 30 additions & 2 deletions ietf/templates/nomcom/view_feedback_unrelated.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "nomcom/nomcom_private_base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2015-2023, All Rights Reserved #}
{% load origin %}
{% load nomcom_tags textfilters %}
{% block subtitle %}- View unrelated feedback{% endblock %}
Expand All @@ -22,11 +22,26 @@ <h2>Feedback not related to nominees</h2>
{% for ft in feedback_types %}
<div role="tabpanel" class="tab-pane {% if forloop.first %} active{% endif %}"
id="{{ ft.ft.slug }}">
{% if is_chair and nomcom.group.state_id == 'active' and reclassify %}
<form class="form-inline" id="batch-action-form" method="post">
{% csrf_token %}
{% endif %}
{% for feedback in ft.feedback %}
{% if not forloop.first %}
<hr>
{% endif %}
<dl class="row">
{% if is_chair and nomcom.group.state_id == 'active' and reclassify %}
<dt class="col-sm-2">
<input class="batch-select form-check-input"
type="checkbox"
value="{{ feedback.id }}"
id="id-{{ feedback.id }}"
aria-label="feedback.id"
name="selected">
</dt>
<dd/>
{% endif %}
<dt class="col-sm-2">
From
</dt>
Expand All @@ -39,6 +54,14 @@ <h2>Feedback not related to nominees</h2>
<dd class="col-sm-10">
{{ feedback.time|date:"Y-m-d" }}
</dd>
{% if feedback.subject %}
<dt class="col-sm-2">
Subject
</dt>
<dd class="col-sm-10">
{{ feedback.subject }}
</dd>
{% endif %}
<dt class="col-sm-2">
Feedback
</dt>
Expand All @@ -47,12 +70,17 @@ <h2>Feedback not related to nominees</h2>
</dd>
</dl>
{% endfor %}
{% if is_chair and nomcom.group.state_id == 'active' and reclassify and ft.feedback|length > 0 %}
<p><button class="btn btn-warning" type="submit" title="Run action">
Reclassify
</button></p>
{% endif %}
</div>
{% endfor %}
</div>
<p>
<a class="btn btn-secondary"
href="{% url 'ietf.nomcom.views.view_feedback' year %}">Back</a>
href="{% url 'ietf.nomcom.views.view_feedback' year %}{% if reclassify %}?reclassify{% endif %}">Back</a>
</p>
{% endblock %}
{% block js %}
Expand Down

0 comments on commit 3a6d70c

Please sign in to comment.