1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-07-18 15:20:19 +02:00

Models fixed

This commit is contained in:
Ehouarn
2025-07-06 18:11:09 +02:00
parent f6ad6197de
commit c7bd733911
2 changed files with 59 additions and 76 deletions

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.21 on 2025-07-04 19:05 # Generated by Django 4.2.21 on 2025-07-06 16:07
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -16,14 +16,17 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='ChallengeCategory', name='Challenge',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True, verbose_name='name')), ('name', models.CharField(max_length=255, verbose_name='name')),
('description', models.CharField(max_length=255, verbose_name='description')),
('points', models.PositiveIntegerField(verbose_name='points')),
('obtained', models.PositiveIntegerField(default=0, verbose_name='obtained')),
], ],
options={ options={
'verbose_name': 'challenge category', 'verbose_name': 'challenge',
'verbose_name_plural': 'challenge categories', 'verbose_name_plural': 'challenges',
}, },
), ),
migrations.CreateModel( migrations.CreateModel(
@ -32,7 +35,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, unique=True, verbose_name='name')), ('name', models.CharField(max_length=255, unique=True, verbose_name='name')),
('description', models.CharField(max_length=255, verbose_name='description')), ('description', models.CharField(max_length=255, verbose_name='description')),
('score', models.PositiveIntegerField(verbose_name='score')), ('score', models.PositiveIntegerField(default=0, verbose_name='score')),
('rank', models.PositiveIntegerField(verbose_name='rank')), ('rank', models.PositiveIntegerField(verbose_name='rank')),
], ],
options={ options={
@ -40,21 +43,6 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Families', 'verbose_name_plural': 'Families',
}, },
), ),
migrations.CreateModel(
name='Challenge',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='name')),
('description', models.CharField(max_length=255, verbose_name='description')),
('points', models.PositiveIntegerField(verbose_name='points')),
('obtained', models.PositiveIntegerField(verbose_name='obtained')),
('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='family.challengecategory', verbose_name='category')),
],
options={
'verbose_name': 'challenge',
'verbose_name_plural': 'challenges',
},
),
migrations.CreateModel( migrations.CreateModel(
name='Achievement', name='Achievement',
fields=[ fields=[

View File

@ -11,16 +11,17 @@ class Family(models.Model):
name = models.CharField( name = models.CharField(
max_length=255, max_length=255,
verbose_name=_('name'), verbose_name=_('name'),
unique=True unique=True,
) )
description = models.CharField( description = models.CharField(
max_length=255, max_length=255,
verbose_name=_('description') verbose_name=_('description'),
) )
score = models.PositiveIntegerField( score = models.PositiveIntegerField(
verbose_name=_('score') verbose_name=_('score'),
default=0,
) )
rank = models.PositiveIntegerField( rank = models.PositiveIntegerField(
@ -34,6 +35,36 @@ class Family(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
def update_score(self, *args, **kwargs):
challenge_set = Challenge.objects.select_for_update().filter(achievement__family=self)
points_sum = challenge_set.aggregate(models.Sum("points"))
self.score = points_sum["points__sum"]
self.save()
self.update_ranking()
@staticmethod
def update_ranking(*args, **kwargs):
"""
Update ranking when adding or removing points
"""
family_set = Family.objects.select_for_update().all().order_by("-score")
for i in range(family_set.count()):
if i == 0 or family_set[i].score != family_set[i - 1].score:
new_rank = i + 1
family = family_set[i]
family.rank = new_rank
family._force_save = True
family.save()
def save(self, *args, **kwargs):
if self.rank is None:
last_family = Family.objects.order_by("rank").last()
if last_family is None or last_family.score > self.score:
self.rank = Family.objects.count() + 1
else:
self.rank = last_family.rank
super().save(*args, **kwargs)
class FamilyMembership(models.Model): class FamilyMembership(models.Model):
user = models.OneToOneField( user = models.OneToOneField(
@ -64,21 +95,6 @@ class FamilyMembership(models.Model):
return _('Family membership of {user} to {family}').format(user=self.user.username, family=self.family.name, ) return _('Family membership of {user} to {family}').format(user=self.user.username, family=self.family.name, )
class ChallengeCategory(models.Model):
name = models.CharField(
max_length=255,
verbose_name=_('name'),
unique=True,
)
class Meta:
verbose_name = _('challenge category')
verbose_name_plural = _('challenge categories')
def __str__(self):
return self.name
class Challenge(models.Model): class Challenge(models.Model):
name = models.CharField( name = models.CharField(
max_length=255, max_length=255,
@ -94,15 +110,18 @@ class Challenge(models.Model):
verbose_name=_('points'), verbose_name=_('points'),
) )
category = models.ForeignKey( obtained = models.PositiveIntegerField(
ChallengeCategory, verbose_name=_('obtained'),
verbose_name=_('category'), default=0,
on_delete=models.PROTECT
) )
obtained = models.PositiveIntegerField( @transaction.atomic
verbose_name=_('obtained') def save(self, *args, **kwargs):
) super().save(*args, **kwargs)
# Update families who already obtained this challenge
achievements = Achievement.objects.filter(challenge=self)
for achievement in achievements:
achievement.save()
class Meta: class Meta:
verbose_name = _('challenge') verbose_name = _('challenge')
@ -136,20 +155,6 @@ class Achievement(models.Model):
def __str__(self): def __str__(self):
return _('Challenge {challenge} carried out by Family {family}').format(challenge=self.challenge.name, family=self.family.name, ) return _('Challenge {challenge} carried out by Family {family}').format(challenge=self.challenge.name, family=self.family.name, )
@classmethod
def update_ranking(cls, *args, **kwargs):
"""
Update ranking when adding or removing points
"""
family_set = cls.objects.select_for_update().all().order_by("-score")
for i in range(family_set.count()):
if i == 0 or family_set[i].score != family_set[i - 1].score:
new_rank = i + 1
family = family_set[i]
family.rank = new_rank
family._force_save = True
family.save()
@transaction.atomic @transaction.atomic
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
""" """
@ -157,25 +162,20 @@ 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)
challenge_points = self.challenge.points
is_new = self.pk is None is_new = self.pk is None
super.save(*args, **kwargs) super().save(*args, **kwargs)
# Only grant points when getting a new achievement self.family.refresh_from_db()
self.family.update_score()
# Count only when getting a new achievement
if is_new: if is_new:
self.family.refresh_from_db()
self.family.score += challenge_points
self.family._force_save = True
self.family.save()
self.challenge.refresh_from_db() self.challenge.refresh_from_db()
self.challenge.obtained += 1 self.challenge.obtained += 1
self.challenge._force_save = True self.challenge._force_save = True
self.challenge.save() self.challenge.save()
self.__class__.update_ranking()
@transaction.atomic @transaction.atomic
def delete(self, *args, **kwargs): def delete(self, *args, **kwargs):
""" """
@ -183,20 +183,15 @@ class Achievement(models.Model):
""" """
# Get the family and challenge before deletion # Get the family and challenge before deletion
self.family = Family.objects.select_for_update().get(pk=self.family_id) self.family = Family.objects.select_for_update().get(pk=self.family_id)
challenge_points = self.challenge.points
# Delete the achievement # Delete the achievement
super().delete(*args, **kwargs) super().delete(*args, **kwargs)
# Remove points from the family # Remove points from the family
self.family.refresh_from_db() self.family.refresh_from_db()
self.family.score -= challenge_points self.family.update_score()
self.family._force_save = True
self.family.save()
self.challenge.refresh_from_db() self.challenge.refresh_from_db()
self.challenge.obtained -= 1 self.challenge.obtained -= 1
self.challenge._force_save = True self.challenge._force_save = True
self.challenge.save() self.challenge.save()
self.__class__.update_ranking()