Make erdnaxe be happy

This commit is contained in:
Yohann D'ANELLO 2020-03-07 22:28:59 +01:00
parent 3eb0c185f2
commit d0206fb790
30 changed files with 215 additions and 205 deletions

View File

@ -11,7 +11,7 @@ class ActivityAdmin(admin.ModelAdmin):
Admin customisation for Activity Admin customisation for Activity
""" """
list_display = ('name', 'activity_type', 'organizer') list_display = ('name', 'activity_type', 'organizer')
list_filter = ('activity_type', ) list_filter = ('activity_type',)
search_fields = ['name', 'organizer__name'] search_fields = ['name', 'organizer__name']
# Organize activities by start date # Organize activities by start date

View File

@ -11,6 +11,7 @@ class ActivityTypeSerializer(serializers.ModelSerializer):
REST API Serializer for Activity types. REST API Serializer for Activity types.
The djangorestframework plugin will analyse the model `ActivityType` and parse all fields in the API. The djangorestframework plugin will analyse the model `ActivityType` and parse all fields in the API.
""" """
class Meta: class Meta:
model = ActivityType model = ActivityType
fields = '__all__' fields = '__all__'
@ -21,6 +22,7 @@ class ActivitySerializer(serializers.ModelSerializer):
REST API Serializer for Activities. REST API Serializer for Activities.
The djangorestframework plugin will analyse the model `Activity` and parse all fields in the API. The djangorestframework plugin will analyse the model `Activity` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Activity model = Activity
fields = '__all__' fields = '__all__'
@ -31,6 +33,7 @@ class GuestSerializer(serializers.ModelSerializer):
REST API Serializer for Guests. REST API Serializer for Guests.
The djangorestframework plugin will analyse the model `Guest` and parse all fields in the API. The djangorestframework plugin will analyse the model `Guest` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Guest model = Guest
fields = '__all__' fields = '__all__'

View File

@ -3,8 +3,8 @@
from rest_framework import viewsets from rest_framework import viewsets
from ..models import ActivityType, Activity, Guest
from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer from .serializers import ActivityTypeSerializer, ActivitySerializer, GuestSerializer
from ..models import ActivityType, Activity, Guest
class ActivityTypeViewSet(viewsets.ModelViewSet): class ActivityTypeViewSet(viewsets.ModelViewSet):

View File

@ -14,6 +14,7 @@ class UserSerializer(serializers.ModelSerializer):
REST API Serializer for Users. REST API Serializer for Users.
The djangorestframework plugin will analyse the model `User` and parse all fields in the API. The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
""" """
class Meta: class Meta:
model = User model = User
exclude = ( exclude = (

View File

@ -1,11 +1,11 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _
class Changelog(models.Model): class Changelog(models.Model):

View File

@ -7,6 +7,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core import serializers from django.core import serializers
from django.db.models.signals import pre_save, post_save, post_delete from django.db.models.signals import pre_save, post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from .models import Changelog from .models import Changelog
@ -56,7 +57,7 @@ EXCLUDED = [
'sessions.session', 'sessions.session',
'reversion.revision', 'reversion.revision',
'reversion.version', 'reversion.version',
] ]
@receiver(pre_save) @receiver(pre_save)

View File

@ -18,9 +18,9 @@ class ProfileInline(admin.StackedInline):
class CustomUserAdmin(UserAdmin): class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, ) inlines = (ProfileInline,)
list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff') list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff')
list_select_related = ('profile', ) list_select_related = ('profile',)
form = ProfileForm form = ProfileForm
def get_inline_instances(self, request, obj=None): def get_inline_instances(self, request, obj=None):

View File

@ -11,6 +11,7 @@ class ProfileSerializer(serializers.ModelSerializer):
REST API Serializer for Profiles. REST API Serializer for Profiles.
The djangorestframework plugin will analyse the model `Profile` and parse all fields in the API. The djangorestframework plugin will analyse the model `Profile` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Profile model = Profile
fields = '__all__' fields = '__all__'
@ -21,6 +22,7 @@ class ClubSerializer(serializers.ModelSerializer):
REST API Serializer for Clubs. REST API Serializer for Clubs.
The djangorestframework plugin will analyse the model `Club` and parse all fields in the API. The djangorestframework plugin will analyse the model `Club` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Club model = Club
fields = '__all__' fields = '__all__'
@ -31,6 +33,7 @@ class RoleSerializer(serializers.ModelSerializer):
REST API Serializer for Roles. REST API Serializer for Roles.
The djangorestframework plugin will analyse the model `Role` and parse all fields in the API. The djangorestframework plugin will analyse the model `Role` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Role model = Role
fields = '__all__' fields = '__all__'
@ -41,6 +44,7 @@ class MembershipSerializer(serializers.ModelSerializer):
REST API Serializer for Memberships. REST API Serializer for Memberships.
The djangorestframework plugin will analyse the model `Memberships` and parse all fields in the API. The djangorestframework plugin will analyse the model `Memberships` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Membership model = Membership
fields = '__all__' fields = '__all__'

