Skip to content
Snippets Groups Projects
Commit 4a1ace36 authored by Roang's avatar Roang
Browse files

Add teams list view

parent 3060d66c
Branches
Tags
No related merge requests found
......@@ -1443,6 +1443,30 @@ msgstr "Self-organized Session löschen"
msgid "SoS__delete__introduction"
msgstr "Hiermit wird diese Self-organized Session gelöscht. Wenn du sie nur unsichtbar machen möchtest gibt es weiter oben die Möglichkeit die Self-organized Session zurückzunehmen!"
# use translation from core
msgid "Teams"
msgstr ""
msgid "Team__name"
msgstr "Name"
# Use translation from core
msgid "Team__require_staff"
msgstr ""
msgid "Team__member_count"
msgstr "Mitgliederanzahl"
# Use translation from core
msgid "TeamMember__can_manage"
msgstr ""
msgid "Team__list__not_a_member"
msgstr "Kein Mitglied"
msgid "Team__create__button"
msgstr "Team anlegen"
msgid "data_table__info__no_data"
msgstr "Bisher keine Daten vorhanden"
......
......@@ -1448,6 +1448,30 @@ msgstr "Delete self-organized session"
msgid "SoS__delete__introduction"
msgstr "This removes this self-organized session. If you only want to make it invisible, there is an option above to recall the self-organized session above!"
# use translation from core
msgid "Teams"
msgstr ""
msgid "Team__name"
msgstr "name"
# Use translation from core
msgid "Team__require_staff"
msgstr ""
msgid "Team__member_count"
msgstr "members count"
# Use translation from core
msgid "TeamMember__can_manage"
msgstr ""
msgid "Team__list__not_a_member"
msgstr "Not a member"
msgid "Team__create__button"
msgstr "Create team"
msgid "data_table__info__no_data"
msgstr "No data available in table"
......
{% extends "backoffice/base.html" %}
{% load rules %}
{% load i18n %}
{% load static %}
{% block htmlhead %}
<link rel="stylesheet"
href="{% static 'vendor/datatables/datatables.min.css' %}">
{% endblock htmlhead %}
{% block scripts %}
<script src="{% static 'vendor/datatables/datatables.min.js' %}"></script>
<script nonce="{{ request.csp_nonce }}">{% include "backoffice/components/list_script.js" with table_id='teams' %}</script>
{% endblock scripts %}
{% block content %}
{% has_perm 'books.add_book' request.user as can_add_team %}
<div class="card">
<div class="card-header">
<span class="text-muted">{% trans "Teams" %}:</span> {{ mode_display }}
</div>
<div class="card-body">
{% if filter_tag is not None %}
<div class="alert alert-info">
{% trans "assemblies_filtered_tag" %}: <strong>{{ filter_tag }}</strong>
</div>
{% endif %}
<table class="table table-sm" id="teams">
<thead>
<tr>
<th>{% trans "Team__name" %}</th>
<th>{% trans "Team__require_staff" %}</th>
{% if user.is_staff %}
<th>{% trans "Team__member_count" %}</th>
{% endif %}
<th>{% trans "TeamMember__can_manage" %}</th>
</tr>
</thead>
<tbody>
{% for team in object_list %}
<tr>
<td>
<a href="{% url 'backoffice:team' uuid=team.uuid %}">{{ team.name }}</a>
</td>
<td>{{ team.require_staff|yesno }}</td>
{% if user.is_staff %}<td>{{ team.members_count }}</td>{% endif %}
<td>
{% if user.id in team.member_list %}
{{ team.can_manage|yesno }}
{% else %}
{% trans "Team__list__not_a_member" %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if can_add_team %}
<div class="card-footer text-end">
<a href="{% url 'backoffice:team-create' %}"
class="btn btn-primary btn-sm">{% trans "Team__create__button" %}</a>
</div>
{% endif %}
</div>
{% endblock content %}
......@@ -2,5 +2,6 @@ from .base import * # noqa: F401, F403, I001
from .assemblies import * # noqa: F401, F403
from .auth import * # noqa: F401, F403
from .invitations import * # noqa: F401, F403
from .teams import * # noqa: F401, F403
__all__ = ('*',) # noqa: F405
from backoffice.tests.teams.teams import TeamListViewTestCase
__all__ = ('TeamListViewTestCase',)
from unittest.mock import patch
from django.urls import reverse
from django.utils.translation import activate
from django.utils.translation import gettext as _
from core.models import (
Team,
TeamMember,
)
from core.tests.mock import mocktrans
from backoffice.tests.base import BackOfficeTestCase
class TeamListViewTestCase(BackOfficeTestCase):
def setUp(self):
super().setUp()
self.teams = {}
for team in ['team1', 'team2', 'team3']:
self.teams[team] = Team.objects.create(
name=team,
conference=self.conf,
)
self.teams['team1'].require_staff = True
TeamMember.objects.create(team=self.teams['team1'], user=self.staff)
TeamMember.objects.create(team=self.teams['team1'], user=self.admin, can_manage=True)
TeamMember.objects.create(team=self.teams['team2'], user=self.staff, can_manage=True)
TeamMember.objects.create(team=self.teams['team3'], user=self.admin)
def test_team_list_unauthenticated(self):
activate('en')
response = self.client.get(reverse('backoffice:teams'))
self.assertRedirects(response, reverse('backoffice:login') + '?next=' + reverse('backoffice:teams'))
def test_team_list_admin(self):
self.client.force_login(self.admin)
with patch('backoffice.views.teams.teams._', mocktrans):
response = self.client.get(reverse('backoffice:teams'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'backoffice/teams/list.html')
self.assertQuerySetEqual(Team.objects.all().order_by('name'), response.context['teams'])
for team in self.teams:
self.assertContains(response, team)
self.assertContains(response, _('Team__create__button'))
def test_team_list_staff(self):
self.client.force_login(self.staff)
response = self.client.get(reverse('backoffice:teams'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'backoffice/teams/list.html')
self.assertQuerySetEqual(Team.objects.all().order_by('name'), response.context['teams'])
for team in self.teams:
self.assertContains(response, team)
self.assertNotContains(response, _('Team__create__button'))
def test_team_list_user(self):
self.client.force_login(self.user)
with patch('backoffice.views.teams.teams._', mocktrans):
response = self.client.get(reverse('backoffice:teams'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'backoffice/teams/list.html')
self.assertQuerySetEqual(Team.objects.filter(require_staff=False).order_by('name'), response.context['teams'])
for team in self.teams:
self.assertContains(response, team)
self.assertNotContains(response, _('Team__create__button'))
......@@ -13,6 +13,7 @@ from backoffice.views import (
profile,
projects,
schedules,
teams,
vouchers,
wiki,
)
......@@ -152,6 +153,7 @@ urlpatterns = [
path('self-organized/sessions/<uuid:pk>/delete', events.SoSDeleteView.as_view(), name='sos-delete'),
path('sos/new', RedirectView.as_view(pattern_name='backoffice:sos-create')),
path('sos/<uuid:pk>/', RedirectView.as_view(pattern_name='backoffice:sos-edit')),
path('teams', teams.TeamListView.as_view(), name='teams'),
path('vouchers', vouchers.VouchersView.as_view(), name='vouchers'),
path('_boom', misc.BoomView.as_view()),
]
from backoffice.views.teams.teams import (
TeamListView,
)
__all__ = [
'TeamListView',
]
from typing import Any
from django.contrib.postgres.aggregates.general import ArrayAgg
from django.db.models import BooleanField, Case, Count, When
from django.db.models.query import Q, QuerySet
from django.views.generic import ListView
from core.models import ConferenceMember, Team
from backoffice.views.mixins import ConferenceRuleLoginRequiredMixin
class TeamListView(ConferenceRuleLoginRequiredMixin, ListView):
model = Team
template_name = 'backoffice/teams/list.html'
context_object_name = 'teams'
permission_required = 'core.view_team'
def get_queryset(self) -> QuerySet[Team]:
member = ConferenceMember.get_member(
conference=self.conference,
user=self.request.user,
)
qs = super().get_queryset().order_by('name')
if member.is_staff:
qs = qs.annotate(
members_count=Count('members'),
)
else:
qs = qs.filter(require_staff=False)
return qs.annotate(
member_list=ArrayAgg('members__user'),
self_manage_count=Count('members', filter=Q(members__user=self.request.user, members__can_manage=True)),
).annotate(
can_manage=Case(When(self_manage_count__gt=0, then=True), default=False, output_field=BooleanField()),
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment