Skip to content
4 changes: 2 additions & 2 deletions src/backoffice/templates/token_category_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ <h3 class="card-title">Token Category Details | BackOffice</h3>
</tr>
<tr>
<th>Used by:</th>
<td>{{ object.token_set.count }}</td>
<td>{{ category_tokens | length }}</td>
</tr>
<tr>
<th>Created:</th>
Expand All @@ -50,7 +50,7 @@ <h3>Related tokens</h3>
</tr>
</thead>
<tbody>
{% for token in object.token_set.all %}
{% for token in category_tokens %}
<tr>
<td><a href="{% url 'backoffice:token_detail' camp_slug=token.camp.slug pk=token.pk %}">{{ token.token }}</a></td>
<td>{{ token.camp }}</td>
Expand Down
2 changes: 1 addition & 1 deletion src/backoffice/templates/token_category_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ <h3 class="card-title">Token Category List - BackOffice</h3>
<tr>
<td><a href="{% url 'backoffice:token_category_detail' camp_slug=camp.slug pk=category.pk %}">{{ category.name }}</a></td>
<td>{{ category.description }}</td>
<td class="text-start">{{ category.token_set.count }}</td>
<td class="text-start">{{ category.token_count }}</td>
<td>
<a href="{% url 'backoffice:token_create' camp_slug=camp.slug %}" class="me-1"><i class="fas fa-add text-success"></i></a>
<a href="{% url 'backoffice:token_category_detail' camp_slug=camp.slug pk=category.pk %}" class="me-1"><i class="fas fa-search text-info"></i></a>
Expand Down
99 changes: 99 additions & 0 deletions src/backoffice/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import pytest
import uuid

from tokens.models import TokenCategory
from tokens.models import TokenFind
from tokens.models import Token


@pytest.fixture
def token_category_factory(db):
"""Fixture for returning a factory function."""

def create_token_category(persist=False, **kwargs) -> TokenCategory:
"""Create a TokenCategory instance. Saves to DB if persist=True."""
defaults = {
"name": "token-category",
"description": "token category description",
}

defaults.update(**kwargs)

category = TokenCategory(**defaults)

if persist:
category.save()

return category

return create_token_category


@pytest.fixture
def token_category(token_category_factory) -> TokenCategory:
"""Fixture for a single TokenCategory"""
return token_category_factory(persist=True)


@pytest.fixture
def token_factory(db, camp, token_category):
"""Fixture for returning a factory function."""

def create_token(persist=False, **kwargs) -> Token:
"""Create a Token instance. Saves to DB if persist=True."""
defaults = {
"category": token_category,
"camp": camp,
"token": str(uuid.uuid4()).replace('-', ''),
"hint": "token-hint",
"description": "token description",
"active": True,
"valid_when": None,
}

defaults.update(**kwargs)

token = Token(**defaults)

if persist:
token.save()

return token

return create_token


@pytest.fixture
def token(token_factory) -> Token:
"""Fixture for returning a single active token"""
return token_factory(persist=True)


@pytest.fixture
def token_find_factory(db, token, users):
"""Fixture for returning a factory function."""

def create_token_find(persist=False, **kwargs) -> TokenFind:
"""Create a TokenFind instance. Saves to DB if persist=True."""
defaults = {
"token": token,
"user": users[0],
}

defaults.update(**kwargs)

token_find = TokenFind(**defaults)

if persist:
token_find.save()

return token_find

return create_token_find


@pytest.fixture
def token_find(token_find_factory) -> TokenFind:
"""Fixture for returning a single token find."""
return token_find_factory(persist=True)

100 changes: 100 additions & 0 deletions src/backoffice/tests/test_game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import pytest

from django.urls import reverse

from tokens.models import TokenCategory


class TestTokenCategoryListView:
"""Class for grouping TokenCategoryListView tests."""

@pytest.fixture
def url(self, camp) -> str:
"""Reverse URL for view."""
kwargs = {"camp_slug": camp.slug}
return reverse("backoffice:token_category_list", kwargs=kwargs)

@pytest.fixture
def mixed_categories(self, token_category_factory, token_factory) -> list:
"""Fixture for categories with and without related tokens."""
categories = []
for i in range(5):
category = token_category_factory(persist=True, name=f"category{i}")

if i % 2 == 0:
token_factory(persist=True, category=category)

categories.append(category)

return categories

@pytest.mark.usefixtures("mixed_categories")
def test_all_categories_exist_in_queryset(self, admin_client):
"""Test if all categories with and without Tokens exist in queryset."""
expected = TokenCategory.objects.all()

response = admin_client.get(admin_client.url)
result = response.context.get("object_list")

assert result.count() == expected.count()

def test_annotate_token_count_for_current_camp(
self,
admin_client,
token_category,
token_factory,
test_camps
):
"""Test the object list has annotated token count for current camp."""
expected = 0

response = admin_client.get(admin_client.url)
result = response.context.get("object_list")

assert result.get(pk=token_category.pk).token_count == expected

token_count = 6
expected = token_count / len(test_camps)
for i in range(token_count):
token_factory(persist=True, camp=test_camps[i % 3])

response = admin_client.get(admin_client.url)
result = response.context.get("object_list")

assert result.get(pk=token_category.pk).token_count == expected


class TestTokenCategoryDetailView:
"""Class for grouping TokenCategoryDetailView tests."""

@pytest.fixture
def url(self, camp, token_category) -> str:
"""Reverse URL for view."""
kwargs = {"camp_slug": camp.slug, "pk": token_category.pk}
return reverse("backoffice:token_category_detail", kwargs=kwargs)

def test_add_category_tokens_for_current_camp_to_context(
self,
admin_client,
token_category,
token_category_factory,
test_camps,
token_factory
):
"""Test only tokens for this category and camp is added to context."""
category2 = token_category_factory(persist=True, name="test")

expected = []
for camp in list(test_camps):
for _ in range(3):
if camp == test_camps.current:
expected.append(
token_factory(persist=True, camp=camp, category=token_category)
)
token_factory(persist=True, camp=camp, category=category2)

response = admin_client.get(admin_client.url)
result = response.context.get("category_tokens")

assert list(result) == expected

18 changes: 18 additions & 0 deletions src/backoffice/views/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ class TokenCategoryListView(CampViewMixin, RaisePermissionRequiredMixin, ListVie
model = TokenCategory
template_name = "token_category_list.html"

def get_queryset(self):
"""
Get all Categories and annotate count of tokens for the current camp.
"""
camp_tokens = Count("token", filter=Q(token__camp=self.request.camp))
return TokenCategory.objects.annotate(token_count=camp_tokens)


class TokenCategoryCreateView(CampViewMixin, RaisePermissionRequiredMixin, CreateView):
"""Create a new token category."""
Expand Down Expand Up @@ -158,6 +165,17 @@ class TokenCategoryDetailView(CampViewMixin, RaisePermissionRequiredMixin, Detai
model = TokenCategory
template_name = "token_category_detail.html"

def get_context_data(self, **kwargs):
"""Add all category tokens for the current camp to context."""
context = super().get_context_data(**kwargs)
context.update({
"category_tokens": Token.objects.filter(
category=self.object,
camp=self.request.camp
)
})
return context


class TokenCategoryDeleteView(CampViewMixin, RaisePermissionRequiredMixin, DeleteView):
"""Delete a token category"""
Expand Down
Loading
Loading