Format code

This commit is contained in:
Alexandre Iooss 2020-02-18 12:31:15 +01:00
parent 0d7db68372
commit f89d91e524
No known key found for this signature in database
GPG Key ID: 6C79278F3FCDCC02
19 changed files with 262 additions and 167 deletions

View File

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

View File

@ -5,6 +5,7 @@
from ..models import ActivityType, Activity, Guest
from rest_framework import serializers
class ActivityTypeSerializer(serializers.ModelSerializer):
"""
REST API Serializer for Activity types.

View File

@ -17,10 +17,13 @@ class UserSerializer(serializers.ModelSerializer):
REST API Serializer for Users.
The djangorestframework plugin will analyse the model `User` and parse all fields in the API.
"""
class Meta:
model = User
exclude = ('password', 'groups', 'user_permissions',)
exclude = (
'password',
'groups',
'user_permissions',
)
class UserViewSet(viewsets.ModelViewSet):

View File

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

View File

@ -2,30 +2,35 @@
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from django_filters import FilterSet, CharFilter,NumberFilter
from django_filters import FilterSet, CharFilter, NumberFilter
from django.contrib.auth.models import User
from django.db.models import CharField
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit
from .models import Club
from .models import Club
class UserFilter(FilterSet):
class Meta:
model = User
fields = ['last_name','first_name','username','profile__section']
filter_overrides={
CharField:{
'filter_class':CharFilter,
'extra': lambda f:{
'lookup_expr':'icontains'
fields = ['last_name', 'first_name', 'username', 'profile__section']
filter_overrides = {
CharField: {
'filter_class': CharFilter,
'extra': lambda f: {
'lookup_expr': 'icontains'
}
}
}
class UserFilterFormHelper(FormHelper):
form_method = 'GET'
layout = Layout(
'last_name','first_name','username','profile__section',
Submit('Submit','Apply Filter'),
'last_name',
'first_name',
'username',
'profile__section',
Submit('Submit', 'Apply Filter'),
)

View File

@ -16,11 +16,11 @@ from crispy_forms.bootstrap import InlineField, FormActions, StrictButton, Div,
from crispy_forms.layout import Layout
class SignUpForm(UserCreationForm):
class Meta:
model = User
fields = ['first_name','last_name','username','email']
fields = ['first_name', 'last_name', 'username', 'email']
class ProfileForm(forms.ModelForm):
"""
@ -31,48 +31,56 @@ class ProfileForm(forms.ModelForm):
fields = '__all__'
exclude = ['user']
class ClubForm(forms.ModelForm):
class Meta:
model = Club
fields ='__all__'
fields = '__all__'
class AddMembersForm(forms.Form):
class Meta:
fields = ('',)
fields = ('', )
class MembershipForm(forms.ModelForm):
class Meta:
model = Membership
fields = ('user','roles','date_start')
fields = ('user', 'roles', 'date_start')
# 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
# et récupère les noms d'utilisateur valides
widgets = {
'user': autocomplete.ModelSelect2(url='member:user_autocomplete',
attrs={
'data-placeholder': 'Nom ...',
'data-minimum-input-length': 1,
}),
'user':
autocomplete.ModelSelect2(
url='member:user_autocomplete',
attrs={
'data-placeholder': 'Nom ...',
'data-minimum-input-length': 1,
},
),
}
MemberFormSet = forms.modelformset_factory(Membership,
form=MembershipForm,
extra=2,
can_delete=True)
MemberFormSet = forms.modelformset_factory(
Membership,
form=MembershipForm,
extra=2,
can_delete=True,
)
class FormSetHelper(FormHelper):
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_tag = False
self.form_method = 'POST'
self.form_class='form-inline'
self.form_class = 'form-inline'
# self.template = 'bootstrap/table_inline_formset.html'
self.layout = Layout(
Div(
Div('user',css_class='col-sm-2'),
Div('roles',css_class='col-sm-2'),
Div('date_start',css_class='col-sm-2'),
Div('user', css_class='col-sm-2'),
Div('roles', css_class='col-sm-2'),
Div('date_start', css_class='col-sm-2'),
css_class="row formset-row",
)
)
))

View File

@ -9,6 +9,7 @@ from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from django.urls import reverse, reverse_lazy
class Profile(models.Model):
"""
An user profile
@ -50,7 +51,8 @@ class Profile(models.Model):
verbose_name_plural = _('user profile')
def get_absolute_url(self):
return reverse('user_detail',args=(self.pk,))
return reverse('user_detail', args=(self.pk, ))
class Club(models.Model):
"""
@ -98,7 +100,7 @@ class Club(models.Model):
return self.name
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):
@ -129,15 +131,15 @@ class Membership(models.Model):
"""
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT
on_delete=models.PROTECT,
)
club = models.ForeignKey(
Club,
on_delete=models.PROTECT
on_delete=models.PROTECT,
)
roles = models.ForeignKey(
Role,
on_delete=models.PROTECT
on_delete=models.PROTECT,
)
date_start = models.DateField(
verbose_name=_('membership starts on'),

View File

@ -1,6 +1,3 @@
#!/usr/bin/env python
# -*- mode: python; coding: utf-8 -*-
# Copyright (C) 2018-2019 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later

View File

@ -5,15 +5,20 @@ from .models import Club
from django.conf import settings
from django.contrib.auth.models import User
class ClubTable(tables.Table):
class Meta:
attrs = {'class':'table table-bordered table-condensed table-striped table-hover'}
attrs = {
'class':
'table table-bordered table-condensed table-striped table-hover'
}
model = Club
template_name = 'django_tables2/bootstrap.html'
fields = ('id','name','email')
row_attrs = {'class':'table-row',
'data-href': lambda record: record.pk }
fields = ('id', 'name', 'email')
row_attrs = {
'class': 'table-row',
'data-href': lambda record: record.pk
}
class UserTable(tables.Table):
@ -21,7 +26,10 @@ class UserTable(tables.Table):
solde = tables.Column(accessor='note.balance')
class Meta:
attrs = {'class':'table table-bordered table-condensed table-striped table-hover'}
attrs = {
'class':
'table table-bordered table-condensed table-striped table-hover'
}
template_name = 'django_tables2/bootstrap.html'
fields = ('last_name','first_name','username','email')
fields = ('last_name', 'first_name', 'username', 'email')
model = User

View File

@ -16,14 +16,14 @@ from rest_framework.authtoken.models import Token
from note.models import Alias, Note, NoteUser
from .models import Profile, Club, Membership
from .forms import SignUpForm, ProfileForm, ClubForm,MembershipForm, MemberFormSet,FormSetHelper
from .tables import ClubTable,UserTable
from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper
from .tables import ClubTable, UserTable
from .filters import UserFilter, UserFilterFormHelper
from note.models.transactions import Transaction
from note.tables import HistoryTable
class UserCreateView(CreateView):
"""
Une vue pour inscrire un utilisateur et lui créer un profile
@ -31,10 +31,10 @@ class UserCreateView(CreateView):
form_class = SignUpForm
success_url = reverse_lazy('login')
template_name ='member/signup.html'
template_name = 'member/signup.html'
second_form = ProfileForm
def get_context_data(self,**kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["profile_form"] = self.second_form()
@ -49,17 +49,20 @@ class UserCreateView(CreateView):
profile.save()
return super().form_valid(form)
class UserUpdateView(LoginRequiredMixin,UpdateView):
class UserUpdateView(LoginRequiredMixin, UpdateView):
model = User
fields = ['first_name','last_name','username','email']
fields = ['first_name', 'last_name', 'username', 'email']
template_name = 'member/profile_update.html'
second_form = ProfileForm
def get_context_data(self,**kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_modified'] = context['user']
context['user'] = self.request.user
context["profile_form"] = self.second_form(instance=context['user_modified'].profile)
context["profile_form"] = self.second_form(
instance=context['user_modified'].profile)
return context
@ -71,44 +74,52 @@ class UserUpdateView(LoginRequiredMixin,UpdateView):
new_username = form.data['username']
# Si l'utilisateur cherche à modifier son pseudo, le nouveau pseudo ne doit pas être proche d'un alias existant
note = NoteUser.objects.filter(alias__normalized_name=Alias.normalize(new_username))
note = NoteUser.objects.filter(
alias__normalized_name=Alias.normalize(new_username))
if note.exists() and note.get().user != self.request.user:
form.add_error('username', _("An alias with a similar name already exists."))
form.add_error('username',
_("An alias with a similar name already exists."))
return form
def form_valid(self, form):
profile_form = ProfileForm(data=self.request.POST,instance=self.request.user.profile)
profile_form = ProfileForm(
data=self.request.POST,
instance=self.request.user.profile,
)
if form.is_valid() and profile_form.is_valid():
new_username = form.data['username']
alias = Alias.objects.filter(name=new_username)
# Si le nouveau pseudo n'est pas un de nos alias, on supprime éventuellement un alias similaire pour le remplacer
if not alias.exists():
similar = Alias.objects.filter(normalized_name=Alias.normalize(new_username))
similar = Alias.objects.filter(
normalized_name=Alias.normalize(new_username))
if similar.exists():
similar.delete()
user = form.save(commit=False)
profile = profile_form.save(commit=False)
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
user.save()
return super().form_valid(form)
def get_success_url(self, **kwargs):
if kwargs:
return reverse_lazy('member:user_detail', kwargs = {'pk': kwargs['id']})
if kwargs:
return reverse_lazy('member:user_detail',
kwargs={'pk': kwargs['id']})
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):
"""
Affiche les informations sur un utilisateur, sa note, ses clubs ...
"""
model = Profile
context_object_name = "profile"
def get_context_data(slef,**kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = context['profile'].user
history_list = \
@ -119,7 +130,8 @@ class UserDetailView(LoginRequiredMixin,DetailView):
context['club_list'] = ClubTable(club_list)
return context
class UserListView(LoginRequiredMixin,SingleTableView):
class UserListView(LoginRequiredMixin, SingleTableView):
"""
Affiche la liste des utilisateurs, avec une fonction de recherche statique
"""
@ -129,13 +141,13 @@ class UserListView(LoginRequiredMixin,SingleTableView):
filter_class = UserFilter
formhelper_class = UserFilterFormHelper
def get_queryset(self,**kwargs):
def get_queryset(self, **kwargs):
qs = super().get_queryset()
self.filter = self.filter_class(self.request.GET,queryset=qs)
self.filter = self.filter_class(self.request.GET, queryset=qs)
self.filter.form.helper = self.formhelper_class()
return self.filter.qs
def get_context_data(self,**kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["filter"] = self.filter
return context
@ -149,22 +161,25 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
template_name = "member/manage_auth_tokens.html"
def get(self, request, *args, **kwargs):
if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists():
if 'regenerate' in request.GET and Token.objects.filter(
user=request.user).exists():
Token.objects.get(user=self.request.user).delete()
return redirect(reverse_lazy('member:auth_token') + "?show", permanent=True)
return redirect(reverse_lazy('member:auth_token') + "?show",
permanent=True)
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
context['token'] = Token.objects.get_or_create(
user=self.request.user)[0]
return context
class UserAutocomplete(autocomplete.Select2QuerySetView):
"""
Auto complete users by usernames
"""
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.
@ -181,32 +196,36 @@ class UserAutocomplete(autocomplete.Select2QuerySetView):
return qs
###################################
############## CLUB ###############
###################################
class ClubCreateView(LoginRequiredMixin,CreateView):
class ClubCreateView(LoginRequiredMixin, CreateView):
"""
Create Club
"""
model = Club
form_class = ClubForm
def form_valid(self,form):
def form_valid(self, form):
return super().form_valid(form)
class ClubListView(LoginRequiredMixin,SingleTableView):
class ClubListView(LoginRequiredMixin, SingleTableView):
"""
List existing Clubs
"""
model = Club
table_class = ClubTable
class ClubDetailView(LoginRequiredMixin,DetailView):
model = Club
context_object_name="club"
def get_context_data(self,**kwargs):
class ClubDetailView(LoginRequiredMixin, DetailView):
model = Club
context_object_name = "club"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
club = context["club"]
club_transactions = \
@ -218,23 +237,25 @@ class ClubDetailView(LoginRequiredMixin,DetailView):
context['member_list'] = club_member
return context
class ClubAddMemberView(LoginRequiredMixin,CreateView):
class ClubAddMemberView(LoginRequiredMixin, CreateView):
model = Membership
form_class = MembershipForm
template_name = 'member/add_members.html'
def get_context_data(self,**kwargs):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['formset'] = MemberFormSet()
context['helper'] = FormSetHelper()
return context
def post(self,request,*args,**kwargs):
def post(self, request, *args, **kwargs):
formset = MembershipFormset(request.POST)
if formset.is_valid():
return self.form_valid(formset)
else:
return self.form_invalid(formset)
def form_valid(self,formset):
def form_valid(self, formset):
formset.save()
return super().form_valid(formset)

View File

@ -25,7 +25,10 @@ class NoteAdmin(PolymorphicParentModelAdmin):
Parent regrouping all note types as children
"""
child_models = (NoteClub, NoteSpecial, NoteUser)
list_filter = (PolymorphicChildModelFilter, 'is_active',)
list_filter = (
PolymorphicChildModelFilter,
'is_active',
)
# Use a polymorphic list
list_display = ('pretty', 'balance', 'is_active')
@ -44,11 +47,12 @@ class NoteClubAdmin(PolymorphicChildModelAdmin):
"""
Child for a club note, see NoteAdmin
"""
inlines = (AliasInlines,)
inlines = (AliasInlines, )
# We can't change club after creation or the balance
readonly_fields = ('club', 'balance')
search_fields = ('club',)
search_fields = ('club', )
def has_add_permission(self, request):
"""
A club note should not be manually added
@ -67,7 +71,7 @@ class NoteSpecialAdmin(PolymorphicChildModelAdmin):
"""
Child for a special note, see NoteAdmin
"""
readonly_fields = ('balance',)
readonly_fields = ('balance', )
@admin.register(NoteUser)
@ -75,7 +79,7 @@ class NoteUserAdmin(PolymorphicChildModelAdmin):
"""
Child for an user note, see NoteAdmin
"""
inlines = (AliasInlines,)
inlines = (AliasInlines, )
# We can't change user after creation or the balance
readonly_fields = ('user', 'balance')
@ -101,7 +105,10 @@ class TransactionAdmin(admin.ModelAdmin):
list_display = ('created_at', 'poly_source', 'poly_destination',
'quantity', 'amount', 'transaction_type', 'valid')
list_filter = ('transaction_type', 'valid')
autocomplete_fields = ('source', 'destination',)
autocomplete_fields = (
'source',
'destination',
)
def poly_source(self, obj):
"""
@ -136,8 +143,8 @@ class TransactionTemplateAdmin(admin.ModelAdmin):
Admin customisation for TransactionTemplate
"""
list_display = ('name', 'poly_destination', 'amount', 'template_type')
list_filter = ('template_type',)
autocomplete_fields = ('destination',)
list_filter = ('template_type', )
autocomplete_fields = ('destination', )
def poly_destination(self, obj):
"""
@ -153,5 +160,5 @@ class TransactionCategoryAdmin(admin.ModelAdmin):
"""
Admin customisation for TransactionTemplate
"""
list_display = ('name',)
list_filter = ('name',)
list_display = ('name', )
list_filter = ('name', )

View File

@ -17,7 +17,10 @@ class NoteSerializer(serializers.ModelSerializer):
model = Note
fields = '__all__'
extra_kwargs = {
'url': {'view_name': 'project-detail', 'lookup_field': 'pk'},
'url': {
'view_name': 'project-detail',
'lookup_field': 'pk'
},
}
@ -69,6 +72,7 @@ class NotePolymorphicSerializer(PolymorphicSerializer):
NoteSpecial: NoteSpecialSerializer
}
class TransactionTemplateSerializer(serializers.ModelSerializer):
"""
REST API Serializer for Transaction templates.

View File

@ -69,7 +69,9 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
queryset = Note.objects.all()
alias = self.request.query_params.get("alias", ".*")
queryset = queryset.filter(Q(alias__name__regex=alias) | Q(alias__normalized_name__regex=alias.lower()))
queryset = queryset.filter(
Q(alias__name__regex=alias)
| Q(alias__normalized_name__regex=alias.lower()))
note_type = self.request.query_params.get("type", None)
if note_type:
@ -79,7 +81,8 @@ class NotePolymorphicViewSet(viewsets.ModelViewSet):
elif "club" in l:
queryset = queryset.filter(polymorphic_ctype__model="noteclub")
elif "special" in l:
queryset = queryset.filter(polymorphic_ctype__model="notespecial")
queryset = queryset.filter(
polymorphic_ctype__model="notespecial")
else:
queryset = queryset.none()
@ -104,7 +107,8 @@ class AliasViewSet(viewsets.ModelViewSet):
queryset = Alias.objects.all()
alias = self.request.query_params.get("alias", ".*")
queryset = queryset.filter(Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
queryset = queryset.filter(
Q(name__regex=alias) | Q(normalized_name__regex=alias.lower()))
note_id = self.request.query_params.get("note", None)
if note_id:
@ -114,11 +118,14 @@ class AliasViewSet(viewsets.ModelViewSet):
if note_type:
l = str(note_type).lower()
if "user" in l:
queryset = queryset.filter(note__polymorphic_ctype__model="noteuser")
queryset = queryset.filter(
note__polymorphic_ctype__model="noteuser")
elif "club" in l:
queryset = queryset.filter(note__polymorphic_ctype__model="noteclub")
queryset = queryset.filter(
note__polymorphic_ctype__model="noteclub")
elif "special" in l:
queryset = queryset.filter(note__polymorphic_ctype__model="notespecial")
queryset = queryset.filter(
note__polymorphic_ctype__model="notespecial")
else:
queryset = queryset.none()

View File

@ -20,9 +20,9 @@ class NoteConfig(AppConfig):
"""
post_save.connect(
signals.save_user_note,
sender=settings.AUTH_USER_MODEL
sender=settings.AUTH_USER_MODEL,
)
post_save.connect(
signals.save_club_note,
sender='member.Club'
sender='member.Club',
)

View File

@ -4,10 +4,11 @@ from dal import autocomplete, forward
from django import forms
from .models import Transaction, TransactionTemplate
class TransactionTemplateForm(forms.ModelForm):
class Meta:
model = TransactionTemplate
fields ='__all__'
fields = '__all__'
# Le champ de destination 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
@ -15,11 +16,14 @@ class TransactionTemplateForm(forms.ModelForm):
# Pour force le type d'une note, il faut rajouter le paramètre :
# forward=(forward.Const('TYPE', 'note_type') où TYPE est dans {user, club, special}
widgets = {
'destination': autocomplete.ModelSelect2(url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
}),
'destination':
autocomplete.ModelSelect2(
url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
},
),
}
@ -31,26 +35,38 @@ class TransactionForm(forms.ModelForm):
class Meta:
model = Transaction
fields = ('source', 'destination', 'reason', 'amount',)
fields = (
'source',
'destination',
'reason',
'amount',
)
# Voir ci-dessus
widgets = {
'source': autocomplete.ModelSelect2(url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
},),
'destination': autocomplete.ModelSelect2(url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
},),
'source':
autocomplete.ModelSelect2(
url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
},
),
'destination':
autocomplete.ModelSelect2(
url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
},
),
}
class ConsoForm(forms.ModelForm):
class ConsoForm(forms.ModelForm):
def save(self, commit=True):
button: TransactionTemplate = TransactionTemplate.objects.filter(name=self.data['button']).get()
button: TransactionTemplate = TransactionTemplate.objects.filter(
name=self.data['button']).get()
self.instance.destination = button.destination
self.instance.amount = button.amount
self.instance.transaction_type = 'bouton'
@ -59,15 +75,18 @@ class ConsoForm(forms.ModelForm):
class Meta:
model = Transaction
fields = ('source',)
fields = ('source', )
# 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
# et récupère les aliases de note valides
widgets = {
'source': autocomplete.ModelSelect2(url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
}),
'source':
autocomplete.ModelSelect2(
url='note:note_autocomplete',
attrs={
'data-placeholder': 'Note ...',
'data-minimum-input-length': 1,
},
),
}

