Commit 14282427 authored by erdnaxe's avatar erdnaxe 🎇

Merge branch 'models' into 'master'

Not so atomic, sorry

See merge request !2
parents ab616b3f 90ffaae2
Pipeline #1353 passed with stage
in 2 minutes and 26 seconds
[run]
source =
adherents
activity
member
note
theme
omit =
adherents/tests/*.py
adherents/migrations/*.py
activity/tests/*.py
activity/migrations/*.py
member/tests/*.py
member/migrations/*.py
note/tests/*.py
note/migrations/*.py
theme/tests/*.py
\ No newline at end of file
......@@ -36,3 +36,6 @@ settings_local.py
env/
venv/
db.sqlite3
# Ignore migrations during first phase dev
migrations/
......@@ -2,4 +2,4 @@
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
default_app_config = 'adherents.apps.AdherentsConfig'
default_app_config = 'activity.apps.ActivityConfig'
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib import admin
from .models import Activity, ActivityType, Guest
class ActivityAdmin(admin.ModelAdmin):
"""
Admin customisation for Activity
"""
list_display = ('name', 'activity_type', 'organizer')
list_filter = ('activity_type',)
search_fields = ['name', 'organizer__name']
# Organize activities by start date
date_hierarchy = 'date_start'
ordering = ['-date_start']
class ActivityTypeAdmin(admin.ModelAdmin):
"""
Admin customisation for ActivityType
"""
list_display = ('name', 'can_invite', 'guest_entry_fee')
# Register your models here.
admin.site.register(Activity, ActivityAdmin)
admin.site.register(ActivityType, ActivityTypeAdmin)
admin.site.register(Guest)
......@@ -6,6 +6,6 @@ from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class AdherentsConfig(AppConfig):
name = 'adherents'
verbose_name = _('adherents')
class ActivityConfig(AppConfig):
name = 'activity'
verbose_name = _('activity')
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-07-08 13:45+0200\n"
"POT-Creation-Date: 2019-07-16 13:45+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -18,34 +13,62 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: models.py:26
msgid "phone number"
msgstr "numéro de téléphone"
#: apps.py:11 models.py:61
msgid "activity"
msgstr "activité"
#: models.py:30
msgid "section"
msgstr "section"
#: models.py:12 models.py:29
msgid "name"
msgstr "nom"
#: models.py:31
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
#: models.py:16
msgid "can invite"
msgstr "peut inviter"
#: models.py:35 models.py:36
msgid "user profile"
msgstr "profil utilisateur"
#: models.py:19
msgid "guest entry fee"
msgstr "cotisation de l'entrée invité"
#: models.py:52
msgid "date"
msgstr "date"
#: models.py:23
msgid "activity type"
msgstr "type d'activité"
#: models.py:57
msgid "amount"
msgstr "montant"
#: models.py:24
msgid "activity types"
msgstr "types d'activité"
#: models.py:33
msgid "description"
msgstr "description"
#: models.py:39
msgid "type"
msgstr "type"
#: models.py:45
msgid "organizer"
msgstr "organisateur"
#: models.py:51
msgid "attendees club"
msgstr ""
#: models.py:54
msgid "start date"
msgstr "date de début"
#: models.py:61
msgid "membership fee"
msgstr "cotisation"
#: models.py:57
msgid "end date"
msgstr "date de fin"
#: models.py:62
msgid "membership fees"
msgstr "cotisations"
msgid "activities"
msgstr "activités"
#: models.py:88
msgid "guest"
msgstr "invité"
#: models.py:89
msgid "guests"
msgstr "invités"
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
class ActivityType(models.Model):
name = models.CharField(
verbose_name=_('name'),
max_length=255,
)
can_invite = models.BooleanField(
verbose_name=_('can invite'),
)
guest_entry_fee = models.PositiveIntegerField(
verbose_name=_('guest entry fee'),
)
class Meta:
verbose_name = _("activity type")
verbose_name_plural = _("activity types")
def __str__(self):
return self.name
class Activity(models.Model):
name = models.CharField(
verbose_name=_('name'),
max_length=255,
)
description = models.TextField(
verbose_name=_('description'),
)
activity_type = models.ForeignKey(
ActivityType,
on_delete=models.PROTECT,
related_name='+',
verbose_name=_('type'),
)
organizer = models.ForeignKey(
'member.Club',
on_delete=models.PROTECT,
related_name='+',
verbose_name=_('organizer'),
)
attendees_club = models.ForeignKey(
'member.Club',
on_delete=models.PROTECT,
related_name='+',
verbose_name=_('attendees club'),
)
date_start = models.DateTimeField(
verbose_name=_('start date'),
)
date_end = models.DateTimeField(
verbose_name=_('end date'),
)
class Meta:
verbose_name = _("activity")
verbose_name_plural = _("activities")
class Guest(models.Model):
activity = models.ForeignKey(
Activity,
on_delete=models.PROTECT,
related_name='+',
)
name = models.CharField(
max_length=255,
)
inviter = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
related_name='+',
)
entry = models.DateTimeField(
null=True,
)
entry_transaction = models.ForeignKey(
'note.Transaction',
on_delete=models.PROTECT,
)
class Meta:
verbose_name = _("guest")
verbose_name_plural = _("guests")
# Generated by Django 2.2.3 on 2019-07-16 07:17
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('avatar', models.ImageField(blank=True, max_length=255, upload_to='', verbose_name='profile picture')),
('phone_number', models.CharField(blank=True, default='', max_length=50, null=True, verbose_name='phone number')),
('section', models.CharField(help_text='e.g. "1A0", "9A♥", "SAPHIRE"', max_length=255, verbose_name='section')),
('genre', models.CharField(blank=True, choices=[(None, 'ND'), ('M', 'M'), ('F', 'F')], max_length=1, null=True)),
('address', models.TextField(blank=True, null=True)),
('paid', models.BooleanField(default=False, verbose_name='paid')),
('is_active', models.BooleanField(default=True, verbose_name='is active')),
('is_deleted', models.BooleanField(default=False, verbose_name='is deleted')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'user profile',
'verbose_name_plural': 'user profile',
},
),
migrations.CreateModel(
name='MembershipFee',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(max_length=255, verbose_name='date')),
('amount', models.DecimalField(decimal_places=2, max_digits=5, verbose_name='amount')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'membership fee',
'verbose_name_plural': 'membership fees',
},
),
]
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
default_app_config = 'member.apps.MemberConfig'
......@@ -7,7 +7,7 @@ from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .forms import CustomUserChangeForm
from .models import Profile
from .models import Club, Membership, Profile, Role
class ProfileInline(admin.StackedInline):
......@@ -33,5 +33,11 @@ class CustomUserAdmin(UserAdmin):
return super().get_inline_instances(request, obj)
# Update Django User with profile
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
# Add other models
admin.site.register(Club)
admin.site.register(Membership)
admin.site.register(Role)
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class MemberConfig(AppConfig):
name = 'member'
verbose_name = _('member')
......@@ -10,6 +10,7 @@ class CustomUserChangeForm(UserChangeForm):
Make first name, last name and email required
in the default Django Auth User model
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['first_name'].required = True
......
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-07-16 15:21+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: apps.py:11
msgid "member"
msgstr "adhérent"
#: models.py:24
msgid "phone number"
msgstr "numéro de téléphone"
#: models.py:30
msgid "section"
msgstr "section"
#: models.py:31
msgid "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
msgstr "e.g. \"1A0\", \"9A♥\", \"SAPHIRE\""
#: models.py:37
msgid "address"
msgstr "adresse"
#: models.py:43
msgid "paid"
msgstr "payé"
#: models.py:48 models.py:49
msgid "user profile"
msgstr "profil utilisateur"
#: models.py:57 models.py:102
msgid "name"
msgstr "nom"
#: models.py:62
msgid "email"
msgstr "courriel"
#: models.py:67
msgid "membership fee"
msgstr "cotisation pour adhérer"
#: models.py:71
msgid "membership duration"
msgstr "durée de l'adhésion"
#: models.py:72
msgid "The longest time a membership can last (NULL = infinite)."
msgstr "La durée maximale d'une adhésion (NULL = infinie)."
#: models.py:77
msgid "membership start"
msgstr "début de l'adhésion"
#: models.py:78
msgid "How long after January 1st the members can renew their membership."
msgstr ""
#: models.py:83
msgid "membership end"
msgstr "fin de l'adhésion"
#: models.py:84
msgid ""
"How long the membership can last after January 1st of the next year after "
"members can renew their membership."
msgstr ""
#: models.py:90
msgid "club"
msgstr "club"
#: models.py:91
msgid "clubs"
msgstr "clubs"
#: models.py:108
msgid "role"
msgstr "rôle"
#: models.py:109
msgid "roles"
msgstr "rôles"
#: models.py:126
msgid "membership starts on"
msgstr "l'adhésion commence le"
#: models.py:129
msgid "membership ends on"
msgstr "l'adhésion finie le"
#: models.py:133
msgid "fee"
msgstr "cotisation"
#: models.py:137
msgid "membership"
msgstr "adhésion"
#: models.py:138
msgid "memberships"
msgstr "adhésions"
......@@ -3,7 +3,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
......@@ -17,40 +16,26 @@ class Profile(models.Model):
We do not want to patch the Django Contrib Auth User class
so this model add an user profile with additional information.
"""
GENRES = [
(None, "ND"),
("M", "M"),
("F", "F"),
]
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
avatar = models.ImageField(
max_length=255,
blank=True,
verbose_name=_('profile picture'),
)
phone_number = models.CharField(
verbose_name=_('phone number'),
max_length=50,
blank=True,
null=True,
default='',
verbose_name=_('phone number'),
)
section = models.CharField(
max_length=255,
verbose_name=_('section'),
help_text=_('e.g. "1A0", "9A♥", "SAPHIRE"'),
)
genre = models.CharField(
max_length=1,
max_length=255,
blank=True,
null=True,
choices=GENRES,
)
address = models.TextField(
address = models.CharField(
verbose_name=_('address'),
max_length=255,
blank=True,
null=True,
)
......@@ -58,44 +43,102 @@ class Profile(models.Model):
verbose_name=_("paid"),
default=False,
)
is_active = models.BooleanField(
verbose_name=_("is active"),
default=True,
)
is_deleted = models.BooleanField(
verbose_name=_("is deleted"),
default=False,
)
class Meta:
verbose_name = _('user profile')
verbose_name_plural = _('user profile')
class MembershipFee(models.Model):
class Club(models.Model):
"""
A student club
"""
name = models.CharField(
verbose_name=_('name'),
max_length=255,
unique=True,
)
email = models.EmailField(
verbose_name=_('email'),
)
# Memberships
membership_fee = models.PositiveIntegerField(
verbose_name=_('membership fee'),
)
membership_duration = models.DurationField(
null=True,
verbose_name=_('membership duration'),
help_text=_('The longest time a membership can last '
'(NULL = infinite).'),
)
membership_start = models.DurationField(
null=True,
verbose_name=_('membership start'),
help_text=_('How long after January 1st the members can renew '
'their membership.'),
)
membership_end = models.DurationField(
null=True,
verbose_name=_('membership end'),
help_text=_('How long the membership can last after January 1st '
'of the next year after members can renew their '
'membership.'),
)
class Meta:
verbose_name = _("club")
verbose_name_plural = _("clubs")
def __str__(self):
return self.name
class Role(models.Model):
"""
User can become member by paying a membership fee
Role that an user can have in a club
"""
name = models.CharField(
verbose_name=_('name'),
max_length=255,
unique=True,
)
class Meta:
verbose_name = _('role')
verbose_name_plural = _('roles')
class Membership(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
on_delete=models.PROTECT
)
date = models.DateField(
max_length=255,
verbose_name=_('date'),
club = models.ForeignKey(
Club,
on_delete=models.PROTECT
)
roles = models.ForeignKey(
Role,
on_delete=models.PROTECT
)
date_start = models.DateField(
verbose_name=_('membership starts on'),
)
date_end = models.DateField(
verbose_name=_('membership ends on'),
null=True,
)
amount = models.DecimalField(
max_digits=5, # Max 999.99 €
decimal_places=2,
verbose_name=_('amount'),
fee = models.PositiveIntegerField(
verbose_name=_('fee'),
)
class Meta:
verbose_name = _('membership fee')
verbose_name_plural = _('membership fees')
verbose_name = _('membership')
verbose_name_plural = _('memberships')
@receiver(post_save, sender=User)
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def save_user_profile(instance, created, **_kwargs):
"""
Hook to save an user profile when an user is updated
......
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib import admin
from .models import NoteClub, NoteSpec, NoteUser
from .models import Alias
from .models.notes import Alias, NoteClub, NoteSpecial, NoteUser
from .models.transactions import MembershipTransaction, Transaction, \
TransactionTemplate
class AliasInlines(admin.TabularInline):
"""
Define user and club aliases when editing their note
"""
extra = 0
model = Alias
class NoteClubAdmin(admin.ModelAdmin):
"""
Admin customisation for NoteClub
"""
inlines = (AliasInlines,)
list_display = ('club', 'balance', 'is_active')
list_filter = ('is_active',)
search_fields = ['club__name']
# We can't change club after creation
readonly_fields = ('club',)
def has_add_permission(self, request):
"""
A club note should not be manually added
"""
return False
def has_delete_permission(self, request, obj=None):
"""
A club note should not be manually removed
"""
return False
class NoteSpecialAdmin(admin.ModelAdmin):
"""
Admin customisation for NoteSpecial
"""
list_display = ('special_type', 'balance', 'is_active')
class NoteUserAdmin(admin.ModelAdmin):
"""
Admin customisation for NoteUser
"""
inlines = (AliasInlines,)
list_display = ('user', 'balance', 'is_active')
list_filter = ('is_active',)
search_fields = ['user__username']
# Organize note by registration date
date_hierarchy = 'user__date_joined'
ordering = ['-user__date_joined']
# We can't change user after creation
readonly_fields = ('user',)
def has_add_permission(self, request):
"""
An user note should not be manually added
"""
return False
def has_delete_permission(self, request, obj=None):