1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-07-19 23:51:25 +02:00

Achievement delete

This commit is contained in:
Ehouarn
2025-07-18 22:11:43 +02:00
parent 67b936ae98
commit 9e700fd3de
9 changed files with 119 additions and 22 deletions

View File

@ -1,6 +1,7 @@
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.urls import path from django.urls import path
from .views import FamilyViewSet, FamilyMembershipViewSet, ChallengeViewSet, AchievementViewSet, BatchAchievementsAPIView from .views import FamilyViewSet, FamilyMembershipViewSet, ChallengeViewSet, AchievementViewSet, BatchAchievementsAPIView
@ -13,6 +14,7 @@ def register_family_urls(router, path):
router.register(path + '/challenge', ChallengeViewSet) router.register(path + '/challenge', ChallengeViewSet)
router.register(path + '/achievement', AchievementViewSet) router.register(path + '/achievement', AchievementViewSet)
urlpatterns = [ urlpatterns = [
path('achievements/batch/', BatchAchievementsAPIView.as_view(), name='batch_achievements') path('achievements/batch/', BatchAchievementsAPIView.as_view(), name='batch_achievements')
] ]

View File

@ -8,10 +8,6 @@ from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status from rest_framework import status
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from django.http import JsonResponse
import json
from .serializers import FamilySerializer, FamilyMembershipSerializer, ChallengeSerializer, AchievementSerializer from .serializers import FamilySerializer, FamilyMembershipSerializer, ChallengeSerializer, AchievementSerializer
from ..models import Family, FamilyMembership, Challenge, Achievement from ..models import Family, FamilyMembership, Challenge, Achievement
@ -71,6 +67,7 @@ class AchievementViewSet(ReadProtectedModelViewSet):
class BatchAchievementsAPIView(APIView): class BatchAchievementsAPIView(APIView):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def post(self, request, format=None): def post(self, request, format=None):
print("POST de la view spéciale") print("POST de la view spéciale")
family_ids = request.data.get('families', []) family_ids = request.data.get('families', [])
@ -88,4 +85,4 @@ class BatchAchievementsAPIView(APIView):
family.update_score() family.update_score()
Family.update_ranking() Family.update_ranking()
return Response({'status': 'ok'}, status=status.HTTP_201_CREATED) return Response({'status': 'ok'}, status=status.HTTP_201_CREATED)

View File

@ -3,6 +3,8 @@
import django_tables2 as tables import django_tables2 as tables
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django_tables2 import A
from .models import Family, Challenge, FamilyMembership, Achievement from .models import Family, Challenge, FamilyMembership, Achievement
@ -63,11 +65,28 @@ class AchievementTable(tables.Table):
""" """
List recent achievements. List recent achievements.
""" """
delete = tables.LinkColumn(
'family:achievement_delete',
args=[A('id')],
verbose_name=_("Delete"),
text=_("Delete"),
orderable=False,
attrs={
'th': {
'id': 'delete-membership-header'
},
'a': {
'class': 'btn btn-danger',
'data-type': 'delete-membership'
}
},
)
class Meta: class Meta:
attrs = { attrs = {
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
} }
model = Achievement model = Achievement
fields = ('family', 'challenge', 'obtained_at', ) fields = ('family', 'challenge', 'challenge__points', 'obtained_at', )
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
orderable = False order_by = ('-obtained_at',)

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% comment %}
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load i18n crispy_forms_tags %}
{% block content %}
<div class="card bg-light">
<div class="card-header text-center">
<h4>{% trans "Delete achievement" %}</h4>
</div>
<div class="card-body">
<div class="alert alert-warning">
{% blocktrans %}Are you sure you want to delete this achievement? This action can't be undone.{% endblocktrans %}
</div>
</div>
<div class="card-footer text-center">
<form method="post">
{% csrf_token %}
<a class="btn btn-primary" href="{% url 'family:achievement_list' %}">{% trans "Return to achievements list" %}</a>
{% if not object.locked %}
<button class="btn btn-danger" type="submit">{% trans "Delete" %}</button>
{% endif %}
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% comment %}
Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %}
{% load i18n django_tables2 %}
{% block content %}
<div class="card mb-4" id="history">
<div class="card-header">
<p class="card-text font-weight-bold">
{% trans "Recent achievements history" %}
</p>
<a class="btn btn-sm btn-primary mx-2" href="{% url "family:manage" %}">
{% trans "Return to management page" %}
</a>
</div>
{% render_table table %}
</div>
{% endblock %}