View File

@ -10,7 +10,6 @@ from django.core.validators import RegexValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from polymorphic.models import PolymorphicModel
"""
Defines each note types
"""
@ -34,8 +33,7 @@ class Note(PolymorphicModel):
default=True,
help_text=_(
'Designates whether this note should be treated as active. '
'Unselect this instead of deleting notes.'
),
'Unselect this instead of deleting notes.'),
)
display_image = models.ImageField(
verbose_name=_('display image'),
@ -85,7 +83,8 @@ class Note(PolymorphicModel):
"""
Verify alias (simulate save)
"""
aliases = Alias.objects.filter(normalized_name=Alias.normalize(str(self)))
aliases = Alias.objects.filter(
normalized_name=Alias.normalize(str(self)))
if aliases.exists():
# Alias exists, so check if it is linked to this note
if aliases.first().note != self:
@ -181,15 +180,15 @@ class Alias(models.Model):
validators=[
RegexValidator(
regex=settings.ALIAS_VALIDATOR_REGEX,
message=_('Invalid alias')
message=_('Invalid alias'),
)
] if settings.ALIAS_VALIDATOR_REGEX else []
] if settings.ALIAS_VALIDATOR_REGEX else [],
)
normalized_name = models.CharField(
max_length=255,
unique=True,
default='',
editable=False
editable=False,
)
note = models.ForeignKey(
Note,
@ -209,11 +208,9 @@ class Alias(models.Model):
Normalizes a string: removes most diacritics and does casefolding
"""
return ''.join(
char
for char in unicodedata.normalize('NFKD', string.casefold())
char for char in unicodedata.normalize('NFKD', string.casefold())
if all(not unicodedata.category(char).startswith(cat)
for cat in {'M', 'P', 'Z', 'C'})
).casefold()
for cat in {'M', 'P', 'Z', 'C'})).casefold()
def save(self, *args, **kwargs):
"""
@ -229,8 +226,9 @@ class Alias(models.Model):
raise ValidationError(_('Alias too long.'))
try:
if self != Alias.objects.get(normalized_name=normalized_name):
raise ValidationError(_('An alias with a similar name '
'already exists.'))
raise ValidationError(
_('An alias with a similar name '
'already exists.'))
except Alias.DoesNotExist:
pass

View File

@ -7,16 +7,19 @@ from .models.transactions import Transaction
class HistoryTable(tables.Table):
class Meta:
attrs = {'class':'table table-bordered table-condensed table-striped table-hover'}
attrs = {
'class':
'table table-bordered table-condensed table-striped table-hover'
}
model = Transaction
template_name = 'django_tables2/bootstrap.html'
sequence = ('...','total','valid')
sequence = ('...', 'total', 'valid')
total = tables.Column() #will use Transaction.total() !!
total = tables.Column() #will use Transaction.total() !!
def order_total(self, QuerySet, is_descending):
# needed for rendering
QuerySet = QuerySet.annotate(
total=F('amount') * F('quantity')
).order_by(('-' if is_descending else '') + 'total')
total=F('amount') *
F('quantity')).order_by(('-' if is_descending else '') + 'total')
return (QuerySet, True)