View File

@ -3,8 +3,8 @@
from rest_framework import viewsets from rest_framework import viewsets
from ..models import Profile, Club, Role, Membership
from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer from .serializers import ProfileSerializer, ClubSerializer, RoleSerializer, MembershipSerializer
from ..models import Profile, Club, Role, Membership
class ProfileViewSet(viewsets.ModelViewSet): class ProfileViewSet(viewsets.ModelViewSet):

View File

@ -1,11 +1,11 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django_filters import FilterSet, CharFilter
from django.contrib.auth.models import User
from django.db.models import CharField
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit from crispy_forms.layout import Layout, Submit
from django.contrib.auth.models import User
from django.db.models import CharField
from django_filters import FilterSet, CharFilter
class UserFilter(FilterSet): class UserFilter(FilterSet):

View File

@ -1,23 +1,22 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from crispy_forms.bootstrap import Div
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout
from dal import autocomplete from dal import autocomplete
from django import forms
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django import forms
from .models import Profile, Club, Membership from .models import Profile, Club, Membership
from crispy_forms.helper import FormHelper
from crispy_forms.bootstrap import Div
from crispy_forms.layout import Layout
class SignUpForm(UserCreationForm): class SignUpForm(UserCreationForm):
def __init__(self,*args,**kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs) super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.pop("autofocus", None) self.fields['username'].widget.attrs.pop("autofocus", None)
self.fields['first_name'].widget.attrs.update({"autofocus":"autofocus"}) self.fields['first_name'].widget.attrs.update({"autofocus": "autofocus"})
class Meta: class Meta:
model = User model = User
@ -28,6 +27,7 @@ class ProfileForm(forms.ModelForm):
""" """
A form for the extras field provided by the :model:`member.Profile` model. A form for the extras field provided by the :model:`member.Profile` model.
""" """
class Meta: class Meta:
model = Profile model = Profile
fields = '__all__' fields = '__all__'
@ -42,7 +42,7 @@ class ClubForm(forms.ModelForm):
class AddMembersForm(forms.Form): class AddMembersForm(forms.Form):
class Meta: class Meta:
fields = ('', ) fields = ('',)
class MembershipForm(forms.ModelForm): class MembershipForm(forms.ModelForm):

View File

@ -3,8 +3,8 @@
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
class Profile(models.Model): class Profile(models.Model):
@ -48,7 +48,7 @@ class Profile(models.Model):
verbose_name_plural = _('user profile') verbose_name_plural = _('user profile')
def get_absolute_url(self): def get_absolute_url(self):
return reverse('user_detail', args=(self.pk, )) return reverse('user_detail', args=(self.pk,))
class Club(models.Model): class Club(models.Model):
@ -97,7 +97,7 @@ class Club(models.Model):
return self.name return self.name
def get_absolute_url(self): def get_absolute_url(self):
return reverse_lazy('member:club_detail', args=(self.pk, )) return reverse_lazy('member:club_detail', args=(self.pk,))
class Role(models.Model): class Role(models.Model):
@ -153,7 +153,6 @@ class Membership(models.Model):
verbose_name = _('membership') verbose_name = _('membership')
verbose_name_plural = _('memberships') verbose_name_plural = _('memberships')
# @receiver(post_save, sender=settings.AUTH_USER_MODEL) # @receiver(post_save, sender=settings.AUTH_USER_MODEL)
# def save_user_profile(instance, created, **_kwargs): # def save_user_profile(instance, created, **_kwargs):
# """ # """

View File

@ -1,6 +1,7 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
def save_user_profile(instance, created, raw, **_kwargs): def save_user_profile(instance, created, raw, **_kwargs):
""" """
Hook to create and save a profile when an user is updated if it is not registered with the signup form Hook to create and save a profile when an user is updated if it is not registered with the signup form

View File

