feat: add group leadership list (#8135)
* feat: add Group Leadership list * fix: only offer export to staff * fix: fix export button conditional * fix: improve tests. black format --------- Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
This commit is contained in:
parent
8bc51e3cc2
commit
b07d4dbebc
|
@ -65,6 +65,53 @@ class StreamTests(TestCase):
|
||||||
self.assertTrue(Role.objects.filter(name="delegate", group__acronym=stream_acronym, email__address="ad2@ietf.org"))
|
self.assertTrue(Role.objects.filter(name="delegate", group__acronym=stream_acronym, email__address="ad2@ietf.org"))
|
||||||
|
|
||||||
|
|
||||||
|
class GroupLeadershipTests(TestCase):
|
||||||
|
def test_leadership_wg(self):
|
||||||
|
# setup various group states
|
||||||
|
bof_role = RoleFactory(
|
||||||
|
group__type_id="wg", group__state_id="bof", name_id="chair"
|
||||||
|
)
|
||||||
|
proposed_role = RoleFactory(
|
||||||
|
group__type_id="wg", group__state_id="proposed", name_id="chair"
|
||||||
|
)
|
||||||
|
active_role = RoleFactory(
|
||||||
|
group__type_id="wg", group__state_id="active", name_id="chair"
|
||||||
|
)
|
||||||
|
conclude_role = RoleFactory(
|
||||||
|
group__type_id="wg", group__state_id="conclude", name_id="chair"
|
||||||
|
)
|
||||||
|
url = urlreverse(
|
||||||
|
"ietf.group.views.group_leadership", kwargs={"group_type": "wg"}
|
||||||
|
)
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertContains(r, "Group Leadership")
|
||||||
|
self.assertContains(r, bof_role.person.last_name())
|
||||||
|
self.assertContains(r, proposed_role.person.last_name())
|
||||||
|
self.assertContains(r, active_role.person.last_name())
|
||||||
|
self.assertNotContains(r, conclude_role.person.last_name())
|
||||||
|
|
||||||
|
def test_leadership_wg_csv(self):
|
||||||
|
url = urlreverse(
|
||||||
|
"ietf.group.views.group_leadership_csv", kwargs={"group_type": "wg"}
|
||||||
|
)
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertEqual(r["Content-Type"], "text/csv")
|
||||||
|
self.assertContains(r, "Chairman, Sops")
|
||||||
|
|
||||||
|
def test_leadership_rg(self):
|
||||||
|
role = RoleFactory(group__type_id="rg", name_id="chair")
|
||||||
|
url = urlreverse(
|
||||||
|
"ietf.group.views.group_leadership", kwargs={"group_type": "rg"}
|
||||||
|
)
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertContains(r, "Group Leadership")
|
||||||
|
self.assertContains(r, role.person.last_name())
|
||||||
|
self.assertNotContains(r, "Chairman, Sops")
|
||||||
|
|
||||||
|
|
||||||
class GroupStatsTests(TestCase):
|
class GroupStatsTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
|
|
|
@ -57,7 +57,9 @@ info_detail_urls = [
|
||||||
|
|
||||||
|
|
||||||
group_urls = [
|
group_urls = [
|
||||||
url(r'^$', views.active_groups),
|
url(r'^$', views.active_groups),
|
||||||
|
url(r'^leadership/(?P<group_type>(wg|rg))/$', views.group_leadership),
|
||||||
|
url(r'^leadership/(?P<group_type>(wg|rg))/csv/$', views.group_leadership_csv),
|
||||||
url(r'^groupstats.json', views.group_stats_data, None, 'ietf.group.views.group_stats_data'),
|
url(r'^groupstats.json', views.group_stats_data, None, 'ietf.group.views.group_stats_data'),
|
||||||
url(r'^groupmenu.json', views.group_menu_data, None, 'ietf.group.views.group_menu_data'),
|
url(r'^groupmenu.json', views.group_menu_data, None, 'ietf.group.views.group_menu_data'),
|
||||||
url(r'^chartering/$', views.chartering_groups),
|
url(r'^chartering/$', views.chartering_groups),
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import csv
|
||||||
import datetime
|
import datetime
|
||||||
import itertools
|
import itertools
|
||||||
import math
|
import math
|
||||||
|
@ -437,6 +438,48 @@ def prepare_group_documents(request, group, clist):
|
||||||
|
|
||||||
return docs, meta, docs_related, meta_related
|
return docs, meta, docs_related, meta_related
|
||||||
|
|
||||||
|
|
||||||
|
def get_leadership(group_type):
|
||||||
|
people = Person.objects.filter(
|
||||||
|
role__name__slug="chair",
|
||||||
|
role__group__type=group_type,
|
||||||
|
role__group__state__slug__in=("active", "bof", "proposed"),
|
||||||
|
).distinct()
|
||||||
|
leaders = []
|
||||||
|
for person in people:
|
||||||
|
parts = person.name_parts()
|
||||||
|
groups = [
|
||||||
|
r.group.acronym
|
||||||
|
for r in person.role_set.filter(
|
||||||
|
name__slug="chair",
|
||||||
|
group__type=group_type,
|
||||||
|
group__state__slug__in=("active", "bof", "proposed"),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
entry = {"name": "%s, %s" % (parts[3], parts[1]), "groups": ", ".join(groups)}
|
||||||
|
leaders.append(entry)
|
||||||
|
return sorted(leaders, key=lambda a: a["name"])
|
||||||
|
|
||||||
|
|
||||||
|
def group_leadership(request, group_type=None):
|
||||||
|
context = {}
|
||||||
|
context["leaders"] = get_leadership(group_type)
|
||||||
|
context["group_type"] = group_type
|
||||||
|
return render(request, "group/group_leadership.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
def group_leadership_csv(request, group_type=None):
|
||||||
|
leaders = get_leadership(group_type)
|
||||||
|
response = HttpResponse(content_type="text/csv")
|
||||||
|
response["Content-Disposition"] = (
|
||||||
|
f'attachment; filename="group_leadership_{group_type}.csv"'
|
||||||
|
)
|
||||||
|
writer = csv.writer(response, dialect=csv.excel, delimiter=str(","))
|
||||||
|
writer.writerow(["Name", "Groups"])
|
||||||
|
for leader in leaders:
|
||||||
|
writer.writerow([leader["name"], leader["groups"]])
|
||||||
|
return response
|
||||||
|
|
||||||
def group_home(request, acronym, group_type=None):
|
def group_home(request, acronym, group_type=None):
|
||||||
group = get_group_or_404(acronym, group_type)
|
group = get_group_or_404(acronym, group_type)
|
||||||
kwargs = dict(acronym=group.acronym)
|
kwargs = dict(acronym=group.acronym)
|
||||||
|
|
34
ietf/templates/group/group_leadership.html
Normal file
34
ietf/templates/group/group_leadership.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{# Copyright The IETF Trust 2024, All Rights Reserved #}
|
||||||
|
{% load origin static person_filters ietf_filters %}
|
||||||
|
{% block pagehead %}
|
||||||
|
<link rel="stylesheet" href="{% static 'ietf/css/list.css' %}">
|
||||||
|
{% endblock %}
|
||||||
|
{% block title %}Group Leadership{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% origin %}
|
||||||
|
<h1>Group Leadership ({{ group_type }})</h1>
|
||||||
|
{% if user|has_role:"Secretariat" %}
|
||||||
|
<div class="text-end">
|
||||||
|
<a class="btn btn-primary" href="{% url 'ietf.group.views.group_leadership_csv' group_type=group_type %}">
|
||||||
|
<i class="bi bi-file-ruled"></i> Export as CSV
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<table class="table table-sm table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Leader</th>
|
||||||
|
<th scope="col">Groups</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for leader in leaders %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ leader.name }}</td>
|
||||||
|
<td>{{ leader.groups }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
Loading…
Reference in a new issue