diff --git a/apps/family/api/views.py b/apps/family/api/views.py index 79a719d1..b680e135 100644 --- a/apps/family/api/views.py +++ b/apps/family/api/views.py @@ -75,13 +75,24 @@ class BatchAchievementsAPIView(APIView): challenge_ids = request.data.get('challenges') families = Family.objects.filter(id__in=family_ids) challenges = Challenge.objects.filter(id__in=challenge_ids) + results = [] for family in families: for challenge in challenges: - a = Achievement(family=family, challenge=challenge) - a.save(update_score=False) - + a, created = Achievement.objects.get_or_create(family=family, challenge=challenge) + if created: + results.append({ + 'family': family.name, + 'challenge': challenge.name, + 'status': 'created' + }) + else: + results.append({ + 'family': family.name, + 'challenge': challenge.name, + 'status': 'error', + }) for family in families: family.update_score() Family.update_ranking() - return Response({'status': 'ok'}, status=status.HTTP_201_CREATED) + return Response({'results': results}, status=status.HTTP_201_CREATED) diff --git a/apps/family/migrations/0005_alter_achievement_unique_together.py b/apps/family/migrations/0005_alter_achievement_unique_together.py new file mode 100644 index 00000000..69e659b4 --- /dev/null +++ b/apps/family/migrations/0005_alter_achievement_unique_together.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.4 on 2025-08-13 20:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('family', '0004_remove_challenge_obtained'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='achievement', + unique_together={('challenge', 'family')}, + ), + ] diff --git a/apps/family/models.py b/apps/family/models.py index 71ccfa08..3ce27748 100644 --- a/apps/family/models.py +++ b/apps/family/models.py @@ -170,6 +170,7 @@ class Achievement(models.Model): ) class Meta: + unique_together = ('challenge', 'family',) verbose_name = _('achievement') verbose_name_plural = _('achievements') diff --git a/apps/family/static/family/js/achievements.js b/apps/family/static/family/js/achievements.js index b9774579..50cd5935 100644 --- a/apps/family/static/family/js/achievements.js +++ b/apps/family/static/family/js/achievements.js @@ -25,11 +25,6 @@ $(document).ready(function () { location.hash = this.getAttribute('href') }) - - // Ensure we begin in single consumption. Fix issue with TurboLinks and BootstrapJS - - - document.getElementById("consume_all").addEventListener('click', consumeAll) }) notes = [] @@ -140,13 +135,25 @@ function consumeAll () { headers: { 'X-CSRFToken': CSRF_TOKEN }, - success: function () { + success: function (data) { reset() - addMsg("Défis validés pour les familles !", 'success', 5000) - }, - error: function (e) { - reset() - addMsg("Erreur lors de la création des achievements.",'danger',5000) + data.results.forEach(function (result) { + if (result.status === 'created') { + addMsg( + interpolate(gettext('Invalid achievement for challenge %s ' + + 'and family %s created.'), [result.challenge, result.family]), + 'success', + 5000 + ) + } else { + addMsg( + interpolate(gettext('An achievement for challenge %s ' + + 'and family %s already exists.'), [result.challenge, result.family]), + 'danger', + 8000 + ) + } + }) } }) } diff --git a/apps/family/templates/family/manage.html b/apps/family/templates/family/manage.html index b344bb5b..596fcb3a 100644 --- a/apps/family/templates/family/manage.html +++ b/apps/family/templates/family/manage.html @@ -177,25 +177,27 @@ SPDX-License-Identifier: GPL-3.0-or-later