@ -1,33 +1,33 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView,DeleteView
from django.views.generic.edit import FormMixin
from django.contrib.auth.models import User
from django.contrib import messages
from django.urls import reverse_lazy
from django.http import HttpResponseRedirect
from django.db.models import Q
from django.core.exceptions import ValidationError
from django.conf import settings
from django_tables2.views import SingleTableView
from rest_framework.authtoken.models import Token
from dal import autocomplete
from PIL import Image
import io import io
from PIL import Image
from dal import autocomplete
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, UpdateView, TemplateView, DeleteView
from django.views.generic.edit import FormMixin
from django_tables2.views import SingleTableView
from rest_framework.authtoken.models import Token
from note.forms import AliasForm, ImageForm
from note.models import Alias, NoteUser from note.models import Alias, NoteUser
from note.models.transactions import Transaction from note.models.transactions import Transaction
from note.tables import HistoryTable, AliasTable from note.tables import HistoryTable, AliasTable
from note.forms import AliasForm, ImageForm
from .models import Profile, Club, Membership
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
from .tables import ClubTable, UserTable
from .filters import UserFilter, UserFilterFormHelper from .filters import UserFilter, UserFilterFormHelper
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
from .models import Club, Membership
from .tables import ClubTable, UserTable
class UserCreateView(CreateView): class UserCreateView(CreateView):
@ -109,7 +109,7 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
return reverse_lazy('member:user_detail', return reverse_lazy('member:user_detail',
kwargs={'pk': kwargs['id']}) kwargs={'pk': kwargs['id']})
else: else:
return reverse_lazy('member:user_detail', args=(self.object.id, )) return reverse_lazy('member:user_detail', args=(self.object.id,))
class UserDetailView(LoginRequiredMixin, DetailView): class UserDetailView(LoginRequiredMixin, DetailView):
@ -157,13 +157,14 @@ class UserListView(LoginRequiredMixin, SingleTableView):
context["filter"] = self.filter context["filter"] = self.filter
return context return context
class AliasView(LoginRequiredMixin,FormMixin,DetailView):
class AliasView(LoginRequiredMixin, FormMixin, DetailView):
model = User model = User
template_name = 'member/profile_alias.html' template_name = 'member/profile_alias.html'
context_object_name = 'user_object' context_object_name = 'user_object'
form_class = AliasForm form_class = AliasForm
def get_context_data(self,**kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
note = context['user_object'].note note = context['user_object'].note
context["aliases"] = AliasTable(note.alias_set.all()) context["aliases"] = AliasTable(note.alias_set.all())
@ -172,7 +173,7 @@ class AliasView(LoginRequiredMixin,FormMixin,DetailView):
def get_success_url(self): def get_success_url(self):
return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id}) return reverse_lazy('member:user_alias', kwargs={'pk': self.object.id})
def post(self,request,*args,**kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
if form.is_valid(): if form.is_valid():
@ -186,41 +187,44 @@ class AliasView(LoginRequiredMixin,FormMixin,DetailView):
alias.save() alias.save()
return super().form_valid(form) return super().form_valid(form)
class DeleteAliasView(LoginRequiredMixin, DeleteView): class DeleteAliasView(LoginRequiredMixin, DeleteView):
model = Alias model = Alias
def delete(self,request,*args,**kwargs): def delete(self, request, *args, **kwargs):
try: try:
self.object = self.get_object() self.object = self.get_object()
self.object.delete() self.object.delete()
except ValidationError as e: except ValidationError as e:
# TODO: pass message to redirected view. # TODO: pass message to redirected view.
messages.error(self.request,str(e)) messages.error(self.request, str(e))
else: else:
messages.success(self.request,_("Alias successfully deleted")) messages.success(self.request, _("Alias successfully deleted"))
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):
print(self.request) print(self.request)
return reverse_lazy('member:user_alias',kwargs={'pk':self.object.note.user.pk}) return reverse_lazy('member:user_alias', kwargs={'pk': self.object.note.user.pk})
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs) return self.post(request, *args, **kwargs)
class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView): class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
model = User model = User
template_name = 'member/profile_picture_update.html' template_name = 'member/profile_picture_update.html'
context_object_name = 'user_object' context_object_name = 'user_object'
form_class = ImageForm form_class = ImageForm
def get_context_data(self,*args,**kwargs):
context = super().get_context_data(*args,**kwargs) def get_context_data(self, *args, **kwargs):
context['form'] = self.form_class(self.request.POST,self.request.FILES) context = super().get_context_data(*args, **kwargs)
context['form'] = self.form_class(self.request.POST, self.request.FILES)
return context return context
def get_success_url(self): def get_success_url(self):
return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id}) return reverse_lazy('member:user_detail', kwargs={'pk': self.object.id})
def post(self,request,*args,**kwargs): def post(self, request, *args, **kwargs):
form = self.get_form() form = self.get_form()
self.object = self.get_object() self.object = self.get_object()
if form.is_valid(): if form.is_valid():
@ -230,7 +234,7 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
print(form) print(form)
return self.form_invalid(form) return self.form_invalid(form)
def form_valid(self,form): def form_valid(self, form):
image_field = form.cleaned_data['image'] image_field = form.cleaned_data['image']
x = form.cleaned_data['x'] x = form.cleaned_data['x']
y = form.cleaned_data['y'] y = form.cleaned_data['y']
@ -238,15 +242,15 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
h = form.cleaned_data['height'] h = form.cleaned_data['height']
# image crop and resize # image crop and resize
image_file = io.BytesIO(image_field.read()) image_file = io.BytesIO(image_field.read())
ext = image_field.name.split('.')[-1].lower() # ext = image_field.name.split('.')[-1].lower()
#TODO: support GIF format # TODO: support GIF format
image = Image.open(image_file) image = Image.open(image_file)
image = image.crop((x, y, x+w, y+h)) image = image.crop((x, y, x + w, y + h))
image_clean = image.resize((settings.PIC_WIDTH, image_clean = image.resize((settings.PIC_WIDTH,
settings.PIC_RATIO*settings.PIC_WIDTH), settings.PIC_RATIO * settings.PIC_WIDTH),
Image.ANTIALIAS) Image.ANTIALIAS)
image_file = io.BytesIO() image_file = io.BytesIO()
image_clean.save(image_file,"PNG") image_clean.save(image_file, "PNG")
image_field.file = image_file image_field.file = image_file
# renaming # renaming
filename = "{}_pic.png".format(self.object.note.pk) filename = "{}_pic.png".format(self.object.note.pk)
@ -283,6 +287,7 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
""" """
Auto complete users by usernames Auto complete users by usernames
""" """
def get_queryset(self): def get_queryset(self):
""" """
Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion. Quand une personne cherche un utilisateur par pseudo, une requête est envoyée sur l'API dédiée à l'auto-complétion.