View File

@ -2,10 +2,17 @@ from django import template
def pretty_money(value):
if value%100 == 0:
return "{:s}{:d}".format("- " if value < 0 else "", abs(value) // 100)
if value % 100 == 0:
return "{:s}{:d}".format(
"- " if value < 0 else "",
abs(value) // 100,
)
else:
return "{:s}{:d}{:02d}".format("- " if value < 0 else "", abs(value) // 100, abs(value) % 100)
return "{:s}{:d}{:02d}".format(
"- " if value < 0 else "",
abs(value) // 100,
abs(value) % 100,
)
register = template.Library()

View File

@ -12,6 +12,7 @@ from django.views.generic import CreateView, ListView, DetailView, UpdateView
from .models import Note, Transaction, TransactionCategory, TransactionTemplate, Alias
from .forms import TransactionForm, TransactionTemplateForm, ConsoForm
class TransactionCreate(LoginRequiredMixin, CreateView):
"""
Show transfer page
@ -30,14 +31,13 @@ class TransactionCreate(LoginRequiredMixin, CreateView):
'to one or others')
return context
def get_form(self, form_class=None):
"""
If the user has no right to transfer funds, then it won't have the choice of the source of the transfer.
"""
form = super().get_form(form_class)
if False: # TODO: fix it with "if %user has no right to transfer funds"
if False: # TODO: fix it with "if %user has no right to transfer funds"
del form.fields['source']
return form
@ -46,7 +46,7 @@ class TransactionCreate(LoginRequiredMixin, CreateView):
"""
If the user has no right to transfer funds, then it will be the source of the transfer by default.
"""
if False: # TODO: fix it with "if %user has no right to transfer funds"
if False: # TODO: fix it with "if %user has no right to transfer funds"
form.instance.source = self.request.user.note
return super().form_valid(form)
@ -56,7 +56,6 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
"""
Auto complete note by aliases
"""
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.
@ -101,27 +100,30 @@ class NoteAutocomplete(autocomplete.Select2QuerySetView):
return str(result.note.pk)
class TransactionTemplateCreateView(LoginRequiredMixin,CreateView):
class TransactionTemplateCreateView(LoginRequiredMixin, CreateView):
"""
Create TransactionTemplate
"""
model = TransactionTemplate
form_class = TransactionTemplateForm
class TransactionTemplateListView(LoginRequiredMixin,ListView):
class TransactionTemplateListView(LoginRequiredMixin, ListView):
"""
List TransactionsTemplates
"""
model = TransactionTemplate
form_class = TransactionTemplateForm
class TransactionTemplateUpdateView(LoginRequiredMixin,UpdateView):
class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView):
"""
"""
model = TransactionTemplate
form_class = TransactionTemplateForm
class ConsoView(LoginRequiredMixin,CreateView):
class ConsoView(LoginRequiredMixin, CreateView):
"""
Consume
"""
@ -139,11 +141,14 @@ class ConsoView(LoginRequiredMixin,CreateView):
if 'template_type' not in self.kwargs.keys():
return context
template_type = TransactionCategory.objects.filter(name=self.kwargs.get('template_type')).get()
context['buttons'] = TransactionTemplate.objects.filter(template_type=template_type)
template_type = TransactionCategory.objects.filter(
name=self.kwargs.get('template_type')).get()
context['buttons'] = TransactionTemplate.objects.filter(
template_type=template_type)
context['title'] = template_type
return context
def get_success_url(self):
return reverse('note:consos',args=(self.kwargs.get('template_type'),))
return reverse('note:consos',
args=(self.kwargs.get('template_type'), ))