View File

@ -143,24 +143,21 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div> </div>
</div> </div>
{# Mode switch #}
<div class="card-footer border-primary">
<a class="btn btn-sm btn-secondary float-left" href="{% url 'note:template_list' %}">
<i class="fa fa-edit"></i> {% trans "Edit" %}
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
{# transaction history #} {# transaction history #}
<div class="card mb-4" id="history"> <div class="card">
<div class="card-header"> <div class="card-header position-relative" id="historyListHeading">
<p class="card-text font-weight-bold"> <a class="stretched-link font-weight-bold"
href="{% url 'family:achievement_list' %}" >
{% trans "Recent achievements history" %} {% trans "Recent achievements history" %}
</p> </a>
</div>
<div id="history_list">
{% render_table table %}
</div> </div>
{% render_table table %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -18,5 +18,7 @@ urlpatterns = [
path('challenge/detail/<int:pk>/', views.ChallengeDetailView.as_view(), name="challenge_detail"), path('challenge/detail/<int:pk>/', views.ChallengeDetailView.as_view(), name="challenge_detail"),
path('challenge/update/<int:pk>/', views.ChallengeUpdateView.as_view(), name="challenge_update"), path('challenge/update/<int:pk>/', views.ChallengeUpdateView.as_view(), name="challenge_update"),
path('manage/', views.FamilyManageView.as_view(), name="manage"), path('manage/', views.FamilyManageView.as_view(), name="manage"),
path('achievements/', views.AchievementsView.as_view(), name="achievement_list"),
path('achievement/delete/<int:pk>/', views.AchievementDeleteView.as_view(), name="achievement_delete"),
path('api/family/', include('family.api.urls')), path('api/family/', include('family.api.urls')),
] ]

View File

@ -7,6 +7,7 @@ from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction from django.db import transaction
from django.views.generic import DetailView, UpdateView from django.views.generic import DetailView, UpdateView
from django.views.generic.edit import DeleteView
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
@ -195,7 +196,7 @@ class ChallengeCreateView(ProtectQuerysetMixin, ProtectedCreateView):
) )
def get_success_url(self): def get_success_url(self):
return reverse_lazy('family:challenge_list') return reverse_lazy('family:manage')
class ChallengeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): class ChallengeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
@ -264,7 +265,7 @@ class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
# retrieves only Transaction that user has the right to see. # retrieves only Transaction that user has the right to see.
return Achievement.objects.filter( return Achievement.objects.filter(
PermissionBackend.filter_queryset(self.request, Achievement, "view") PermissionBackend.filter_queryset(self.request, Achievement, "view")
).order_by("-obtained_at").all()[:20] ).order_by("-obtained_at").all()
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -277,3 +278,33 @@ class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
context["can_add_challenge"] = PermissionBackend.check_perm(self.request, "family.add_challenge") context["can_add_challenge"] = PermissionBackend.check_perm(self.request, "family.add_challenge")
return context return context
def get_table(self, **kwargs):
table = super().get_table(**kwargs)
table.exclude = ('delete',)
table.orderable = False
return table
class AchievementsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
List all achievements
"""
model = Achievement
table_class = AchievementTable
extra_context = {'title': _('Achievement list')}
def get_table(self, **kwargs):
table = super().get_table(**kwargs)
table.orderable = True
return table
class AchievementDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView):
"""
Delete an Achievement
"""
model = Achievement
def get_success_url(self):
return reverse_lazy('family:achievement_list')

View File

@ -168,7 +168,7 @@ class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
class InvoiceDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView): class InvoiceDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView):
""" """
Delete a non-validated WEI registration Delete a non-locked Invoice
""" """
model = Invoice model = Invoice
extra_context = {"title": _("Delete invoice")} extra_context = {"title": _("Delete invoice")}