View File

@ -47,11 +47,11 @@ class NoteClubAdmin(PolymorphicChildModelAdmin):
""" """
Child for a club note, see NoteAdmin Child for a club note, see NoteAdmin
""" """
inlines = (AliasInlines, ) inlines = (AliasInlines,)
# We can't change club after creation or the balance # We can't change club after creation or the balance
readonly_fields = ('club', 'balance') readonly_fields = ('club', 'balance')
search_fields = ('club', ) search_fields = ('club',)
def has_add_permission(self, request): def has_add_permission(self, request):
""" """
@ -71,7 +71,7 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin):
""" """
Child for a special note, see NoteAdmin Child for a special note, see NoteAdmin
""" """
readonly_fields = ('balance', ) readonly_fields = ('balance',)
@admin.register(NoteUser) @admin.register(NoteUser)
@ -79,7 +79,7 @@ class NoteUserAdmin(PolymorphicChildModelAdmin):
""" """
Child for an user note, see NoteAdmin Child for an user note, see NoteAdmin
""" """
inlines = (AliasInlines, ) inlines = (AliasInlines,)
# We can't change user after creation or the balance # We can't change user after creation or the balance
readonly_fields = ('user', 'balance') readonly_fields = ('user', 'balance')
@ -133,7 +133,7 @@ class TransactionAdmin(PolymorphicParentModelAdmin):
Else the amount of money would not be transferred Else the amount of money would not be transferred
""" """
if obj: # user is editing an existing object if obj: # user is editing an existing object
return 'created_at', 'source', 'destination', 'quantity',\ return 'created_at', 'source', 'destination', 'quantity', \
'amount' 'amount'
return [] return []
@ -143,9 +143,9 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
""" """
Admin customisation for TransactionTemplate Admin customisation for TransactionTemplate
""" """
list_display = ('name', 'poly_destination', 'amount', 'category', 'display', ) list_display = ('name', 'poly_destination', 'amount', 'category', 'display',)
list_filter = ('category', 'display') list_filter = ('category', 'display')
autocomplete_fields = ('destination', ) autocomplete_fields = ('destination',)
def poly_destination(self, obj): def poly_destination(self, obj):
""" """
@ -161,5 +161,5 @@ class TemplateCategoryAdmin(admin.ModelAdmin):
""" """
Admin customisation for TransactionTemplate Admin customisation for TransactionTemplate
""" """
list_display = ('name', ) list_display = ('name',)
list_filter = ('name', ) list_filter = ('name',)

View File

