Skip to content

Commit

Permalink
feat: add group leadership list (#8135)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
rpcross and rjsparks authored Nov 15, 2024
1 parent 8bc51e3 commit b07d4db
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 1 deletion.
47 changes: 47 additions & 0 deletions ietf/group/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,53 @@ def test_stream_edit(self):
self.assertTrue(Role.objects.filter(name="delegate", group__acronym=stream_acronym, email__address="[email protected]"))


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):
def setUp(self):
super().setUp()
Expand Down
4 changes: 3 additions & 1 deletion ietf/group/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@


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'^groupmenu.json', views.group_menu_data, None, 'ietf.group.views.group_menu_data'),
url(r'^chartering/$', views.chartering_groups),
Expand Down
43 changes: 43 additions & 0 deletions ietf/group/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@


import copy
import csv
import datetime
import itertools
import math
Expand Down Expand Up @@ -437,6 +438,48 @@ def prepare_group_documents(request, group, clist):

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):
group = get_group_or_404(acronym, group_type)
kwargs = dict(acronym=group.acronym)
Expand Down
34 changes: 34 additions & 0 deletions ietf/templates/group/group_leadership.html
Original file line number Diff line number Diff line change
@@ -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 %}

0 comments on commit b07d4db

Please sign in to comment.