mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-07-04 22:54:05 +02:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
6c7d86185a |
0
apps/family/__init__.py
Normal file
0
apps/family/__init__.py
Normal file
3
apps/family/admin.py
Normal file
3
apps/family/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
11
apps/family/apps.py
Normal file
11
apps/family/apps.py
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class FamilyConfig(AppConfig):
|
||||
name = 'family'
|
||||
verbose_name = _('family')
|
0
apps/family/migrations/__init__.py
Normal file
0
apps/family/migrations/__init__.py
Normal file
165
apps/family/models.py
Normal file
165
apps/family/models.py
Normal file
@ -0,0 +1,165 @@
|
||||
from django.db import models, transaction
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class Family(models.Model):
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('name'),
|
||||
unique=True
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('description')
|
||||
)
|
||||
|
||||
score = models.PositiveIntegerField(
|
||||
verbose_name=_('score')
|
||||
)
|
||||
|
||||
rank = models.PositiveIntegerField(
|
||||
verbose_name=_('rank'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Family')
|
||||
verbose_name_plural = _('Families')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class FamilyMembership(models.Model):
|
||||
user = models.OneToOneField(
|
||||
User,
|
||||
on_delete=models.PROTECT,
|
||||
related_name=_('family_memberships'),
|
||||
verbose_name=_('user'),
|
||||
)
|
||||
|
||||
family = models.ForeignKey(
|
||||
Family,
|
||||
on_delete=models.PROTECT,
|
||||
related_name=_('members'),
|
||||
verbose_name=_('family'),
|
||||
)
|
||||
|
||||
year = models.PositiveIntegerField(
|
||||
verbose_name=_('year'),
|
||||
default=timezone.now().year,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('family membership')
|
||||
verbose_name_plural = _('family memberships')
|
||||
|
||||
def __str__(self):
|
||||
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):
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('name'),
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
max_length=255,
|
||||
verbose_name=_('description'),
|
||||
)
|
||||
|
||||
points = models.PositiveIntegerField(
|
||||
verbose_name=_('points'),
|
||||
)
|
||||
|
||||
category = models.ForeignKey(
|
||||
ChallengeCategory,
|
||||
verbose_name=_('category'),
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('challenge')
|
||||
verbose_name_plural = _('challenges')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Achievement(models.Model):
|
||||
challenge = models.ForeignKey(
|
||||
Challenge,
|
||||
on_delete=models.PROTECT,
|
||||
|
||||
)
|
||||
family = models.ForeignKey(
|
||||
Family,
|
||||
on_delete=models.PROTECT,
|
||||
verbose_name=_('family'),
|
||||
)
|
||||
|
||||
obtained_at = models.DateTimeField(
|
||||
verbose_name=_('obtained at'),
|
||||
default=timezone.now,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('achievement')
|
||||
verbose_name_plural = _('achievements')
|
||||
|
||||
def __str__(self):
|
||||
return _('Challenge {challenge} carried out by Family {family}').format(challenge=self.challenge.name, family=self.family.name, )
|
||||
|
||||
@transaction.atomic
|
||||
def save(self, *args, **kwargs):
|
||||
"""
|
||||
When saving, also grants points to the family
|
||||
"""
|
||||
self.family = Family.objects.select_for_update().get(pk=self.family_id)
|
||||
challenge_points = self.challenge.points
|
||||
is_new = self.pk is None
|
||||
|
||||
super.save(*args, **kwargs)
|
||||
|
||||
# Only grant points when getting a new achievement
|
||||
if is_new:
|
||||
self.family.refresh_from_db()
|
||||
self.family.score += challenge_points
|
||||
self.family._force_save = True
|
||||
self.family.save()
|
||||
|
||||
@transaction.atomic
|
||||
def delete(self, *args, **kwargs):
|
||||
"""
|
||||
When deleting, also removes points from the family
|
||||
"""
|
||||
# Get the family and challenge before deletion
|
||||
self.family = Family.objects.select_for_update().get(pk=self.family_id)
|
||||
challenge_points = self.challenge.points
|
||||
|
||||
# Delete the achievement
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
# Remove points from the family
|
||||
self.family.refresh_from_db()
|
||||
self.family.score -= challenge_points
|
||||
self.family._force_save = True
|
||||
self.family.save()
|
3
apps/family/tests.py
Normal file
3
apps/family/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
apps/family/views.py
Normal file
3
apps/family/views.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
@ -70,6 +70,7 @@ INSTALLED_APPS = [
|
||||
# Note apps
|
||||
'api',
|
||||
'activity',
|
||||
'family',
|
||||
'food',
|
||||
'logs',
|
||||
'member',
|
||||
@ -270,7 +271,7 @@ OAUTH2_PROVIDER = {
|
||||
'PKCE_REQUIRED': False, # PKCE (fix a breaking change of django-oauth-toolkit 2.0.0)
|
||||
'OIDC_ENABLED': True,
|
||||
'OIDC_RSA_PRIVATE_KEY':
|
||||
os.getenv('OIDC_RSA_PRIVATE_KEY', '/var/secrets/oidc.key'),
|
||||
os.getenv('OIDC_RSA_PRIVATE_KEY', 'CHANGE_ME_IN_ENV_SETTINGS').replace('\\n', '\n'), # for multilines
|
||||
'SCOPES': { 'openid': "OpenID Connect scope" },
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user