@ -13,6 +13,7 @@ class NoteSerializer(serializers.ModelSerializer):
REST API Serializer for Notes. REST API Serializer for Notes.
The djangorestframework plugin will analyse the model `Note` and parse all fields in the API. The djangorestframework plugin will analyse the model `Note` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Note model = Note
fields = '__all__' fields = '__all__'
@ -29,6 +30,7 @@ class NoteClubSerializer(serializers.ModelSerializer):
REST API Serializer for Club's notes. REST API Serializer for Club's notes.
The djangorestframework plugin will analyse the model `NoteClub` and parse all fields in the API. The djangorestframework plugin will analyse the model `NoteClub` and parse all fields in the API.
""" """
class Meta: class Meta:
model = NoteClub model = NoteClub
fields = '__all__' fields = '__all__'
@ -39,6 +41,7 @@ class NoteSpecialSerializer(serializers.ModelSerializer):
REST API Serializer for special notes. REST API Serializer for special notes.
The djangorestframework plugin will analyse the model `NoteSpecial` and parse all fields in the API. The djangorestframework plugin will analyse the model `NoteSpecial` and parse all fields in the API.
""" """
class Meta: class Meta:
model = NoteSpecial model = NoteSpecial
fields = '__all__' fields = '__all__'
@ -49,6 +52,7 @@ class NoteUserSerializer(serializers.ModelSerializer):
REST API Serializer for User's notes. REST API Serializer for User's notes.
The djangorestframework plugin will analyse the model `NoteUser` and parse all fields in the API. The djangorestframework plugin will analyse the model `NoteUser` and parse all fields in the API.
""" """
class Meta: class Meta:
model = NoteUser model = NoteUser
fields = '__all__' fields = '__all__'
@ -59,6 +63,7 @@ class AliasSerializer(serializers.ModelSerializer):
REST API Serializer for Aliases. REST API Serializer for Aliases.
The djangorestframework plugin will analyse the model `Alias` and parse all fields in the API. The djangorestframework plugin will analyse the model `Alias` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Alias model = Alias
fields = '__all__' fields = '__all__'
@ -78,6 +83,7 @@ class TransactionTemplateSerializer(serializers.ModelSerializer):
REST API Serializer for Transaction templates. REST API Serializer for Transaction templates.
The djangorestframework plugin will analyse the model `TransactionTemplate` and parse all fields in the API. The djangorestframework plugin will analyse the model `TransactionTemplate` and parse all fields in the API.
""" """
class Meta: class Meta:
model = TransactionTemplate model = TransactionTemplate
fields = '__all__' fields = '__all__'
@ -88,6 +94,7 @@ class TransactionSerializer(serializers.ModelSerializer):
REST API Serializer for Transactions. REST API Serializer for Transactions.
The djangorestframework plugin will analyse the model `Transaction` and parse all fields in the API. The djangorestframework plugin will analyse the model `Transaction` and parse all fields in the API.
""" """
class Meta: class Meta:
model = Transaction model = Transaction
fields = '__all__' fields = '__all__'
@ -98,6 +105,7 @@ class MembershipTransactionSerializer(serializers.ModelSerializer):
REST API Serializer for Membership transactions. REST API Serializer for Membership transactions.
The djangorestframework plugin will analyse the model `MembershipTransaction` and parse all fields in the API. The djangorestframework plugin will analyse the model `MembershipTransaction` and parse all fields in the API.
""" """
class Meta: class Meta:
model = MembershipTransaction model = MembershipTransaction
fields = '__all__' fields = '__all__'

View File

@ -4,11 +4,11 @@
from django.db.models import Q from django.db.models import Q
from rest_framework import viewsets from rest_framework import viewsets
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \ from .serializers import NoteSerializer, NotePolymorphicSerializer, NoteClubSerializer, NoteSpecialSerializer, \
NoteUserSerializer, AliasSerializer, \ NoteUserSerializer, AliasSerializer, \
TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer TransactionTemplateSerializer, TransactionSerializer, MembershipTransactionSerializer
from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction
class NoteViewSet(viewsets.ModelViewSet): class NoteViewSet(viewsets.ModelViewSet):

View File

