diff --git a/apps/family/tables.py b/apps/family/tables.py
index 0e3ffc47..de00b815 100644
--- a/apps/family/tables.py
+++ b/apps/family/tables.py
@@ -30,6 +30,11 @@ class ChallengeTable(tables.Table):
"""
List all challenges
"""
+ name = tables.LinkColumn(
+ "family:challenge_detail",
+ args=[A("pk")],
+ )
+
class Meta:
attrs = {
'class': 'table table-condensed table-striped table-hover'
@@ -37,4 +42,4 @@ class ChallengeTable(tables.Table):
order_by = ('id',)
model = Challenge
template_name = 'django_tables2/bootstrap4.html'
- fields = ('name', 'points', 'category',)
+ fields = ('name', 'description', 'points',)
diff --git a/apps/family/templates/family/challenge_detail.html b/apps/family/templates/family/challenge_detail.html
new file mode 100644
index 00000000..44addf57
--- /dev/null
+++ b/apps/family/templates/family/challenge_detail.html
@@ -0,0 +1,32 @@
+{% extends "base.html" %}
+{% comment %}
+Copyright (C) by BDE ENS Paris-Saclay
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
+{% load i18n crispy_forms_tags %}
+
+{% block content %}
+
+
+
+
+ {% for field, value in fields %}
+ - {{ field }} : {{ value }}
+ {% endfor %}
+ - {% trans "Obtained by " %} {{obtained}}
+ {% if obtained > 1 %}
+ {% trans "families" %}
+ {% else %}
+ {% trans "family" %}
+ {% endif %}
+
+
+
+ {% trans "Return to the challenge list" %}
+
+
+
+
+{% endblock %}
diff --git a/apps/family/templates/family/family_detail.html b/apps/family/templates/family/family_detail.html
new file mode 100644
index 00000000..b8f9d918
--- /dev/null
+++ b/apps/family/templates/family/family_detail.html
@@ -0,0 +1,5 @@
+{% extends "base.html" %}
+{% comment %}
+Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
+SPDX-License-Identifier: GPL-3.0-or-later
+{% endcomment %}
diff --git a/apps/family/urls.py b/apps/family/urls.py
index 99b87d92..622d8b54 100644
--- a/apps/family/urls.py
+++ b/apps/family/urls.py
@@ -3,10 +3,13 @@
from django.urls import path
-from .views import FamilyListView, ChallengeListView
+from .views import FamilyListView, FamilyDetailView, ChallengeListView, ChallengeDetailView, ChallengeUpdateView
app_name = 'family'
urlpatterns = [
path('list/', FamilyListView.as_view(), name="family_list"),
+ path('detail//', FamilyDetailView.as_view(), name="family_detail"),
path('challenge/list/', ChallengeListView.as_view(), name="challenge_list"),
+ path('challenge/detail//', ChallengeDetailView.as_view(), name="challenge_detail"),
+ path('challenge/update//', ChallengeUpdateView.as_view(), name="challenge_update"),
]
diff --git a/apps/family/views.py b/apps/family/views.py
index 8d41ccac..d8072dbb 100644
--- a/apps/family/views.py
+++ b/apps/family/views.py
@@ -5,6 +5,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import DetailView, UpdateView
from django.utils.translation import gettext_lazy as _
from django_tables2 import SingleTableView
+from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from .models import Family, Challenge
@@ -54,6 +55,21 @@ class FamilyUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
extra_context = {"title": _('Update family')}
+class ChallengeCreateView(ProtectQuerysetMixin, ProtectedCreateView):
+ """
+ Create challenge
+ """
+ model = Challenge
+ extra_context = {"title": _('Create challenge')}
+
+ def get_sample_object(self):
+ return Challenge(
+ name="",
+ description="Sample challenge",
+ points=0,
+ )
+
+
class ChallengeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
List all challenges
@@ -61,3 +77,35 @@ class ChallengeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVie
model = Challenge
table_class = ChallengeTable
extra_context = {"title": _('Challenges list')}
+
+
+class ChallengeDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
+ """
+ Display details of a challenge
+ """
+ model = Challenge
+ context_object_name = "challenge"
+ extra_context = {"title": _('Details of:')}
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ fields = ["name", "description", "points",]
+
+ fields = dict([(field, getattr(self.object, field)) for field in fields])
+
+ context["fields"] = [(
+ Challenge._meta.get_field(field).verbose_name.capitalize(),
+ value) for field, value in fields.items()]
+ context["obtained"] = getattr(self.object, "obtained")
+ context["update"] = PermissionBackend.check_perm(self.request, "family.change_challenge")
+
+ return context
+
+class ChallengeUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
+ """
+ Update the information of a challenge
+ """
+ model = Challenge
+ context_object_name = "challenge"
+ extra_context = {"title": _('Update challenge')}
+ template_name = 'family/challenge_update.html'