mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	Tests
This commit is contained in:
		@@ -3,7 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from api.viewsets import ReadProtectedModelViewSet
 | 
					from api.viewsets import ReadProtectedModelViewSet
 | 
				
			||||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
					from django_filters.rest_framework import DjangoFilterBackend
 | 
				
			||||||
from rest_framework.filters import SearchFilter
 | 
					from api.filters import RegexSafeSearchFilter
 | 
				
			||||||
from rest_framework.views import APIView
 | 
					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
 | 
				
			||||||
@@ -21,9 +21,9 @@ class FamilyViewSet(ReadProtectedModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Family.objects.order_by('id')
 | 
					    queryset = Family.objects.order_by('id')
 | 
				
			||||||
    serializer_class = FamilySerializer
 | 
					    serializer_class = FamilySerializer
 | 
				
			||||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
					    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
				
			||||||
    filterset_fields = ['name', ]
 | 
					    filterset_fields = ['name', 'description', 'score', 'rank', ]
 | 
				
			||||||
    search_fields = ['$name', ]
 | 
					    search_fields = ['$name', '$description', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FamilyMembershipViewSet(ReadProtectedModelViewSet):
 | 
					class FamilyMembershipViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
@@ -34,9 +34,11 @@ class FamilyMembershipViewSet(ReadProtectedModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = FamilyMembership.objects.order_by('id')
 | 
					    queryset = FamilyMembership.objects.order_by('id')
 | 
				
			||||||
    serializer_class = FamilyMembershipSerializer
 | 
					    serializer_class = FamilyMembershipSerializer
 | 
				
			||||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
					    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
				
			||||||
    filterset_fields = ['name', ]
 | 
					    filterset_fields = ['user__username', 'user__first_name', 'user__last_name', 'user__email', 'user__note__alias__name',
 | 
				
			||||||
    search_fields = ['$name', ]
 | 
					                        'user__note__alias__normalized_name', 'family__name', 'family__description', 'year', ]
 | 
				
			||||||
 | 
					    search_fields = ['$user__username', '$user__first_name', '$user__last_name', '$user__email', '$user__note__alias__name',
 | 
				
			||||||
 | 
					                     '$user__note__alias__normalized_name', '$family__name', '$family__description', '$year', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ChallengeViewSet(ReadProtectedModelViewSet):
 | 
					class ChallengeViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
@@ -47,9 +49,9 @@ class ChallengeViewSet(ReadProtectedModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Challenge.objects.order_by('id')
 | 
					    queryset = Challenge.objects.order_by('id')
 | 
				
			||||||
    serializer_class = ChallengeSerializer
 | 
					    serializer_class = ChallengeSerializer
 | 
				
			||||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
					    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
				
			||||||
    filterset_fields = ['name', ]
 | 
					    filterset_fields = ['name', 'description', 'points', ]
 | 
				
			||||||
    search_fields = ['$name', ]
 | 
					    search_fields = ['$name', '$description', '$points', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AchievementViewSet(ReadProtectedModelViewSet):
 | 
					class AchievementViewSet(ReadProtectedModelViewSet):
 | 
				
			||||||
@@ -60,22 +62,19 @@ class AchievementViewSet(ReadProtectedModelViewSet):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    queryset = Achievement.objects.order_by('id')
 | 
					    queryset = Achievement.objects.order_by('id')
 | 
				
			||||||
    serializer_class = AchievementSerializer
 | 
					    serializer_class = AchievementSerializer
 | 
				
			||||||
    filter_backends = [DjangoFilterBackend, SearchFilter]
 | 
					    filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
 | 
				
			||||||
    filterset_fields = ['name', ]
 | 
					    filterset_fields = ['family__name', 'family__description', 'challenge__name', 'challenge__description', 'obtained_at', 'valid', ]
 | 
				
			||||||
    search_fields = ['$name', ]
 | 
					    search_fields = ['$family__name', '$family__description', '$challenge__name', '$challenge__description', ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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")
 | 
					        family_ids = request.data.get('families')
 | 
				
			||||||
        family_ids = request.data.get('families', [])
 | 
					        challenge_ids = request.data.get('challenges')
 | 
				
			||||||
        challenge_ids = request.data.get('challenges', [])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        families = Family.objects.filter(id__in=family_ids)
 | 
					        families = Family.objects.filter(id__in=family_ids)
 | 
				
			||||||
        challenges = Challenge.objects.filter(id__in=challenge_ids)
 | 
					        challenges = Challenge.objects.filter(id__in=challenge_ids)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        for family in families:
 | 
					        for family in families:
 | 
				
			||||||
            for challenge in challenges:
 | 
					            for challenge in challenges:
 | 
				
			||||||
                a = Achievement(family=family, challenge=challenge)
 | 
					                a = Achievement(family=family, challenge=challenge)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								apps/family/migrations/0004_remove_challenge_obtained.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								apps/family/migrations/0004_remove_challenge_obtained.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					# Generated by Django 5.2.4 on 2025-07-22 14:33
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ('family', '0003_achievement_valid_alter_familymembership_family'),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.RemoveField(
 | 
				
			||||||
 | 
					            model_name='challenge',
 | 
				
			||||||
 | 
					            name='obtained',
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@@ -4,6 +4,7 @@
 | 
				
			|||||||
from django.db import models, transaction
 | 
					from django.db import models, transaction
 | 
				
			||||||
from django.utils import timezone
 | 
					from django.utils import timezone
 | 
				
			||||||
from django.contrib.auth.models import User
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -44,6 +45,9 @@ class Family(models.Model):
 | 
				
			|||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return self.name
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('family:family_detail', args=(self.pk,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update_score(self, *args, **kwargs):
 | 
					    def update_score(self, *args, **kwargs):
 | 
				
			||||||
        challenge_set = Challenge.objects.select_for_update().filter(achievement__family=self, achievement__valid=True)
 | 
					        challenge_set = Challenge.objects.select_for_update().filter(achievement__family=self, achievement__valid=True)
 | 
				
			||||||
        points_sum = challenge_set.aggregate(models.Sum("points"))
 | 
					        points_sum = challenge_set.aggregate(models.Sum("points"))
 | 
				
			||||||
@@ -119,10 +123,16 @@ class Challenge(models.Model):
 | 
				
			|||||||
        verbose_name=_('points'),
 | 
					        verbose_name=_('points'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    obtained = models.PositiveIntegerField(
 | 
					    @property
 | 
				
			||||||
        verbose_name=_('obtained'),
 | 
					    def obtained(self):
 | 
				
			||||||
        default=0,
 | 
					        achievements = Achievement.objects.filter(challenge=self, valid=True)
 | 
				
			||||||
    )
 | 
					        return achievements.count()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_absolute_url(self):
 | 
				
			||||||
 | 
					        return reverse_lazy('family:challenge_detail', args=(self.pk,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @transaction.atomic
 | 
					    @transaction.atomic
 | 
				
			||||||
    def save(self, *args, **kwargs):
 | 
					    def save(self, *args, **kwargs):
 | 
				
			||||||
@@ -136,9 +146,6 @@ class Challenge(models.Model):
 | 
				
			|||||||
        verbose_name = _('challenge')
 | 
					        verbose_name = _('challenge')
 | 
				
			||||||
        verbose_name_plural = _('challenges')
 | 
					        verbose_name_plural = _('challenges')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					 | 
				
			||||||
        return self.name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Achievement(models.Model):
 | 
					class Achievement(models.Model):
 | 
				
			||||||
    challenge = models.ForeignKey(
 | 
					    challenge = models.ForeignKey(
 | 
				
			||||||
@@ -176,7 +183,6 @@ class Achievement(models.Model):
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        self.family = Family.objects.select_for_update().get(pk=self.family_id)
 | 
					        self.family = Family.objects.select_for_update().get(pk=self.family_id)
 | 
				
			||||||
        self.challenge = Challenge.objects.select_for_update().get(pk=self.challenge_id)
 | 
					        self.challenge = Challenge.objects.select_for_update().get(pk=self.challenge_id)
 | 
				
			||||||
        is_new = self.pk is None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        super().save(*args, **kwargs)
 | 
					        super().save(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,13 +190,6 @@ class Achievement(models.Model):
 | 
				
			|||||||
            self.family.refresh_from_db()
 | 
					            self.family.refresh_from_db()
 | 
				
			||||||
            self.family.update_score()
 | 
					            self.family.update_score()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Count only when getting a new achievement
 | 
					 | 
				
			||||||
        if is_new:
 | 
					 | 
				
			||||||
            self.challenge.refresh_from_db()
 | 
					 | 
				
			||||||
            self.challenge.obtained += 1
 | 
					 | 
				
			||||||
            self.challenge._force_save = True
 | 
					 | 
				
			||||||
            self.challenge.save()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @transaction.atomic
 | 
					    @transaction.atomic
 | 
				
			||||||
    def delete(self, *args, **kwargs):
 | 
					    def delete(self, *args, **kwargs):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -205,8 +204,3 @@ class Achievement(models.Model):
 | 
				
			|||||||
        # Remove points from the family
 | 
					        # Remove points from the family
 | 
				
			||||||
        self.family.refresh_from_db()
 | 
					        self.family.refresh_from_db()
 | 
				
			||||||
        self.family.update_score()
 | 
					        self.family.update_score()
 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.challenge.refresh_from_db()
 | 
					 | 
				
			||||||
        self.challenge.obtained -= 1
 | 
					 | 
				
			||||||
        self.challenge._force_save = True
 | 
					 | 
				
			||||||
        self.challenge.save()
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								apps/family/static/family/img/default_picture.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								apps/family/static/family/img/default_picture.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 4.0 KiB  | 
@@ -113,6 +113,7 @@ function reset () {
 | 
				
			|||||||
 * Apply all transactions: all notes in `notes` buy each item in `buttons`
 | 
					 * Apply all transactions: all notes in `notes` buy each item in `buttons`
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
function consumeAll () {
 | 
					function consumeAll () {
 | 
				
			||||||
 | 
					  console.log("test");
 | 
				
			||||||
  if (LOCK) { return }
 | 
					  if (LOCK) { return }
 | 
				
			||||||
  LOCK = true
 | 
					  LOCK = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -130,11 +131,13 @@ function consumeAll () {
 | 
				
			|||||||
    LOCK = false
 | 
					    LOCK = false
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  console.log("couocu")
 | 
				
			||||||
  // Récupérer les IDs des familles et des challenges
 | 
					  // Récupérer les IDs des familles et des challenges
 | 
				
			||||||
  const family_ids = notes_display.map(fam => fam.id)
 | 
					  const family_ids = notes_display.map(fam => fam.id)
 | 
				
			||||||
  const challenge_ids = buttons.map(chal => chal.id)
 | 
					  const challenge_ids = buttons.map(chal => chal.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log(family_ids)
 | 
				
			||||||
 | 
					  console.log(challenge_ids)
 | 
				
			||||||
  $.ajax({
 | 
					  $.ajax({
 | 
				
			||||||
    url: '/family/api/family/achievements/batch/',
 | 
					    url: '/family/api/family/achievements/batch/',
 | 
				
			||||||
    type: 'POST',
 | 
					    type: 'POST',
 | 
				
			||||||
@@ -157,34 +160,6 @@ function consumeAll () {
 | 
				
			|||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Create a new achievement through the API.
 | 
					 | 
				
			||||||
 * @param family The selected family
 | 
					 | 
				
			||||||
 * @param challenge The selected challenge
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
function grantAchievement (family, challenge) {
 | 
					 | 
				
			||||||
  console.log("grant lancée",family,challenge)
 | 
					 | 
				
			||||||
  $.post('/api/family/achievement/',
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      csrfmiddlewaretoken: CSRF_TOKEN,
 | 
					 | 
				
			||||||
      family: family.id,
 | 
					 | 
				
			||||||
      challenge: challenge.id,
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .done(function () {
 | 
					 | 
				
			||||||
      reset()
 | 
					 | 
				
			||||||
      addMsg("Défi validé pour la famille !", 'success', 5000)
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    .fail(function (e) {
 | 
					 | 
				
			||||||
      reset()
 | 
					 | 
				
			||||||
      if (e.responseJSON) {
 | 
					 | 
				
			||||||
        errMsg(e.responseJSON)
 | 
					 | 
				
			||||||
      } else if (e.responseText) {
 | 
					 | 
				
			||||||
        errMsg(e.responseText)
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        errMsg("Erreur inconnue lors de la création de l'achievement.")
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var searchbar = document.getElementById("search-input")
 | 
					var searchbar = document.getElementById("search-input")
 | 
				
			||||||
var search_results = document.getElementById("search-results")
 | 
					var search_results = document.getElementById("search-results")
 | 
				
			||||||
@@ -264,7 +239,6 @@ function li (id, text, extra_css) {
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
function autoCompleteFamily(field_id, family_list_id, families, families_display, family_prefix = 'family', user_family_field = null, profile_pic_field = null, family_click = null) {
 | 
					function autoCompleteFamily(field_id, family_list_id, families, families_display, family_prefix = 'family', user_family_field = null, profile_pic_field = null, family_click = null) {
 | 
				
			||||||
  const field = $('#' + field_id)
 | 
					  const field = $('#' + field_id)
 | 
				
			||||||
  console.log("autoCompleteFamily commence")
 | 
					 | 
				
			||||||
  // Configuration du tooltip
 | 
					  // Configuration du tooltip
 | 
				
			||||||
  field.tooltip({
 | 
					  field.tooltip({
 | 
				
			||||||
    html: true,
 | 
					    html: true,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,12 +84,12 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			|||||||
          </h3>
 | 
					          </h3>
 | 
				
			||||||
        <div class="card-body text-center">
 | 
					        <div class="card-body text-center">
 | 
				
			||||||
            {% if can_add_family %}
 | 
					            {% if can_add_family %}
 | 
				
			||||||
            <a class="btn btn-sm btn-primary mx-2" href="{% url "family:add_family" %}">
 | 
					            <a class="btn btn-sm btn-primary mx-2" href="{% url "family:family_create" %}">
 | 
				
			||||||
                {% trans "Add a family" %}
 | 
					                {% trans "Add a family" %}
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
            {% if can_add_challenge %}
 | 
					            {% if can_add_challenge %}
 | 
				
			||||||
            <a class="btn btn-sm btn-primary mx-2" href="{% url "family:add_challenge" %}">
 | 
					            <a class="btn btn-sm btn-primary mx-2" href="{% url "family:challenge_create" %}">
 | 
				
			||||||
                {% trans "Add a challenge" %}
 | 
					                {% trans "Add a challenge" %}
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
            {% endif %}
 | 
					            {% endif %}
 | 
				
			||||||
@@ -147,7 +147,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{# transaction history #}
 | 
					{# achievement history #}
 | 
				
			||||||
    <div class="card">
 | 
					    <div class="card">
 | 
				
			||||||
        <div class="card-header position-relative" id="historyListHeading">
 | 
					        <div class="card-header position-relative" id="historyListHeading">
 | 
				
			||||||
            <a class="stretched-link font-weight-bold" 
 | 
					            <a class="stretched-link font-weight-bold" 
 | 
				
			||||||
@@ -155,7 +155,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			|||||||
                {% trans "Recent achievements history" %}
 | 
					                {% trans "Recent achievements history" %}
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div id="history_list">
 | 
					        <div id="history">
 | 
				
			||||||
            {% render_table table %}
 | 
					            {% render_table table %}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										0
									
								
								apps/family/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/family/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										318
									
								
								apps/family/tests/test_family.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								apps/family/tests/test_family.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,318 @@
 | 
				
			|||||||
 | 
					# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
 | 
				
			||||||
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from api.tests import TestAPI
 | 
				
			||||||
 | 
					from django.contrib.auth.models import User
 | 
				
			||||||
 | 
					from django.core.files.uploadedfile import SimpleUploadedFile
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from rest_framework.test import APITestCase
 | 
				
			||||||
 | 
					from django.urls import reverse
 | 
				
			||||||
 | 
					from django.utils import timezone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from ..api.views import FamilyViewSet, FamilyMembershipViewSet, ChallengeViewSet, AchievementViewSet
 | 
				
			||||||
 | 
					from ..models import Family, FamilyMembership, Challenge, Achievement
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestFamily(TestCase):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Test family
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        self.user = User.objects.create_superuser(
 | 
				
			||||||
 | 
					            username='admintoto',
 | 
				
			||||||
 | 
					            password='toto1234',
 | 
				
			||||||
 | 
					            email='toto@example.com',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.client.force_login(self.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sess = self.client.session
 | 
				
			||||||
 | 
					        sess['permission_mask'] = 42
 | 
				
			||||||
 | 
					        sess.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.family = Family.objects.create(
 | 
				
			||||||
 | 
					            name='Test family',
 | 
				
			||||||
 | 
					            description='',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.challenge = Challenge.objects.create(
 | 
				
			||||||
 | 
					            name='Test challenge',
 | 
				
			||||||
 | 
					            description='',
 | 
				
			||||||
 | 
					            points=100,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.achievement = Achievement.objects.create(
 | 
				
			||||||
 | 
					            family=self.family,
 | 
				
			||||||
 | 
					            challenge=self.challenge,
 | 
				
			||||||
 | 
					            valid=False,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_list(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test display family list
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:family_list"))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_create(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test create a family
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:family_create"))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("family:family_create"), data={
 | 
				
			||||||
 | 
					            "name": "Family toto",
 | 
				
			||||||
 | 
					            "description": "A test family",
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        self.assertTrue(Family.objects.filter(name="Family toto").exists())
 | 
				
			||||||
 | 
					        self.assertRedirects(response, reverse("family:manage"), 302, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_detail(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test display the detail of a family
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:family_detail", args=(self.family.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_update(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test update a family
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:family_update", args=(self.family.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("family:family_update", args=(self.family.pk,)), data=dict(
 | 
				
			||||||
 | 
					            name="Toto family updated",
 | 
				
			||||||
 | 
					            description="A larger description for the test family"
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.family.get_absolute_url(), 302, 200)
 | 
				
			||||||
 | 
					        self.assertTrue(Family.objects.filter(name="Toto family updated").exists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_update_picture(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test update the picture of a family
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:update_pic", args=(self.family.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        old_pic = self.family.display_image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with open("apps/family/static/family/img/default_picture.png", "rb") as f:
 | 
				
			||||||
 | 
					            image = SimpleUploadedFile("image.png", f.read(), "image/png")
 | 
				
			||||||
 | 
					            response = self.client.post(reverse("family:update_pic", args=(self.family.pk,)), dict(
 | 
				
			||||||
 | 
					                image=image,
 | 
				
			||||||
 | 
					                x=0,
 | 
				
			||||||
 | 
					                y=0,
 | 
				
			||||||
 | 
					                width=200,
 | 
				
			||||||
 | 
					                height=200,
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					            self.assertRedirects(response, self.family.get_absolute_url(), 302, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.family.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertTrue(os.path.exists(self.family.display_image.path))
 | 
				
			||||||
 | 
					        os.remove(self.family.display_image.path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.family.display_image = old_pic
 | 
				
			||||||
 | 
					        self.family.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_add_member(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test add memberships to a family
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:family_add_member", args=(self.family.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        user = User.objects.create(username="totototo")
 | 
				
			||||||
 | 
					        user.profile.registration_valid = True
 | 
				
			||||||
 | 
					        user.profile.email_confirmed = True
 | 
				
			||||||
 | 
					        user.profile.save()
 | 
				
			||||||
 | 
					        user.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("family:family_add_member", args=(self.family.pk,)), data=dict(
 | 
				
			||||||
 | 
					            user=user.pk,
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.family.get_absolute_url(), 302, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertTrue(FamilyMembership.objects.filter(user=user, family=self.family, year=timezone.now().year).exists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_challenge_list(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test display challenge list
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse('family:challenge_list'))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_challenge_create(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test create a challenge
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:challenge_create"))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("family:challenge_create"), data={
 | 
				
			||||||
 | 
					            "name": "Challenge for toto",
 | 
				
			||||||
 | 
					            "description": "A test challenge",
 | 
				
			||||||
 | 
					            "points": 50,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        self.assertTrue(Challenge.objects.filter(name="Challenge for toto").exists())
 | 
				
			||||||
 | 
					        self.assertRedirects(response, reverse("family:manage"), 302, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_challenge_detail(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test display the detail of a challenge
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:challenge_detail", args=(self.challenge.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_challenge_update(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test update a challenge
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:challenge_update", args=(self.challenge.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("family:challenge_update", args=(self.challenge.pk,)), data=dict(
 | 
				
			||||||
 | 
					            name="Challenge updated",
 | 
				
			||||||
 | 
					            description="Another description",
 | 
				
			||||||
 | 
					            points=10,
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					        self.assertRedirects(response, self.challenge.get_absolute_url(), 302, 200)
 | 
				
			||||||
 | 
					        self.assertTrue(Challenge.objects.filter(name="Challenge updated").exists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_render_manage_page(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test render manage page
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:manage"))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_validate_achievement(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test validate an achievement
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        old_family_score = self.family.score
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:achievement_validate", args=(self.achievement.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.post(reverse("family:achievement_validate", args=(self.achievement.pk,)))
 | 
				
			||||||
 | 
					        self.assertRedirects(response, reverse("family:achievement_list"), 302, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.achievement.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertIs(self.achievement.valid, True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.family.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(self.family.score, old_family_score + self.achievement.challenge.points)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_delete_achievement(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Test delete an achievement
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        response = self.client.get(reverse("family:achievement_delete", args=(self.achievement.pk,)))
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 200)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self.client.delete(reverse("family:achievement_delete", args=(self.achievement.pk,)))
 | 
				
			||||||
 | 
					        self.assertRedirects(response, reverse("family:achievement_list"), 302, 200)
 | 
				
			||||||
 | 
					        self.assertFalse(Achievement.objects.filter(pk=self.achievement.pk).exists())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestBatchAchievements(APITestCase):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        self.user = User.objects.create_superuser(
 | 
				
			||||||
 | 
					            username='admintoto',
 | 
				
			||||||
 | 
					            password='toto1234',
 | 
				
			||||||
 | 
					            email='toto@example.com',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.client.force_login(self.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sess = self.client.session
 | 
				
			||||||
 | 
					        sess['permission_mask'] = 42
 | 
				
			||||||
 | 
					        sess.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.families = [
 | 
				
			||||||
 | 
					            Family.objects.create(name=f'Famille {i}', description='') for i in range(2)
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self.challenges = [
 | 
				
			||||||
 | 
					            Challenge.objects.create(name=f'Challenge {i}', description='', points=50) for i in range(3)
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.url = reverse("family:api:batch_achievements")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_batch_achievement_creation(self):
 | 
				
			||||||
 | 
					        family_ids = [f.id for f in self.families]
 | 
				
			||||||
 | 
					        challenge_ids = [c.id for c in self.challenges]
 | 
				
			||||||
 | 
					        response = self.client.post(
 | 
				
			||||||
 | 
					            self.url,
 | 
				
			||||||
 | 
					            data={
 | 
				
			||||||
 | 
					                'families': family_ids,
 | 
				
			||||||
 | 
					                'challenges': challenge_ids
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            format='json'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(response.status_code, 201)
 | 
				
			||||||
 | 
					        self.assertEqual(response.data['status'], 'ok')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expected_count = len(family_ids) * len(challenge_ids)
 | 
				
			||||||
 | 
					        self.assertEqual(Achievement.objects.count(), expected_count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Check that correct couples family/challenge exist
 | 
				
			||||||
 | 
					        for f in self.families:
 | 
				
			||||||
 | 
					            for c in self.challenges:
 | 
				
			||||||
 | 
					                self.assertTrue(
 | 
				
			||||||
 | 
					                    Achievement.objects.filter(family=f, challenge=c).exists()
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestFamilyAPI(TestAPI):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super().setUp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.family = Family.objects.create(
 | 
				
			||||||
 | 
					            name='Test family',
 | 
				
			||||||
 | 
					            description='',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.familymembership = FamilyMembership.objects.create(
 | 
				
			||||||
 | 
					            user=self.user,
 | 
				
			||||||
 | 
					            family=self.family,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.challenge = Challenge.objects.create(
 | 
				
			||||||
 | 
					            name='Test challenge',
 | 
				
			||||||
 | 
					            description='',
 | 
				
			||||||
 | 
					            points=100,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.achievement = Achievement.objects.create(
 | 
				
			||||||
 | 
					            family=self.family,
 | 
				
			||||||
 | 
					            challenge=self.challenge,
 | 
				
			||||||
 | 
					            valid=False,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_family_api(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Load Family API page and test all filters and permissions
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.check_viewset(FamilyViewSet, '/api/family/family/')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_familymembership_api(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Load FamilyMembership API page and test all filters and permissions
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.check_viewset(FamilyMembershipViewSet, '/api/family/familymembership/')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_challenge_api(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Load Challenge API page and test all filters and permissions
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.check_viewset(ChallengeViewSet, '/api/family/challenge/')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_achievement_api(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Load Achievement API page and test all filters and permissions
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self.check_viewset(AchievementViewSet, '/api/family/achievement/')
 | 
				
			||||||
@@ -8,18 +8,18 @@ from . import views
 | 
				
			|||||||
app_name = 'family'
 | 
					app_name = 'family'
 | 
				
			||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    path('list/', views.FamilyListView.as_view(), name="family_list"),
 | 
					    path('list/', views.FamilyListView.as_view(), name="family_list"),
 | 
				
			||||||
    path('add-family/', views.FamilyCreateView.as_view(), name="add_family"),
 | 
					    path('create/', views.FamilyCreateView.as_view(), name="family_create"),
 | 
				
			||||||
    path('<int:pk>/detail/', views.FamilyDetailView.as_view(), name="family_detail"),
 | 
					    path('<int:pk>/detail/', views.FamilyDetailView.as_view(), name="family_detail"),
 | 
				
			||||||
    path('<int:pk>/update/', views.FamilyUpdateView.as_view(), name="family_update"),
 | 
					    path('<int:pk>/update/', views.FamilyUpdateView.as_view(), name="family_update"),
 | 
				
			||||||
    path('<int:pk>/update_pic/', views.FamilyPictureUpdateView.as_view(), name="update_pic"),
 | 
					    path('<int:pk>/update_pic/', views.FamilyPictureUpdateView.as_view(), name="update_pic"),
 | 
				
			||||||
    path('<int:family_pk>/add_member/', views.FamilyAddMemberView.as_view(), name="family_add_member"),
 | 
					    path('<int:family_pk>/add_member/', views.FamilyAddMemberView.as_view(), name="family_add_member"),
 | 
				
			||||||
    path('challenge/list/', views.ChallengeListView.as_view(), name="challenge_list"),
 | 
					    path('challenge/list/', views.ChallengeListView.as_view(), name="challenge_list"),
 | 
				
			||||||
    path('add-challenge/', views.ChallengeCreateView.as_view(), name="add_challenge"),
 | 
					    path('challenge/create/', views.ChallengeCreateView.as_view(), name="challenge_create"),
 | 
				
			||||||
    path('challenge/<int:pk>/detail/', views.ChallengeDetailView.as_view(), name="challenge_detail"),
 | 
					    path('challenge/<int:pk>/detail/', views.ChallengeDetailView.as_view(), name="challenge_detail"),
 | 
				
			||||||
    path('challenge/<int:pk>/update/', views.ChallengeUpdateView.as_view(), name="challenge_update"),
 | 
					    path('challenge/<int:pk>/update/', views.ChallengeUpdateView.as_view(), name="challenge_update"),
 | 
				
			||||||
    path('manage/', views.FamilyManageView.as_view(), name="manage"),
 | 
					    path('manage/', views.FamilyManageView.as_view(), name="manage"),
 | 
				
			||||||
    path('achievement/list/', views.AchievementsView.as_view(), name="achievement_list"),
 | 
					    path('achievement/list/', views.AchievementListView.as_view(), name="achievement_list"),
 | 
				
			||||||
    path('achievement/<int:pk>/validate/', views.AchievementValidateView.as_view(), name="achievement_validate"),
 | 
					    path('achievement/<int:pk>/validate/', views.AchievementValidateView.as_view(), name="achievement_validate"),
 | 
				
			||||||
    path('achievement/<int:pk>/delete/', views.AchievementDeleteView.as_view(), name="achievement_delete"),
 | 
					    path('achievement/<int:pk>/delete/', views.AchievementDeleteView.as_view(), name="achievement_delete"),
 | 
				
			||||||
    path('api/family/', include('family.api.urls')),
 | 
					    path('api/family/', include(('family.api.urls', 'family_api'), namespace='api')),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,14 +8,14 @@ from django.shortcuts import redirect
 | 
				
			|||||||
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, ListView
 | 
					from django.views.generic import DetailView, UpdateView, ListView
 | 
				
			||||||
from django.views.generic.edit import DeleteView
 | 
					from django.views.generic.edit import DeleteView, FormMixin
 | 
				
			||||||
from django.views.generic.base import TemplateView
 | 
					from django.views.generic.base import TemplateView
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from django_tables2 import SingleTableView, MultiTableMixin
 | 
					from django_tables2 import SingleTableView, MultiTableMixin
 | 
				
			||||||
from permission.backends import PermissionBackend
 | 
					from permission.backends import PermissionBackend
 | 
				
			||||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
 | 
					from permission.views import ProtectQuerysetMixin, ProtectedCreateView
 | 
				
			||||||
from django.urls import reverse_lazy
 | 
					from django.urls import reverse_lazy
 | 
				
			||||||
from member.views import PictureUpdateView
 | 
					from member.forms import ImageForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .models import Family, Challenge, FamilyMembership, User, Achievement
 | 
					from .models import Family, Challenge, FamilyMembership, User, Achievement
 | 
				
			||||||
from .tables import FamilyTable, ChallengeTable, FamilyMembershipTable, AchievementTable, FamilyAchievementTable
 | 
					from .tables import FamilyTable, ChallengeTable, FamilyMembershipTable, AchievementTable, FamilyAchievementTable
 | 
				
			||||||
@@ -112,17 +112,28 @@ class FamilyUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 | 
				
			|||||||
        return reverse_lazy('family:family_detail', kwargs={'pk': self.object.pk})
 | 
					        return reverse_lazy('family:family_detail', kwargs={'pk': self.object.pk})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FamilyPictureUpdateView(PictureUpdateView):
 | 
					class FamilyPictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Update profile picture of the family
 | 
					    Update profile picture of the family
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    model = Family
 | 
					    model = Family
 | 
				
			||||||
    extra_context = {"title": _("Update family picture")}
 | 
					    extra_context = {"title": _("Update family picture")}
 | 
				
			||||||
    template_name = 'family/picture_update.html'
 | 
					    template_name = 'family/picture_update.html'
 | 
				
			||||||
 | 
					    form_class = ImageForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
 | 
					        context = super().get_context_data(**kwargs)
 | 
				
			||||||
 | 
					        context['form'] = self.form_class(self.request.POST, self.request.FILES)
 | 
				
			||||||
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_success_url(self):
 | 
					    def get_success_url(self):
 | 
				
			||||||
        """Redirect to family page after upload"""
 | 
					        """Redirect to family page after upload"""
 | 
				
			||||||
        return reverse_lazy('family:family_detail', kwargs={'pk': self.object.id})
 | 
					        return reverse_lazy('family:family_detail', kwargs={'pk': self.object.pk})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def post(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					        form = self.get_form()
 | 
				
			||||||
 | 
					        self.object = self.get_object()
 | 
				
			||||||
 | 
					        return self.form_valid(form) if form.is_valid() else self.form_invalid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @transaction.atomic
 | 
					    @transaction.atomic
 | 
				
			||||||
    def form_valid(self, form):
 | 
					    def form_valid(self, form):
 | 
				
			||||||
@@ -141,6 +152,11 @@ class FamilyPictureUpdateView(PictureUpdateView):
 | 
				
			|||||||
            else:
 | 
					            else:
 | 
				
			||||||
                image.name = "{}_pic.png".format(self.object.pk)
 | 
					                image.name = "{}_pic.png".format(self.object.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Save
 | 
				
			||||||
 | 
					        self.object.display_image = image
 | 
				
			||||||
 | 
					        self.object.save()
 | 
				
			||||||
 | 
					        return super().form_valid(form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FamilyAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
					class FamilyAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@@ -282,8 +298,8 @@ class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
 | 
				
			|||||||
            PermissionBackend.filter_queryset(self.request, Challenge, "view")
 | 
					            PermissionBackend.filter_queryset(self.request, Challenge, "view")
 | 
				
			||||||
        ).order_by('name')
 | 
					        ).order_by('name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        context["can_add_family"] = PermissionBackend.check_perm(self.request, "family.add_family")
 | 
					        context["can_add_family"] = PermissionBackend.check_perm(self.request, "family.family_create")
 | 
				
			||||||
        context["can_add_challenge"] = PermissionBackend.check_perm(self.request, "family.add_challenge")
 | 
					        context["can_add_challenge"] = PermissionBackend.check_perm(self.request, "family.challenge_create")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return context
 | 
					        return context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -294,7 +310,7 @@ class FamilyManageView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
 | 
				
			|||||||
        return table
 | 
					        return table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AchievementsView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):
 | 
					class AchievementListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    List all achievements
 | 
					    List all achievements
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
@@ -333,12 +349,11 @@ class AchievementValidateView(ProtectQuerysetMixin, LoginRequiredMixin, Template
 | 
				
			|||||||
    template_name = 'family/achievement_confirm_validate.html'
 | 
					    template_name = 'family/achievement_confirm_validate.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, request, pk):
 | 
					    def post(self, request, pk):
 | 
				
			||||||
        # On récupère l'objet à valider
 | 
					 | 
				
			||||||
        achievement = Achievement.objects.get(pk=pk)
 | 
					        achievement = Achievement.objects.get(pk=pk)
 | 
				
			||||||
        # On modifie le champ valid
 | 
					
 | 
				
			||||||
        achievement.valid = True
 | 
					        achievement.valid = True
 | 
				
			||||||
        achievement.save()
 | 
					        achievement.save()
 | 
				
			||||||
        # On redirige vers la page de détail ou la liste
 | 
					
 | 
				
			||||||
        return redirect(reverse_lazy('family:achievement_list'))
 | 
					        return redirect(reverse_lazy('family:achievement_list'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user