@ -3,31 +3,25 @@
from dal import autocomplete from dal import autocomplete
from django import forms from django import forms
from django.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import os from .models import Alias
from crispy_forms.helper import FormHelper
from crispy_forms.bootstrap import Div
from crispy_forms.layout import Layout, HTML
from .models import Transaction, TransactionTemplate, TemplateTransaction from .models import Transaction, TransactionTemplate, TemplateTransaction
from .models import Note, Alias
class AliasForm(forms.ModelForm): class AliasForm(forms.ModelForm):
class Meta: class Meta:
model = Alias model = Alias
fields = ("name",) fields = ("name",)
def __init__(self,*args,**kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs) super().__init__(*args, **kwargs)
self.fields["name"].label = False self.fields["name"].label = False
self.fields["name"].widget.attrs={"placeholder":_('New Alias')} self.fields["name"].widget.attrs = {"placeholder": _('New Alias')}
class ImageForm(forms.Form): class ImageForm(forms.Form):
image = forms.ImageField(required = False, image = forms.ImageField(required=False,
label=_('select an image'), label=_('select an image'),
help_text=_('Maximal size: 2MB')) help_text=_('Maximal size: 2MB'))
x = forms.FloatField(widget=forms.HiddenInput()) x = forms.FloatField(widget=forms.HiddenInput())
@ -62,7 +56,6 @@ class TransactionForm(forms.ModelForm):
def save(self, commit=True): def save(self, commit=True):
super().save(commit) super().save(commit)
def clean(self): def clean(self):
""" """
If the user has no right to transfer funds, then it will be the source of the transfer by default. If the user has no right to transfer funds, then it will be the source of the transfer by default.
@ -70,7 +63,7 @@ class TransactionForm(forms.ModelForm):
""" """
cleaned_data = super().clean() cleaned_data = super().clean()
if not "source" in cleaned_data: # TODO Replace it with "if %user has no right to transfer funds" if "source" not in cleaned_data: # TODO Replace it with "if %user has no right to transfer funds"
cleaned_data["source"] = self.user.note cleaned_data["source"] = self.user.note
if cleaned_data["source"].pk == cleaned_data["destination"].pk: if cleaned_data["source"].pk == cleaned_data["destination"].pk:
@ -78,7 +71,6 @@ class TransactionForm(forms.ModelForm):
return cleaned_data return cleaned_data
class Meta: class Meta:
model = Transaction model = Transaction
fields = ( fields = (
@ -122,7 +114,7 @@ class ConsoForm(forms.ModelForm):
class Meta: class Meta:
model = TemplateTransaction model = TemplateTransaction
fields = ('source', ) fields = ('source',)
# Le champ d'utilisateur est remplacé par un champ d'auto-complétion. # Le champ d'utilisateur est remplacé par un champ d'auto-complétion.
# Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion # Quand des lettres sont tapées, une requête est envoyée sur l'API d'auto-complétion

View File

@ -9,6 +9,7 @@ from django.core.validators import RegexValidator
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
""" """
Defines each note types Defines each note types
""" """
@ -27,7 +28,7 @@ class Note(PolymorphicModel):
help_text=_('in centimes, money credited for this instance'), help_text=_('in centimes, money credited for this instance'),
default=0, default=0,
) )
last_negative= models.DateTimeField( last_negative = models.DateTimeField(
verbose_name=_('last negative date'), verbose_name=_('last negative date'),
help_text=_('last time the balance was negative'), help_text=_('last time the balance was negative'),
null=True, null=True,
@ -98,7 +99,7 @@ class Note(PolymorphicModel):
# Alias exists, so check if it is linked to this note # Alias exists, so check if it is linked to this note
if aliases.first().note != self: if aliases.first().note != self:
raise ValidationError(_('This alias is already taken.'), raise ValidationError(_('This alias is already taken.'),
code="same_alias",) code="same_alias", )
else: else:
# Alias does not exist yet, so check if it can exist # Alias does not exist yet, so check if it can exist
a = Alias(name=str(self)) a = Alias(name=str(self))

View File

@ -2,9 +2,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.db import models from django.db import models
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
from .notes import Note, NoteClub from .notes import Note, NoteClub
@ -44,7 +44,7 @@ class TransactionTemplate(models.Model):
verbose_name=_('name'), verbose_name=_('name'),
max_length=255, max_length=255,
unique=True, unique=True,
error_messages={'unique':_("A template with this name already exist")}, error_messages={'unique': _("A template with this name already exist")},
) )
destination = models.ForeignKey( destination = models.ForeignKey(
NoteClub, NoteClub,
@ -63,7 +63,7 @@ class TransactionTemplate(models.Model):
max_length=31, max_length=31,
) )
display = models.BooleanField( display = models.BooleanField(
default = True, default=True,
) )
description = models.CharField( description = models.CharField(
verbose_name=_('description'), verbose_name=_('description'),
@ -75,7 +75,7 @@ class TransactionTemplate(models.Model):
verbose_name_plural = _("transaction templates") verbose_name_plural = _("transaction templates")
def get_absolute_url(self): def get_absolute_url(self):
return reverse('note:template_update', args=(self.pk, )) return reverse('note:template_update', args=(self.pk,))
class Transaction(PolymorphicModel): class Transaction(PolymorphicModel):
@ -168,6 +168,7 @@ class TemplateTransaction(Transaction):
on_delete=models.PROTECT, on_delete=models.PROTECT,
) )
class MembershipTransaction(Transaction): class MembershipTransaction(Transaction):
""" """
Special type of :model:`note.Transaction` associated to a :model:`member.Membership`. Special type of :model:`note.Transaction` associated to a :model:`member.Membership`.

View File

@ -4,8 +4,10 @@
import django_tables2 as tables import django_tables2 as tables
from django.db.models import F from django.db.models import F
from django_tables2.utils import A from django_tables2.utils import A
from .models.transactions import Transaction
from .models.notes import Alias from .models.notes import Alias
from .models.transactions import Transaction
class HistoryTable(tables.Table): class HistoryTable(tables.Table):
class Meta: class Meta:
@ -25,6 +27,7 @@ class HistoryTable(tables.Table):
.order_by(('-' if is_descending else '') + 'total') .order_by(('-' if is_descending else '') + 'total')
return (queryset, True) return (queryset, True)
class AliasTable(tables.Table): class AliasTable(tables.Table):
class Meta: class Meta:
attrs = { attrs = {
@ -32,14 +35,14 @@ class AliasTable(tables.Table):
'table table condensed table-striped table-hover' 'table table condensed table-striped table-hover'
} }
model = Alias model = Alias
fields =('name',) fields = ('name',)
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
show_header = False show_header = False
name = tables.Column(attrs={'td':{'class':'text-center'}}) name = tables.Column(attrs={'td': {'class': 'text-center'}})
delete = tables.LinkColumn('member:user_alias_delete', delete = tables.LinkColumn('member:user_alias_delete',
args=[A('pk')], args=[A('pk')],
attrs={ attrs={
'td': {'class':'col-sm-2'}, 'td': {'class': 'col-sm-2'},
'a': {'class': 'btn btn-danger'} }, 'a': {'class': 'btn btn-danger'}},
text='delete',accessor='pk') text='delete', accessor='pk')

View File

@ -8,8 +8,8 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, ListView, UpdateView from django.views.generic import CreateView, ListView, UpdateView
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
from .models import Transaction, TransactionTemplate, Alias, TemplateTransaction
class TransactionCreate(LoginRequiredMixin, CreateView): class TransactionCreate(LoginRequiredMixin, CreateView):
@ -53,6 +53,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
""" """
Auto complete note by aliases Auto complete note by aliases
""" """
def get_queryset(self): def get_queryset(self):
""" """
Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion. Quand une personne cherche un alias, une requête est envoyée sur l'API dédiée à l'auto-complétion.
@ -66,7 +67,7 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
# self.q est le paramètre de la recherche # self.q est le paramètre de la recherche
if self.q: if self.q:
qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q)))\ qs = qs.filter(Q(name__regex=self.q) | Q(normalized_name__regex=Alias.normalize(self.q))) \
.order_by('normalized_name').distinct() .order_by('normalized_name').distinct()
# Filtrage par type de note (user, club, special) # Filtrage par type de note (user, club, special)
@ -147,4 +148,3 @@ class ConsoView(LoginRequiredMixin, CreateView):
When clicking a button, reload the same page When clicking a button, reload the same page
""" """
return reverse('note:consos') return reverse('note:consos')

View File

@ -1,10 +1,6 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.http import HttpResponseRedirect
from urllib.parse import urlencode, parse_qs, urlsplit, urlunsplit
class TurbolinksMiddleware(object): class TurbolinksMiddleware(object):
""" """
@ -35,4 +31,3 @@ class TurbolinksMiddleware(object):
location = request.session.pop('_turbolinks_redirect_to') location = request.session.pop('_turbolinks_redirect_to')
response['Turbolinks-Location'] = location response['Turbolinks-Location'] = location
return response return response

View File

@ -1,8 +1,6 @@
import os
import re
from .base import * from .base import *
def read_env(): def read_env():
"""Pulled from Honcho code with minor updates, reads local default """Pulled from Honcho code with minor updates, reads local default
environment variables from a .env file located in the project root environment variables from a .env file located in the project root
@ -25,14 +23,16 @@ def read_env():
val = re.sub(r'\\(.)', r'\1', m3.group(1)) val = re.sub(r'\\(.)', r'\1', m3.group(1))
os.environ.setdefault(key, val) os.environ.setdefault(key, val)
read_env() read_env()
app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev') app_stage = os.environ.get('DJANGO_APP_STAGE', 'dev')
if app_stage == 'prod': if app_stage == 'prod':
from .production import * from .production import *
DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD','CHANGE_ME_IN_ENV_SETTINGS')
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY','CHANGE_ME_IN_ENV_SETTINGS') DATABASES["default"]["PASSWORD"] = os.environ.get('DJANGO_DB_PASSWORD', 'CHANGE_ME_IN_ENV_SETTINGS')
ALLOWED_HOSTS.append(os.environ.get('ALLOWED_HOSTS','localhost')) SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'CHANGE_ME_IN_ENV_SETTINGS')
ALLOWED_HOSTS.append(os.environ.get('ALLOWED_HOSTS', 'localhost'))
else: else:
from .development import * from .development import *
@ -43,4 +43,3 @@ except ImportError:
# env variables set at the of in /env/bin/activate # env variables set at the of in /env/bin/activate
# don't forget to unset in deactivate ! # don't forget to unset in deactivate !

View File

@ -183,7 +183,7 @@ FIXTURE_DIRS = [os.path.join(BASE_DIR, "note_kfet/fixtures")]
# Don't put anything in this directory yourself; store your static files # Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS. # in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/" # Example: "/var/www/example.com/static/"
STATIC_ROOT = os.path.join(BASE_DIR,"static/") STATIC_ROOT = os.path.join(BASE_DIR, "static/")
# STATICFILES_DIRS = [ # STATICFILES_DIRS = [
# os.path.join(BASE_DIR, 'static')] # os.path.join(BASE_DIR, 'static')]
STATICFILES_DIRS = [] STATICFILES_DIRS = []
@ -195,8 +195,8 @@ STATIC_URL = '/static/'
ALIAS_VALIDATOR_REGEX = r'' ALIAS_VALIDATOR_REGEX = r''
MEDIA_ROOT=os.path.join(BASE_DIR,"media") MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL='/media/' MEDIA_URL = '/media/'
# Profile Picture Settings # Profile Picture Settings
PIC_WIDTH = 200 PIC_WIDTH = 200
@ -211,7 +211,7 @@ CAS_SHOW_POWERED = False
CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False CAS_REDIRECT_TO_LOGIN_AFTER_LOGOUT = False
CAS_INFO_MESSAGES = { CAS_INFO_MESSAGES = {
"cas_explained": { "cas_explained": {
"message":_( "message": _(
u"The Central Authentication Service grants you access to most of our websites by " u"The Central Authentication Service grants you access to most of our websites by "
u"authenticating only once, so you don't need to type your credentials again unless " u"authenticating only once, so you don't need to type your credentials again unless "
u"your session expires or you logout." u"your session expires or you logout."
@ -224,4 +224,3 @@ CAS_INFO_MESSAGES = {
CAS_INFO_MESSAGES_ORDER = [ CAS_INFO_MESSAGES_ORDER = [
'cas_explained', 'cas_explained',
] ]

View File

@ -11,10 +11,11 @@
# - and more ... # - and more ...
import os
# Database # Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
from . import * from . import *
import os
DATABASES = { DATABASES = {
'default': { 'default': {
@ -53,8 +54,6 @@ SESSION_COOKIE_AGE = 60 * 60 * 3
# Can be modified in secrets.py # Can be modified in secrets.py
CAS_SERVER_URL = "http://localhost:8000/cas/" CAS_SERVER_URL = "http://localhost:8000/cas/"
STATIC_ROOT = '' # not needed in development settings STATIC_ROOT = '' # not needed in development settings
STATICFILES_DIRS = [ STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')] os.path.join(BASE_DIR, 'static')]

View File

@ -26,7 +26,7 @@ DATABASES = {
DEBUG = True DEBUG = True
# Mandatory ! # Mandatory !
ALLOWED_HOSTS = ['127.0.0.1','note.comby.xyz'] ALLOWED_HOSTS = ['127.0.0.1', 'note.comby.xyz']
# Emails # Emails
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

View File

@ -1,13 +1,12 @@
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from cas import views as cas_views
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.views.generic import RedirectView from django.views.generic import RedirectView
from django.conf.urls.static import static
from django.conf import settings
from cas import views as cas_views
urlpatterns = [ urlpatterns = [
# Dev so redirect to something random # Dev so redirect to something random
@ -36,5 +35,5 @@ urlpatterns = [
path('logs/', include('logs.urls')), path('logs/', include('logs.urls')),
] ]
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

View File

@ -1,11 +1,11 @@
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header /> <SOAP-ENV:Header />
<SOAP-ENV:Body> <SOAP-ENV:Body>
<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" <Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol"
xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" IssueInstant="{{IssueInstant}}" IssueInstant="{{ IssueInstant }}"
MajorVersion="1" MinorVersion="1" Recipient="{{Recipient}}" MajorVersion="1" MinorVersion="1" Recipient="{{ Recipient }}"
ResponseID="{{ResponseID}}"> ResponseID="{{ ResponseID }}">
<Status> <Status>
<StatusCode Value="samlp:Success"> <StatusCode Value="samlp:Success">
</StatusCode> </StatusCode>

View File

@ -1,11 +1,11 @@
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header /> <SOAP-ENV:Header />
<SOAP-ENV:Body> <SOAP-ENV:Body>
<Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" <Response xmlns="urn:oasis:names:tc:SAML:1.0:protocol"
xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" IssueInstant="{{IssueInstant}}" IssueInstant="{{ IssueInstant }}"
MajorVersion="1" MinorVersion="1" Recipient="{{Recipient}}" MajorVersion="1" MinorVersion="1" Recipient="{{ Recipient }}"
ResponseID="{{ResponseID}}"> ResponseID="{{ ResponseID }}">
<Status> <Status>
<StatusCode Value="samlp:{{code}}">{{msg}}</StatusCode> <StatusCode Value="samlp:{{code}}">{{msg}}</StatusCode>
</Status> </Status>