diff --git a/bda/__init__.py b/bda/__init__.py index 8b137891..e69de29b 100644 --- a/bda/__init__.py +++ b/bda/__init__.py @@ -1 +0,0 @@ - diff --git a/bda/admin.py b/bda/admin.py index 485471da..b32144f1 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -1,16 +1,24 @@ from datetime import timedelta -from custommail.shortcuts import send_mass_custom_mail +from custommail.shortcuts import send_mass_custom_mail +from dal.autocomplete import ModelSelect2 +from django import forms from django.contrib import admin -from django.db.models import Sum, Count +from django.db.models import Count, Sum from django.template.defaultfilters import pluralize from django.utils import timezone -from django import forms -from dal.autocomplete import ModelSelect2 - -from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\ - Attribution, Tirage, Quote, CategorieSpectacle, SpectacleRevente +from bda.models import ( + Attribution, + CategorieSpectacle, + ChoixSpectacle, + Participant, + Quote, + Salle, + Spectacle, + SpectacleRevente, + Tirage, +) class ReadOnlyMixin(object): @@ -27,8 +35,8 @@ class ReadOnlyMixin(object): class ChoixSpectacleAdminForm(forms.ModelForm): class Meta: widgets = { - 'participant': ModelSelect2(url='bda-participant-autocomplete'), - 'spectacle': ModelSelect2(url='bda-spectacle-autocomplete'), + "participant": ModelSelect2(url="bda-participant-autocomplete"), + "spectacle": ModelSelect2(url="bda-spectacle-autocomplete"), } @@ -43,10 +51,10 @@ class AttributionTabularAdminForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - spectacles = Spectacle.objects.select_related('location') + spectacles = Spectacle.objects.select_related("location") if self.listing is not None: spectacles = spectacles.filter(listing=self.listing) - self.fields['spectacle'].queryset = spectacles + self.fields["spectacle"].queryset = spectacles class WithoutListingAttributionTabularAdminForm(AttributionTabularAdminForm): @@ -70,7 +78,7 @@ class AttributionInline(admin.TabularInline): class WithListingAttributionInline(AttributionInline): - exclude = ('given', ) + exclude = ("given",) form = WithListingAttributionTabularAdminForm listing = True @@ -81,12 +89,10 @@ class WithoutListingAttributionInline(AttributionInline): class ParticipantAdminForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['choicesrevente'].queryset = ( - Spectacle.objects - .select_related('location') + self.fields["choicesrevente"].queryset = Spectacle.objects.select_related( + "location" ) @@ -94,11 +100,13 @@ class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin): inlines = [WithListingAttributionInline, WithoutListingAttributionInline] def get_queryset(self, request): - return Participant.objects.annotate(nb_places=Count('attributions'), - total=Sum('attributions__price')) + return Participant.objects.annotate( + nb_places=Count("attributions"), total=Sum("attributions__price") + ) def nb_places(self, obj): return obj.nb_places + nb_places.admin_order_field = "nb_places" nb_places.short_description = "Nombre de places" @@ -108,33 +116,32 @@ class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin): return "%.02f €" % tot else: return "0 €" + total.admin_order_field = "total" total.short_description = "Total à payer" - list_display = ("user", "nb_places", "total", "paid", "paymenttype", - "tirage") + list_display = ("user", "nb_places", "total", "paid", "paymenttype", "tirage") list_filter = ("paid", "tirage") - search_fields = ('user__username', 'user__first_name', 'user__last_name') - actions = ['send_attribs', ] + search_fields = ("user__username", "user__first_name", "user__last_name") + actions = ["send_attribs"] actions_on_bottom = True list_per_page = 400 readonly_fields = ("total",) - readonly_fields_update = ('user', 'tirage') + readonly_fields_update = ("user", "tirage") form = ParticipantAdminForm def send_attribs(self, request, queryset): datatuple = [] for member in queryset.all(): attribs = member.attributions.all() - context = {'member': member.user} + context = {"member": member.user} shortname = "" if len(attribs) == 0: shortname = "bda-attributions-decus" else: shortname = "bda-attributions" - context['places'] = attribs + context["places"] = attribs print(context) - datatuple.append((shortname, context, "bda@ens.fr", - [member.user.email])) + datatuple.append((shortname, context, "bda@ens.fr", [member.user.email])) send_mass_custom_mail(datatuple) count = len(queryset.all()) if count == 1: @@ -143,24 +150,23 @@ class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin): else: message_bit = "%d membres ont" % count plural = "s" - self.message_user(request, "%s été informé%s avec succès." - % (message_bit, plural)) + self.message_user( + request, "%s été informé%s avec succès." % (message_bit, plural) + ) + send_attribs.short_description = "Envoyer les résultats par mail" class AttributionAdminForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if 'spectacle' in self.fields: - self.fields['spectacle'].queryset = ( - Spectacle.objects - .select_related('location') + if "spectacle" in self.fields: + self.fields["spectacle"].queryset = Spectacle.objects.select_related( + "location" ) - if 'participant' in self.fields: - self.fields['participant'].queryset = ( - Participant.objects - .select_related('user', 'tirage') + if "participant" in self.fields: + self.fields["participant"].queryset = Participant.objects.select_related( + "user", "tirage" ) def clean(self): @@ -171,21 +177,26 @@ class AttributionAdminForm(forms.ModelForm): if participant.tirage != spectacle.tirage: raise forms.ValidationError( "Erreur : le participant et le spectacle n'appartiennent" - "pas au même tirage") + "pas au même tirage" + ) return cleaned_data class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin): def paid(self, obj): return obj.participant.paid - paid.short_description = 'A payé' + + paid.short_description = "A payé" paid.boolean = True list_display = ("id", "spectacle", "participant", "given", "paid") - search_fields = ('spectacle__title', 'participant__user__username', - 'participant__user__first_name', - 'participant__user__last_name') + search_fields = ( + "spectacle__title", + "participant__user__username", + "participant__user__first_name", + "participant__user__last_name", + ) form = AttributionAdminForm - readonly_fields_update = ('spectacle', 'participant') + readonly_fields_update = ("spectacle", "participant") class ChoixSpectacleAdmin(admin.ModelAdmin): @@ -193,13 +204,15 @@ class ChoixSpectacleAdmin(admin.ModelAdmin): def tirage(self, obj): return obj.participant.tirage - list_display = ("participant", "tirage", "spectacle", "priority", - "double_choice") + + list_display = ("participant", "tirage", "spectacle", "priority", "double_choice") list_filter = ("double_choice", "participant__tirage") - search_fields = ('participant__user__username', - 'participant__user__first_name', - 'participant__user__last_name', - 'spectacle__title') + search_fields = ( + "participant__user__username", + "participant__user__first_name", + "participant__user__last_name", + "spectacle__title", + ) class QuoteInline(admin.TabularInline): @@ -209,42 +222,36 @@ class QuoteInline(admin.TabularInline): class SpectacleAdmin(admin.ModelAdmin): inlines = [QuoteInline] model = Spectacle - list_display = ("title", "date", "tirage", "location", "slots", "price", - "listing") - list_filter = ("location", "tirage",) + list_display = ("title", "date", "tirage", "location", "slots", "price", "listing") + list_filter = ("location", "tirage") search_fields = ("title", "location__name") - readonly_fields = ("rappel_sent", ) + readonly_fields = ("rappel_sent",) class TirageAdmin(admin.ModelAdmin): model = Tirage - list_display = ("title", "ouverture", "fermeture", "active", - "enable_do_tirage") - readonly_fields = ("tokens", ) - list_filter = ("active", ) - search_fields = ("title", ) + list_display = ("title", "ouverture", "fermeture", "active", "enable_do_tirage") + readonly_fields = ("tokens",) + list_filter = ("active",) + search_fields = ("title",) class SalleAdmin(admin.ModelAdmin): model = Salle - search_fields = ('name', 'address') + search_fields = ("name", "address") class SpectacleReventeAdminForm(forms.ModelForm): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['confirmed_entry'].queryset = ( - Participant.objects - .select_related('user', 'tirage') + self.fields["confirmed_entry"].queryset = Participant.objects.select_related( + "user", "tirage" ) - self.fields['seller'].queryset = ( - Participant.objects - .select_related('user', 'tirage') + self.fields["seller"].queryset = Participant.objects.select_related( + "user", "tirage" ) - self.fields['soldTo'].queryset = ( - Participant.objects - .select_related('user', 'tirage') + self.fields["soldTo"].queryset = Participant.objects.select_related( + "user", "tirage" ) @@ -252,6 +259,7 @@ class SpectacleReventeAdmin(admin.ModelAdmin): """ Administration des reventes de spectacles """ + model = SpectacleRevente def spectacle(self, obj): @@ -263,12 +271,14 @@ class SpectacleReventeAdmin(admin.ModelAdmin): list_display = ("spectacle", "seller", "date", "soldTo") raw_id_fields = ("attribution",) readonly_fields = ("date_tirage",) - search_fields = ['attribution__spectacle__title', - 'seller__user__username', - 'seller__user__first_name', - 'seller__user__last_name'] + search_fields = [ + "attribution__spectacle__title", + "seller__user__username", + "seller__user__first_name", + "seller__user__last_name", + ] - actions = ['transfer', 'reinit'] + actions = ["transfer", "reinit"] actions_on_bottom = True form = SpectacleReventeAdminForm @@ -284,10 +294,10 @@ class SpectacleReventeAdmin(admin.ModelAdmin): attrib.save() self.message_user( request, - "%d attribution%s %s été transférée%s avec succès." % ( - count, pluralize(count), - pluralize(count, "a,ont"), pluralize(count)) - ) + "%d attribution%s %s été transférée%s avec succès." + % (count, pluralize(count), pluralize(count, "a,ont"), pluralize(count)), + ) + transfer.short_description = "Transférer les reventes sélectionnées" def reinit(self, request, queryset): @@ -296,14 +306,15 @@ class SpectacleReventeAdmin(admin.ModelAdmin): """ count = queryset.count() for revente in queryset.filter( - attribution__spectacle__date__gte=timezone.now()): + attribution__spectacle__date__gte=timezone.now() + ): revente.reset(new_date=timezone.now() - timedelta(hours=1)) self.message_user( request, - "%d attribution%s %s été réinitialisée%s avec succès." % ( - count, pluralize(count), - pluralize(count, "a,ont"), pluralize(count)) - ) + "%d attribution%s %s été réinitialisée%s avec succès." + % (count, pluralize(count), pluralize(count, "a,ont"), pluralize(count)), + ) + reinit.short_description = "Réinitialiser les reventes sélectionnées" diff --git a/bda/algorithm.py b/bda/algorithm.py index f0f48ad9..830ef119 100644 --- a/bda/algorithm.py +++ b/bda/algorithm.py @@ -1,7 +1,7 @@ -from django.db.models import Max - import random +from django.db.models import Max + class Algorithm(object): @@ -16,7 +16,7 @@ class Algorithm(object): show.requests - on crée des tables de demandes pour chaque personne, afin de pouvoir modifier les rankings""" - self.max_group = 2*max(choice.priority for choice in choices) + self.max_group = 2 * max(choice.priority for choice in choices) self.shows = [] showdict = {} for show in shows: @@ -54,16 +54,19 @@ class Algorithm(object): self.ranks[member][show] -= increment def appendResult(self, l, member, show): - l.append((member, - self.ranks[member][show], - self.origranks[member][show], - self.choices[member][show].double)) + l.append( + ( + member, + self.ranks[member][show], + self.origranks[member][show], + self.choices[member][show].double, + ) + ) def __call__(self, seed): random.seed(seed) results = [] - shows = sorted(self.shows, key=lambda x: x.nrequests / x.slots, - reverse=True) + shows = sorted(self.shows, key=lambda x: x.nrequests / x.slots, reverse=True) for show in shows: # On regroupe tous les gens ayant le même rang groups = dict([(i, []) for i in range(1, self.max_group + 1)]) @@ -82,8 +85,10 @@ class Algorithm(object): if len(winners) + 1 < show.slots: self.appendResult(winners, member, show) self.appendResult(winners, member, show) - elif not self.choices[member][show].autoquit \ - and len(winners) < show.slots: + elif ( + not self.choices[member][show].autoquit + and len(winners) < show.slots + ): self.appendResult(winners, member, show) self.appendResult(losers, member, show) else: diff --git a/bda/forms.py b/bda/forms.py index 7e81587a..4560d3e5 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -6,7 +6,6 @@ from bda.models import Attribution, Spectacle, SpectacleRevente class InscriptionInlineFormSet(BaseInlineFormSet): - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -16,9 +15,9 @@ class InscriptionInlineFormSet(BaseInlineFormSet): # set once for all "spectacle" field choices # - restrict choices to the spectacles of this tirage # - force_choices avoid many db requests - spectacles = tirage.spectacle_set.select_related('location') + spectacles = tirage.spectacle_set.select_related("location") choices = [(sp.pk, str(sp)) for sp in spectacles] - self.force_choices('spectacle', choices) + self.force_choices("spectacle", choices) def force_choices(self, name, choices): """Set choices of a field. @@ -30,7 +29,7 @@ class InscriptionInlineFormSet(BaseInlineFormSet): for form in self.forms: field = form.fields[name] if field.empty_label is not None: - field.choices = [('', field.empty_label)] + choices + field.choices = [("", field.empty_label)] + choices else: field.choices = choices @@ -56,125 +55,117 @@ class ReventeModelMultipleChoiceField(forms.ModelMultipleChoiceField): # C'est notre propre revente : pas besoin de spécifier le vendeur if obj.soldTo is not None: suffix = " -- Vendue à {firstname} {lastname}".format( - firstname=obj.soldTo.user.first_name, - lastname=obj.soldTo.user.last_name, - ) + firstname=obj.soldTo.user.first_name, + lastname=obj.soldTo.user.last_name, + ) else: # Ce n'est pas à nous : on ne voit jamais l'acheteur suffix = " -- Vendue par {firstname} {lastname}".format( - firstname=obj.seller.user.first_name, - lastname=obj.seller.user.last_name, - ) + firstname=obj.seller.user.first_name, lastname=obj.seller.user.last_name + ) - return label.format(show=str(obj.attribution.spectacle), - suffix=suffix) + return label.format(show=str(obj.attribution.spectacle), suffix=suffix) class ResellForm(forms.Form): attributions = AttributionModelMultipleChoiceField( - label='', - queryset=Attribution.objects.none(), - widget=forms.CheckboxSelectMultiple, - required=False) + label="", + queryset=Attribution.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) def __init__(self, participant, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['attributions'].queryset = ( - participant.attribution_set - .filter(spectacle__date__gte=timezone.now()) + self.fields["attributions"].queryset = ( + participant.attribution_set.filter(spectacle__date__gte=timezone.now()) .exclude(revente__seller=participant) - .select_related('spectacle', 'spectacle__location', - 'participant__user') + .select_related("spectacle", "spectacle__location", "participant__user") ) class AnnulForm(forms.Form): reventes = ReventeModelMultipleChoiceField( - own=True, - label='', - queryset=Attribution.objects.none(), - widget=forms.CheckboxSelectMultiple, - required=False) + own=True, + label="", + queryset=Attribution.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) def __init__(self, participant, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['reventes'].queryset = ( - participant.original_shows - .filter(attribution__spectacle__date__gte=timezone.now(), - notif_sent=False, - soldTo__isnull=True) - .select_related('attribution__spectacle', - 'attribution__spectacle__location') - ) + self.fields["reventes"].queryset = participant.original_shows.filter( + attribution__spectacle__date__gte=timezone.now(), + notif_sent=False, + soldTo__isnull=True, + ).select_related("attribution__spectacle", "attribution__spectacle__location") class InscriptionReventeForm(forms.Form): spectacles = forms.ModelMultipleChoiceField( - queryset=Spectacle.objects.none(), - widget=forms.CheckboxSelectMultiple, - required=False) + queryset=Spectacle.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) def __init__(self, tirage, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['spectacles'].queryset = ( - tirage.spectacle_set - .select_related('location') - .filter(date__gte=timezone.now()) - ) + self.fields["spectacles"].queryset = tirage.spectacle_set.select_related( + "location" + ).filter(date__gte=timezone.now()) class ReventeTirageAnnulForm(forms.Form): reventes = ReventeModelMultipleChoiceField( - own=False, - label='', - queryset=SpectacleRevente.objects.none(), - widget=forms.CheckboxSelectMultiple, - required=False - ) + own=False, + label="", + queryset=SpectacleRevente.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) def __init__(self, participant, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['reventes'].queryset = ( - participant.entered.filter(soldTo__isnull=True) - .select_related('attribution__spectacle', - 'seller__user') - ) + self.fields["reventes"].queryset = participant.entered.filter( + soldTo__isnull=True + ).select_related("attribution__spectacle", "seller__user") class ReventeTirageForm(forms.Form): reventes = ReventeModelMultipleChoiceField( - own=False, - label='', - queryset=SpectacleRevente.objects.none(), - widget=forms.CheckboxSelectMultiple, - required=False - ) + own=False, + label="", + queryset=SpectacleRevente.objects.none(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) def __init__(self, participant, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['reventes'].queryset = ( + self.fields["reventes"].queryset = ( SpectacleRevente.objects.filter( - notif_sent=True, - shotgun=False, - tirage_done=False - ).exclude(confirmed_entry=participant) - .select_related('attribution__spectacle') + notif_sent=True, shotgun=False, tirage_done=False + ) + .exclude(confirmed_entry=participant) + .select_related("attribution__spectacle") ) class SoldForm(forms.Form): reventes = ReventeModelMultipleChoiceField( - own=True, - label='', - queryset=Attribution.objects.none(), - widget=forms.CheckboxSelectMultiple) + own=True, + label="", + queryset=Attribution.objects.none(), + widget=forms.CheckboxSelectMultiple, + ) def __init__(self, participant, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['reventes'].queryset = ( - participant.original_shows - .filter(soldTo__isnull=False) + self.fields["reventes"].queryset = ( + participant.original_shows.filter(soldTo__isnull=False) .exclude(soldTo=participant) - .select_related('attribution__spectacle', - 'attribution__spectacle__location') + .select_related( + "attribution__spectacle", "attribution__spectacle__location" + ) ) diff --git a/bda/management/commands/loadbdadevdata.py b/bda/management/commands/loadbdadevdata.py index a8e3f298..a608db6a 100644 --- a/bda/management/commands/loadbdadevdata.py +++ b/bda/management/commands/loadbdadevdata.py @@ -5,17 +5,15 @@ Crée deux tirages de test et y inscrit les utilisateurs import os import random -from django.utils import timezone from django.contrib.auth.models import User +from django.utils import timezone -from gestioncof.management.base import MyBaseCommand -from bda.models import Tirage, Spectacle, Salle, Participant, ChoixSpectacle +from bda.models import ChoixSpectacle, Participant, Salle, Spectacle, Tirage from bda.views import do_tirage - +from gestioncof.management.base import MyBaseCommand # Où sont stockés les fichiers json -DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), - 'data') +DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data") class Command(MyBaseCommand): @@ -27,27 +25,29 @@ class Command(MyBaseCommand): # --- Tirage.objects.all().delete() - Tirage.objects.bulk_create([ - Tirage( - title="Tirage de test 1", - ouverture=timezone.now()-timezone.timedelta(days=7), - fermeture=timezone.now(), - active=True - ), - Tirage( - title="Tirage de test 2", - ouverture=timezone.now(), - fermeture=timezone.now()+timezone.timedelta(days=60), - active=True - ) - ]) + Tirage.objects.bulk_create( + [ + Tirage( + title="Tirage de test 1", + ouverture=timezone.now() - timezone.timedelta(days=7), + fermeture=timezone.now(), + active=True, + ), + Tirage( + title="Tirage de test 2", + ouverture=timezone.now(), + fermeture=timezone.now() + timezone.timedelta(days=60), + active=True, + ), + ] + ) tirages = Tirage.objects.all() # --- # Salles # --- - locations = self.from_json('locations.json', DATA_DIR, Salle) + locations = self.from_json("locations.json", DATA_DIR, Salle) # --- # Spectacles @@ -60,15 +60,13 @@ class Command(MyBaseCommand): """ show.tirage = random.choice(tirages) show.listing = bool(random.randint(0, 1)) - show.date = ( - show.tirage.fermeture - + timezone.timedelta(days=random.randint(60, 90)) + show.date = show.tirage.fermeture + timezone.timedelta( + days=random.randint(60, 90) ) show.location = random.choice(locations) return show - shows = self.from_json( - 'shows.json', DATA_DIR, Spectacle, show_callback - ) + + shows = self.from_json("shows.json", DATA_DIR, Spectacle, show_callback) # --- # Inscriptions @@ -79,23 +77,19 @@ class Command(MyBaseCommand): choices = [] for user in User.objects.filter(profile__is_cof=True): for tirage in tirages: - part, _ = Participant.objects.get_or_create( - user=user, - tirage=tirage - ) + part, _ = Participant.objects.get_or_create(user=user, tirage=tirage) shows = random.sample( - list(tirage.spectacle_set.all()), - tirage.spectacle_set.count() // 2 + list(tirage.spectacle_set.all()), tirage.spectacle_set.count() // 2 ) for (rank, show) in enumerate(shows): - choices.append(ChoixSpectacle( - participant=part, - spectacle=show, - priority=rank + 1, - double_choice=random.choice( - ['1', 'double', 'autoquit'] + choices.append( + ChoixSpectacle( + participant=part, + spectacle=show, + priority=rank + 1, + double_choice=random.choice(["1", "double", "autoquit"]), ) - )) + ) ChoixSpectacle.objects.bulk_create(choices) self.stdout.write("- {:d} inscriptions générées".format(len(choices))) diff --git a/bda/management/commands/manage_reventes.py b/bda/management/commands/manage_reventes.py index 5a06d40b..52d25252 100644 --- a/bda/management/commands/manage_reventes.py +++ b/bda/management/commands/manage_reventes.py @@ -4,12 +4,12 @@ Gestion en ligne de commande des reventes. from django.core.management import BaseCommand from django.utils import timezone + from bda.models import SpectacleRevente class Command(BaseCommand): - help = "Envoie les mails de notification et effectue " \ - "les tirages au sort des reventes" + help = "Envoie les mails de notification et effectue " "les tirages au sort des reventes" leave_locale_alone = True def handle(self, *args, **options): @@ -28,22 +28,18 @@ class Command(BaseCommand): ) # Le spectacle est dans plus longtemps : on prévient - elif (revente.can_notif and not revente.notif_sent): + elif revente.can_notif and not revente.notif_sent: self.stdout.write(str(now)) revente.send_notif() self.stdout.write( - "Mails d'inscription à la revente [%s] envoyés" - % revente + "Mails d'inscription à la revente [%s] envoyés" % revente ) # On fait le tirage - elif (now >= revente.date_tirage and not revente.tirage_done): + elif now >= revente.date_tirage and not revente.tirage_done: self.stdout.write(str(now)) winner = revente.tirage() - self.stdout.write( - "Tirage effectué pour la revente [%s]" - % revente - ) + self.stdout.write("Tirage effectué pour la revente [%s]" % revente) if winner: self.stdout.write("Gagnant : %s" % winner.user) diff --git a/bda/management/commands/sendrappels.py b/bda/management/commands/sendrappels.py index 82889f80..33f85330 100644 --- a/bda/management/commands/sendrappels.py +++ b/bda/management/commands/sendrappels.py @@ -3,27 +3,28 @@ Gestion en ligne de commande des mails de rappel. """ from datetime import timedelta + from django.core.management.base import BaseCommand from django.utils import timezone + from bda.models import Spectacle class Command(BaseCommand): - help = 'Envoie les mails de rappel des spectacles dont la date ' \ - 'approche.\nNe renvoie pas les mails déjà envoyés.' + help = "Envoie les mails de rappel des spectacles dont la date " "approche.\nNe renvoie pas les mails déjà envoyés." leave_locale_alone = True def handle(self, *args, **options): now = timezone.now() delay = timedelta(days=4) - shows = Spectacle.objects \ - .filter(date__range=(now, now+delay)) \ - .filter(tirage__active=True) \ - .filter(rappel_sent__isnull=True) \ + shows = ( + Spectacle.objects.filter(date__range=(now, now + delay)) + .filter(tirage__active=True) + .filter(rappel_sent__isnull=True) .all() + ) for show in shows: show.send_rappel() - self.stdout.write( - 'Mails de rappels pour %s envoyés avec succès.' % show) + self.stdout.write("Mails de rappels pour %s envoyés avec succès." % show) if not shows: - self.stdout.write('Aucun mail à envoyer.') + self.stdout.write("Aucun mail à envoyer.") diff --git a/bda/migrations/0001_initial.py b/bda/migrations/0001_initial.py index c4494413..077ddd4e 100644 --- a/bda/migrations/0001_initial.py +++ b/bda/migrations/0001_initial.py @@ -1,108 +1,206 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] operations = [ migrations.CreateModel( - name='Attribution', + name="Attribution", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('given', models.BooleanField(default=False, verbose_name='Donn\xe9e')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("given", models.BooleanField(default=False, verbose_name="Donn\xe9e")), ], ), migrations.CreateModel( - name='ChoixSpectacle', + name="ChoixSpectacle", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('priority', models.PositiveIntegerField(verbose_name=b'Priorit\xc3\xa9')), - ('double_choice', models.CharField(default=b'1', max_length=10, verbose_name=b'Nombre de places', choices=[(b'1', b'1 place'), (b'autoquit', b'2 places si possible, 1 sinon'), (b'double', b'2 places sinon rien')])), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "priority", + models.PositiveIntegerField(verbose_name=b"Priorit\xc3\xa9"), + ), + ( + "double_choice", + models.CharField( + default=b"1", + max_length=10, + verbose_name=b"Nombre de places", + choices=[ + (b"1", b"1 place"), + (b"autoquit", b"2 places si possible, 1 sinon"), + (b"double", b"2 places sinon rien"), + ], + ), + ), ], options={ - 'ordering': ('priority',), - 'verbose_name': 'voeu', - 'verbose_name_plural': 'voeux', + "ordering": ("priority",), + "verbose_name": "voeu", + "verbose_name_plural": "voeux", }, ), migrations.CreateModel( - name='Participant', + name="Participant", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('paid', models.BooleanField(default=False, verbose_name='A pay\xe9')), - ('paymenttype', models.CharField(blank=True, max_length=6, verbose_name='Moyen de paiement', choices=[(b'cash', 'Cash'), (b'cb', b'CB'), (b'cheque', 'Ch\xe8que'), (b'autre', 'Autre')])), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("paid", models.BooleanField(default=False, verbose_name="A pay\xe9")), + ( + "paymenttype", + models.CharField( + blank=True, + max_length=6, + verbose_name="Moyen de paiement", + choices=[ + (b"cash", "Cash"), + (b"cb", b"CB"), + (b"cheque", "Ch\xe8que"), + (b"autre", "Autre"), + ], + ), + ), ], ), migrations.CreateModel( - name='Salle', + name="Salle", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=300, verbose_name=b'Nom')), - ('address', models.TextField(verbose_name=b'Adresse')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=300, verbose_name=b"Nom")), + ("address", models.TextField(verbose_name=b"Adresse")), ], ), migrations.CreateModel( - name='Spectacle', + name="Spectacle", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=300, verbose_name=b'Titre')), - ('date', models.DateTimeField(verbose_name=b'Date & heure')), - ('description', models.TextField(verbose_name=b'Description', blank=True)), - ('slots_description', models.TextField(verbose_name=b'Description des places', blank=True)), - ('price', models.FloatField(verbose_name=b"Prix d'une place", blank=True)), - ('slots', models.IntegerField(verbose_name=b'Places')), - ('priority', models.IntegerField(default=1000, verbose_name=b'Priorit\xc3\xa9')), - ('location', models.ForeignKey(to='bda.Salle', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("title", models.CharField(max_length=300, verbose_name=b"Titre")), + ("date", models.DateTimeField(verbose_name=b"Date & heure")), + ( + "description", + models.TextField(verbose_name=b"Description", blank=True), + ), + ( + "slots_description", + models.TextField( + verbose_name=b"Description des places", blank=True + ), + ), + ( + "price", + models.FloatField(verbose_name=b"Prix d'une place", blank=True), + ), + ("slots", models.IntegerField(verbose_name=b"Places")), + ( + "priority", + models.IntegerField(default=1000, verbose_name=b"Priorit\xc3\xa9"), + ), + ( + "location", + models.ForeignKey(to="bda.Salle", on_delete=models.CASCADE), + ), ], options={ - 'ordering': ('priority', 'date', 'title'), - 'verbose_name': 'Spectacle', + "ordering": ("priority", "date", "title"), + "verbose_name": "Spectacle", }, ), migrations.AddField( - model_name='participant', - name='attributions', - field=models.ManyToManyField(related_name='attributed_to', through='bda.Attribution', to='bda.Spectacle'), + model_name="participant", + name="attributions", + field=models.ManyToManyField( + related_name="attributed_to", + through="bda.Attribution", + to="bda.Spectacle", + ), ), migrations.AddField( - model_name='participant', - name='choices', - field=models.ManyToManyField(related_name='chosen_by', through='bda.ChoixSpectacle', to='bda.Spectacle'), + model_name="participant", + name="choices", + field=models.ManyToManyField( + related_name="chosen_by", + through="bda.ChoixSpectacle", + to="bda.Spectacle", + ), ), migrations.AddField( - model_name='participant', - name='user', - field=models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + model_name="participant", + name="user", + field=models.OneToOneField( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), ), migrations.AddField( - model_name='choixspectacle', - name='participant', - field=models.ForeignKey(to='bda.Participant', on_delete=models.CASCADE), + model_name="choixspectacle", + name="participant", + field=models.ForeignKey(to="bda.Participant", on_delete=models.CASCADE), ), migrations.AddField( - model_name='choixspectacle', - name='spectacle', - field=models.ForeignKey(related_name='participants', to='bda.Spectacle', on_delete=models.CASCADE), + model_name="choixspectacle", + name="spectacle", + field=models.ForeignKey( + related_name="participants", + to="bda.Spectacle", + on_delete=models.CASCADE, + ), ), migrations.AddField( - model_name='attribution', - name='participant', - field=models.ForeignKey(to='bda.Participant', on_delete=models.CASCADE), + model_name="attribution", + name="participant", + field=models.ForeignKey(to="bda.Participant", on_delete=models.CASCADE), ), migrations.AddField( - model_name='attribution', - name='spectacle', - field=models.ForeignKey(related_name='attribues', to='bda.Spectacle', on_delete=models.CASCADE), + model_name="attribution", + name="spectacle", + field=models.ForeignKey( + related_name="attribues", to="bda.Spectacle", on_delete=models.CASCADE + ), ), migrations.AlterUniqueTogether( - name='choixspectacle', - unique_together=set([('participant', 'spectacle')]), + name="choixspectacle", unique_together=set([("participant", "spectacle")]) ), ] diff --git a/bda/migrations/0002_add_tirage.py b/bda/migrations/0002_add_tirage.py index 79f79a57..f4b01ed2 100644 --- a/bda/migrations/0002_add_tirage.py +++ b/bda/migrations/0002_add_tirage.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models from django.conf import settings +from django.db import migrations, models from django.utils import timezone @@ -36,49 +36,77 @@ def fill_tirage_fields(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('bda', '0001_initial'), - ] + dependencies = [("bda", "0001_initial")] operations = [ migrations.CreateModel( - name='Tirage', + name="Tirage", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=300, verbose_name=b'Titre')), - ('ouverture', models.DateTimeField(verbose_name=b"Date et heure d'ouverture du tirage")), - ('fermeture', models.DateTimeField(verbose_name=b'Date et heure de fermerture du tirage')), - ('token', models.TextField(verbose_name=b'Graine du tirage', blank=True)), - ('active', models.BooleanField(default=True, verbose_name=b'Tirage actif')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("title", models.CharField(max_length=300, verbose_name=b"Titre")), + ( + "ouverture", + models.DateTimeField( + verbose_name=b"Date et heure d'ouverture du tirage" + ), + ), + ( + "fermeture", + models.DateTimeField( + verbose_name=b"Date et heure de fermerture du tirage" + ), + ), + ( + "token", + models.TextField(verbose_name=b"Graine du tirage", blank=True), + ), + ( + "active", + models.BooleanField(default=True, verbose_name=b"Tirage actif"), + ), ], ), migrations.AlterField( - model_name='participant', - name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + model_name="participant", + name="user", + field=models.ForeignKey( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), ), # Create fields `spectacle` for `Participant` and `Spectacle` models. # These fields are not nullable, but we first create them as nullable # to give a default value for existing instances of these models. migrations.AddField( - model_name='participant', - name='tirage', - field=models.ForeignKey(to='bda.Tirage', null=True, on_delete=models.CASCADE), + model_name="participant", + name="tirage", + field=models.ForeignKey( + to="bda.Tirage", null=True, on_delete=models.CASCADE + ), ), migrations.AddField( - model_name='spectacle', - name='tirage', - field=models.ForeignKey(to='bda.Tirage', null=True, on_delete=models.CASCADE), + model_name="spectacle", + name="tirage", + field=models.ForeignKey( + to="bda.Tirage", null=True, on_delete=models.CASCADE + ), ), migrations.RunPython(fill_tirage_fields, migrations.RunPython.noop), migrations.AlterField( - model_name='participant', - name='tirage', - field=models.ForeignKey(to='bda.Tirage', on_delete=models.CASCADE), + model_name="participant", + name="tirage", + field=models.ForeignKey(to="bda.Tirage", on_delete=models.CASCADE), ), migrations.AlterField( - model_name='spectacle', - name='tirage', - field=models.ForeignKey(to='bda.Tirage', on_delete=models.CASCADE), + model_name="spectacle", + name="tirage", + field=models.ForeignKey(to="bda.Tirage", on_delete=models.CASCADE), ), ] diff --git a/bda/migrations/0003_update_tirage_and_spectacle.py b/bda/migrations/0003_update_tirage_and_spectacle.py index f5ca671a..3548eb88 100644 --- a/bda/migrations/0003_update_tirage_and_spectacle.py +++ b/bda/migrations/0003_update_tirage_and_spectacle.py @@ -6,19 +6,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0002_add_tirage'), - ] + dependencies = [("bda", "0002_add_tirage")] operations = [ migrations.AlterField( - model_name='spectacle', - name='price', + model_name="spectacle", + name="price", field=models.FloatField(verbose_name=b"Prix d'une place"), ), migrations.AlterField( - model_name='tirage', - name='active', - field=models.BooleanField(default=False, verbose_name=b'Tirage actif'), + model_name="tirage", + name="active", + field=models.BooleanField(default=False, verbose_name=b"Tirage actif"), ), ] diff --git a/bda/migrations/0004_mails-rappel.py b/bda/migrations/0004_mails-rappel.py index f17b711f..d331568a 100644 --- a/bda/migrations/0004_mails-rappel.py +++ b/bda/migrations/0004_mails-rappel.py @@ -6,20 +6,22 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0003_update_tirage_and_spectacle'), - ] + dependencies = [("bda", "0003_update_tirage_and_spectacle")] operations = [ migrations.AddField( - model_name='spectacle', - name='listing', - field=models.BooleanField(default=False, verbose_name=b'Les places sont sur listing'), + model_name="spectacle", + name="listing", + field=models.BooleanField( + default=False, verbose_name=b"Les places sont sur listing" + ), preserve_default=False, ), migrations.AddField( - model_name='spectacle', - name='rappel_sent', - field=models.DateTimeField(null=True, verbose_name=b'Mail de rappel envoy\xc3\xa9', blank=True), + model_name="spectacle", + name="rappel_sent", + field=models.DateTimeField( + null=True, verbose_name=b"Mail de rappel envoy\xc3\xa9", blank=True + ), ), ] diff --git a/bda/migrations/0005_encoding.py b/bda/migrations/0005_encoding.py index b36113c2..eedfcee4 100644 --- a/bda/migrations/0005_encoding.py +++ b/bda/migrations/0005_encoding.py @@ -6,24 +6,24 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0004_mails-rappel'), - ] + dependencies = [("bda", "0004_mails-rappel")] operations = [ migrations.AlterField( - model_name='choixspectacle', - name='priority', - field=models.PositiveIntegerField(verbose_name='Priorit\xe9'), + model_name="choixspectacle", + name="priority", + field=models.PositiveIntegerField(verbose_name="Priorit\xe9"), ), migrations.AlterField( - model_name='spectacle', - name='priority', - field=models.IntegerField(default=1000, verbose_name='Priorit\xe9'), + model_name="spectacle", + name="priority", + field=models.IntegerField(default=1000, verbose_name="Priorit\xe9"), ), migrations.AlterField( - model_name='spectacle', - name='rappel_sent', - field=models.DateTimeField(null=True, verbose_name='Mail de rappel envoy\xe9', blank=True), + model_name="spectacle", + name="rappel_sent", + field=models.DateTimeField( + null=True, verbose_name="Mail de rappel envoy\xe9", blank=True + ), ), ] diff --git a/bda/migrations/0006_add_tirage_switch.py b/bda/migrations/0006_add_tirage_switch.py index fc923c9a..ccfe7505 100644 --- a/bda/migrations/0006_add_tirage_switch.py +++ b/bda/migrations/0006_add_tirage_switch.py @@ -10,26 +10,25 @@ def forwards_func(apps, schema_editor): db_alias = schema_editor.connection.alias for tirage in Tirage.objects.using(db_alias).all(): if tirage.tokens: - tirage.tokens = "Before %s\n\"\"\"%s\"\"\"\n" % ( - timezone.now().strftime("%y-%m-%d %H:%M:%S"), - tirage.tokens) + tirage.tokens = 'Before %s\n"""%s"""\n' % ( + timezone.now().strftime("%y-%m-%d %H:%M:%S"), + tirage.tokens, + ) tirage.save() class Migration(migrations.Migration): - dependencies = [ - ('bda', '0005_encoding'), - ] + dependencies = [("bda", "0005_encoding")] operations = [ - migrations.RenameField('tirage', 'token', 'tokens'), + migrations.RenameField("tirage", "token", "tokens"), migrations.AddField( - model_name='tirage', - name='enable_do_tirage', + model_name="tirage", + name="enable_do_tirage", field=models.BooleanField( - default=False, - verbose_name=b'Le tirage peut \xc3\xaatre lanc\xc3\xa9'), + default=False, verbose_name=b"Le tirage peut \xc3\xaatre lanc\xc3\xa9" + ), ), migrations.RunPython(forwards_func, migrations.RunPython.noop), ] diff --git a/bda/migrations/0007_extends_spectacle.py b/bda/migrations/0007_extends_spectacle.py index 6ea11dc0..87182ff7 100644 --- a/bda/migrations/0007_extends_spectacle.py +++ b/bda/migrations/0007_extends_spectacle.py @@ -1,91 +1,100 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0006_add_tirage_switch'), - ] + dependencies = [("bda", "0006_add_tirage_switch")] operations = [ migrations.CreateModel( - name='CategorieSpectacle', + name="CategorieSpectacle", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, verbose_name='Nom', - unique=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField(max_length=100, verbose_name="Nom", unique=True), + ), ], - options={ - 'verbose_name': 'Cat\xe9gorie', - }, + options={"verbose_name": "Cat\xe9gorie"}, ), migrations.CreateModel( - name='Quote', + name="Quote", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('text', models.TextField(verbose_name='Citation')), - ('author', models.CharField(max_length=200, - verbose_name='Auteur')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("text", models.TextField(verbose_name="Citation")), + ("author", models.CharField(max_length=200, verbose_name="Auteur")), ], ), migrations.AlterModelOptions( - name='spectacle', - options={'ordering': ('date', 'title'), - 'verbose_name': 'Spectacle'}, - ), - migrations.RemoveField( - model_name='spectacle', - name='priority', + name="spectacle", + options={"ordering": ("date", "title"), "verbose_name": "Spectacle"}, ), + migrations.RemoveField(model_name="spectacle", name="priority"), migrations.AddField( - model_name='spectacle', - name='ext_link', + model_name="spectacle", + name="ext_link", field=models.CharField( max_length=500, - verbose_name='Lien vers le site du spectacle', - blank=True), + verbose_name="Lien vers le site du spectacle", + blank=True, + ), ), migrations.AddField( - model_name='spectacle', - name='image', - field=models.ImageField(upload_to='imgs/shows/', null=True, - verbose_name='Image', blank=True), + model_name="spectacle", + name="image", + field=models.ImageField( + upload_to="imgs/shows/", null=True, verbose_name="Image", blank=True + ), ), migrations.AlterField( - model_name='tirage', - name='enable_do_tirage', + model_name="tirage", + name="enable_do_tirage", field=models.BooleanField( - default=False, - verbose_name='Le tirage peut \xeatre lanc\xe9'), + default=False, verbose_name="Le tirage peut \xeatre lanc\xe9" + ), ), migrations.AlterField( - model_name='tirage', - name='tokens', - field=models.TextField(verbose_name='Graine(s) du tirage', - blank=True), + model_name="tirage", + name="tokens", + field=models.TextField(verbose_name="Graine(s) du tirage", blank=True), ), migrations.AddField( - model_name='spectacle', - name='category', - field=models.ForeignKey(blank=True, to='bda.CategorieSpectacle', - on_delete=models.CASCADE, - null=True), + model_name="spectacle", + name="category", + field=models.ForeignKey( + blank=True, + to="bda.CategorieSpectacle", + on_delete=models.CASCADE, + null=True, + ), ), migrations.AddField( - model_name='spectacle', - name='vips', - field=models.TextField(verbose_name='Personnalit\xe9s', - blank=True), + model_name="spectacle", + name="vips", + field=models.TextField(verbose_name="Personnalit\xe9s", blank=True), ), migrations.AddField( - model_name='quote', - name='spectacle', - field=models.ForeignKey(to='bda.Spectacle', - on_delete=models.CASCADE), + model_name="quote", + name="spectacle", + field=models.ForeignKey(to="bda.Spectacle", on_delete=models.CASCADE), ), ] diff --git a/bda/migrations/0008_py3.py b/bda/migrations/0008_py3.py index fe6a8eaf..6aa69abd 100644 --- a/bda/migrations/0008_py3.py +++ b/bda/migrations/0008_py3.py @@ -1,103 +1,110 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0007_extends_spectacle'), - ] + dependencies = [("bda", "0007_extends_spectacle")] operations = [ migrations.AlterField( - model_name='choixspectacle', - name='double_choice', + model_name="choixspectacle", + name="double_choice", field=models.CharField( - verbose_name='Nombre de places', - choices=[('1', '1 place'), - ('autoquit', '2 places si possible, 1 sinon'), - ('double', '2 places sinon rien')], - max_length=10, default='1'), + verbose_name="Nombre de places", + choices=[ + ("1", "1 place"), + ("autoquit", "2 places si possible, 1 sinon"), + ("double", "2 places sinon rien"), + ], + max_length=10, + default="1", + ), ), migrations.AlterField( - model_name='participant', - name='paymenttype', + model_name="participant", + name="paymenttype", field=models.CharField( blank=True, - choices=[('cash', 'Cash'), ('cb', 'CB'), - ('cheque', 'Chèque'), ('autre', 'Autre')], - max_length=6, verbose_name='Moyen de paiement'), + choices=[ + ("cash", "Cash"), + ("cb", "CB"), + ("cheque", "Chèque"), + ("autre", "Autre"), + ], + max_length=6, + verbose_name="Moyen de paiement", + ), ), migrations.AlterField( - model_name='salle', - name='address', - field=models.TextField(verbose_name='Adresse'), + model_name="salle", + name="address", + field=models.TextField(verbose_name="Adresse"), ), migrations.AlterField( - model_name='salle', - name='name', - field=models.CharField(verbose_name='Nom', max_length=300), + model_name="salle", + name="name", + field=models.CharField(verbose_name="Nom", max_length=300), ), migrations.AlterField( - model_name='spectacle', - name='date', - field=models.DateTimeField(verbose_name='Date & heure'), + model_name="spectacle", + name="date", + field=models.DateTimeField(verbose_name="Date & heure"), ), migrations.AlterField( - model_name='spectacle', - name='description', - field=models.TextField(verbose_name='Description', blank=True), + model_name="spectacle", + name="description", + field=models.TextField(verbose_name="Description", blank=True), ), migrations.AlterField( - model_name='spectacle', - name='listing', - field=models.BooleanField( - verbose_name='Les places sont sur listing'), + model_name="spectacle", + name="listing", + field=models.BooleanField(verbose_name="Les places sont sur listing"), ), migrations.AlterField( - model_name='spectacle', - name='price', + model_name="spectacle", + name="price", field=models.FloatField(verbose_name="Prix d'une place"), ), migrations.AlterField( - model_name='spectacle', - name='slots', - field=models.IntegerField(verbose_name='Places'), + model_name="spectacle", + name="slots", + field=models.IntegerField(verbose_name="Places"), ), migrations.AlterField( - model_name='spectacle', - name='slots_description', - field=models.TextField(verbose_name='Description des places', - blank=True), + model_name="spectacle", + name="slots_description", + field=models.TextField(verbose_name="Description des places", blank=True), ), migrations.AlterField( - model_name='spectacle', - name='title', - field=models.CharField(verbose_name='Titre', max_length=300), + model_name="spectacle", + name="title", + field=models.CharField(verbose_name="Titre", max_length=300), ), migrations.AlterField( - model_name='tirage', - name='active', - field=models.BooleanField(verbose_name='Tirage actif', - default=False), + model_name="tirage", + name="active", + field=models.BooleanField(verbose_name="Tirage actif", default=False), ), migrations.AlterField( - model_name='tirage', - name='fermeture', + model_name="tirage", + name="fermeture", field=models.DateTimeField( - verbose_name='Date et heure de fermerture du tirage'), + verbose_name="Date et heure de fermerture du tirage" + ), ), migrations.AlterField( - model_name='tirage', - name='ouverture', + model_name="tirage", + name="ouverture", field=models.DateTimeField( - verbose_name="Date et heure d'ouverture du tirage"), + verbose_name="Date et heure d'ouverture du tirage" + ), ), migrations.AlterField( - model_name='tirage', - name='title', - field=models.CharField(verbose_name='Titre', max_length=300), + model_name="tirage", + name="title", + field=models.CharField(verbose_name="Titre", max_length=300), ), ] diff --git a/bda/migrations/0009_revente.py b/bda/migrations/0009_revente.py index 70d6f338..d888140f 100644 --- a/bda/migrations/0009_revente.py +++ b/bda/migrations/0009_revente.py @@ -1,69 +1,87 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import django.utils.timezone +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0008_py3'), - ] + dependencies = [("bda", "0008_py3")] operations = [ migrations.CreateModel( - name='SpectacleRevente', + name="SpectacleRevente", fields=[ - ('id', models.AutoField(serialize=False, primary_key=True, - auto_created=True, verbose_name='ID')), - ('date', models.DateTimeField( - verbose_name='Date de mise en vente', - default=django.utils.timezone.now)), - ('notif_sent', models.BooleanField( - verbose_name='Notification envoyée', default=False)), - ('tirage_done', models.BooleanField( - verbose_name='Tirage effectué', default=False)), + ( + "id", + models.AutoField( + serialize=False, + primary_key=True, + auto_created=True, + verbose_name="ID", + ), + ), + ( + "date", + models.DateTimeField( + verbose_name="Date de mise en vente", + default=django.utils.timezone.now, + ), + ), + ( + "notif_sent", + models.BooleanField( + verbose_name="Notification envoyée", default=False + ), + ), + ( + "tirage_done", + models.BooleanField(verbose_name="Tirage effectué", default=False), + ), ], - options={ - 'verbose_name': 'Revente', - }, + options={"verbose_name": "Revente"}, ), migrations.AddField( - model_name='participant', - name='choicesrevente', - field=models.ManyToManyField(to='bda.Spectacle', - related_name='subscribed', - blank=True), + model_name="participant", + name="choicesrevente", + field=models.ManyToManyField( + to="bda.Spectacle", related_name="subscribed", blank=True + ), ), migrations.AddField( - model_name='spectaclerevente', - name='answered_mail', - field=models.ManyToManyField(to='bda.Participant', - related_name='wanted', - blank=True), + model_name="spectaclerevente", + name="answered_mail", + field=models.ManyToManyField( + to="bda.Participant", related_name="wanted", blank=True + ), ), migrations.AddField( - model_name='spectaclerevente', - name='attribution', - field=models.OneToOneField(to='bda.Attribution', - on_delete=models.CASCADE, - related_name='revente'), + model_name="spectaclerevente", + name="attribution", + field=models.OneToOneField( + to="bda.Attribution", on_delete=models.CASCADE, related_name="revente" + ), ), migrations.AddField( - model_name='spectaclerevente', - name='seller', - field=models.ForeignKey(to='bda.Participant', - on_delete=models.CASCADE, - verbose_name='Vendeur', - related_name='original_shows'), + model_name="spectaclerevente", + name="seller", + field=models.ForeignKey( + to="bda.Participant", + on_delete=models.CASCADE, + verbose_name="Vendeur", + related_name="original_shows", + ), ), migrations.AddField( - model_name='spectaclerevente', - name='soldTo', - field=models.ForeignKey(to='bda.Participant', - on_delete=models.CASCADE, - verbose_name='Vendue à', null=True, - blank=True), + model_name="spectaclerevente", + name="soldTo", + field=models.ForeignKey( + to="bda.Participant", + on_delete=models.CASCADE, + verbose_name="Vendue à", + null=True, + blank=True, + ), ), ] diff --git a/bda/migrations/0010_spectaclerevente_shotgun.py b/bda/migrations/0010_spectaclerevente_shotgun.py index 35b4da8a..da5c014c 100644 --- a/bda/migrations/0010_spectaclerevente_shotgun.py +++ b/bda/migrations/0010_spectaclerevente_shotgun.py @@ -1,33 +1,35 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations -from django.utils import timezone from datetime import timedelta +from django.db import migrations, models +from django.utils import timezone + def forwards_func(apps, schema_editor): SpectacleRevente = apps.get_model("bda", "SpectacleRevente") for revente in SpectacleRevente.objects.all(): is_expired = timezone.now() > revente.date_tirage() - is_direct = (revente.attribution.spectacle.date >= revente.date and - timezone.now() > revente.date + timedelta(minutes=15)) + is_direct = revente.attribution.spectacle.date >= revente.date and timezone.now() > revente.date + timedelta( + minutes=15 + ) revente.shotgun = is_expired or is_direct revente.save() class Migration(migrations.Migration): - dependencies = [ - ('bda', '0009_revente'), - ] + dependencies = [("bda", "0009_revente")] operations = [ migrations.AddField( - model_name='spectaclerevente', - name='shotgun', - field=models.BooleanField(default=False, verbose_name='Disponible imm\xe9diatement'), + model_name="spectaclerevente", + name="shotgun", + field=models.BooleanField( + default=False, verbose_name="Disponible imm\xe9diatement" + ), ), migrations.RunPython(forwards_func, migrations.RunPython.noop), ] diff --git a/bda/migrations/0011_tirage_appear_catalogue.py b/bda/migrations/0011_tirage_appear_catalogue.py index c2a2479d..446be392 100644 --- a/bda/migrations/0011_tirage_appear_catalogue.py +++ b/bda/migrations/0011_tirage_appear_catalogue.py @@ -6,17 +6,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0010_spectaclerevente_shotgun'), - ] + dependencies = [("bda", "0010_spectaclerevente_shotgun")] operations = [ migrations.AddField( - model_name='tirage', - name='appear_catalogue', + model_name="tirage", + name="appear_catalogue", field=models.BooleanField( - default=False, - verbose_name='Tirage à afficher dans le catalogue' + default=False, verbose_name="Tirage à afficher dans le catalogue" ), - ), + ) ] diff --git a/bda/migrations/0012_notif_time.py b/bda/migrations/0012_notif_time.py index ee777e35..96853a24 100644 --- a/bda/migrations/0012_notif_time.py +++ b/bda/migrations/0012_notif_time.py @@ -6,24 +6,26 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('bda', '0011_tirage_appear_catalogue'), - ] + dependencies = [("bda", "0011_tirage_appear_catalogue")] operations = [ migrations.RenameField( - model_name='spectaclerevente', - old_name='answered_mail', - new_name='confirmed_entry', + model_name="spectaclerevente", + old_name="answered_mail", + new_name="confirmed_entry", ), migrations.AlterField( - model_name='spectaclerevente', - name='confirmed_entry', - field=models.ManyToManyField(blank=True, related_name='entered', to='bda.Participant'), + model_name="spectaclerevente", + name="confirmed_entry", + field=models.ManyToManyField( + blank=True, related_name="entered", to="bda.Participant" + ), ), migrations.AddField( - model_name='spectaclerevente', - name='notif_time', - field=models.DateTimeField(blank=True, verbose_name="Moment d'envoi de la notification", null=True), + model_name="spectaclerevente", + name="notif_time", + field=models.DateTimeField( + blank=True, verbose_name="Moment d'envoi de la notification", null=True + ), ), ] diff --git a/bda/migrations/0012_swap_double_choice.py b/bda/migrations/0012_swap_double_choice.py index 56f3e739..e712f2ff 100644 --- a/bda/migrations/0012_swap_double_choice.py +++ b/bda/migrations/0012_swap_double_choice.py @@ -14,40 +14,38 @@ def swap_double_choice(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('bda', '0011_tirage_appear_catalogue'), - ] + dependencies = [("bda", "0011_tirage_appear_catalogue")] operations = [ # Temporarily allow an extra "tmp" value for the `double_choice` field migrations.AlterField( - model_name='choixspectacle', - name='double_choice', + model_name="choixspectacle", + name="double_choice", field=models.CharField( - verbose_name='Nombre de places', + verbose_name="Nombre de places", max_length=10, - default='1', + default="1", choices=[ - ('tmp', 'tmp'), - ('1', '1 place'), - ('double', '2 places si possible, 1 sinon'), - ('autoquit', '2 places sinon rien') - ] + ("tmp", "tmp"), + ("1", "1 place"), + ("double", "2 places si possible, 1 sinon"), + ("autoquit", "2 places sinon rien"), + ], ), ), migrations.RunPython(swap_double_choice, migrations.RunPython.noop), migrations.AlterField( - model_name='choixspectacle', - name='double_choice', + model_name="choixspectacle", + name="double_choice", field=models.CharField( - verbose_name='Nombre de places', + verbose_name="Nombre de places", max_length=10, - default='1', + default="1", choices=[ - ('1', '1 place'), - ('double', '2 places si possible, 1 sinon'), - ('autoquit', '2 places sinon rien') - ] + ("1", "1 place"), + ("double", "2 places si possible, 1 sinon"), + ("autoquit", "2 places sinon rien"), + ], ), ), ] diff --git a/bda/migrations/0013_merge_20180524_2123.py b/bda/migrations/0013_merge_20180524_2123.py index ae8b0630..8f78b6a9 100644 --- a/bda/migrations/0013_merge_20180524_2123.py +++ b/bda/migrations/0013_merge_20180524_2123.py @@ -7,10 +7,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('bda', '0012_notif_time'), - ('bda', '0012_swap_double_choice'), - ] + dependencies = [("bda", "0012_notif_time"), ("bda", "0012_swap_double_choice")] - operations = [ - ] + operations = [] diff --git a/bda/models.py b/bda/models.py index 63e01e31..bd4ea4cb 100644 --- a/bda/models.py +++ b/bda/models.py @@ -1,23 +1,22 @@ import calendar import random from datetime import timedelta -from custommail.shortcuts import send_mass_custom_mail +from custommail.models import CustomMail +from custommail.shortcuts import send_mass_custom_mail +from django.conf import settings +from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.core import mail from django.db import models from django.db.models import Count -from django.contrib.auth.models import User -from django.conf import settings -from django.utils import timezone, formats - -from custommail.models import CustomMail +from django.utils import formats, timezone def get_generic_user(): generic, _ = User.objects.get_or_create( username="bda_generic", - defaults={"email": "bda@ens.fr", "first_name": "Bureau des arts"} + defaults={"email": "bda@ens.fr", "first_name": "Bureau des arts"}, ) return generic @@ -29,15 +28,15 @@ class Tirage(models.Model): tokens = models.TextField("Graine(s) du tirage", blank=True) active = models.BooleanField("Tirage actif", default=False) appear_catalogue = models.BooleanField( - "Tirage à afficher dans le catalogue", - default=False + "Tirage à afficher dans le catalogue", default=False ) - enable_do_tirage = models.BooleanField("Le tirage peut être lancé", - default=False) + enable_do_tirage = models.BooleanField("Le tirage peut être lancé", default=False) def __str__(self): - return "%s - %s" % (self.title, formats.localize( - timezone.template_localtime(self.fermeture))) + return "%s - %s" % ( + self.title, + formats.localize(timezone.template_localtime(self.fermeture)), + ) class Salle(models.Model): @@ -49,7 +48,7 @@ class Salle(models.Model): class CategorieSpectacle(models.Model): - name = models.CharField('Nom', max_length=100, unique=True) + name = models.CharField("Nom", max_length=100, unique=True) def __str__(self): return self.name @@ -61,28 +60,26 @@ class CategorieSpectacle(models.Model): class Spectacle(models.Model): title = models.CharField("Titre", max_length=300) category = models.ForeignKey( - CategorieSpectacle, on_delete=models.CASCADE, - blank=True, null=True, + CategorieSpectacle, on_delete=models.CASCADE, blank=True, null=True ) date = models.DateTimeField("Date & heure") location = models.ForeignKey(Salle, on_delete=models.CASCADE) - vips = models.TextField('Personnalités', blank=True) + vips = models.TextField("Personnalités", blank=True) description = models.TextField("Description", blank=True) slots_description = models.TextField("Description des places", blank=True) - image = models.ImageField('Image', blank=True, null=True, - upload_to='imgs/shows/') - ext_link = models.CharField('Lien vers le site du spectacle', blank=True, - max_length=500) + image = models.ImageField("Image", blank=True, null=True, upload_to="imgs/shows/") + ext_link = models.CharField( + "Lien vers le site du spectacle", blank=True, max_length=500 + ) price = models.FloatField("Prix d'une place") slots = models.IntegerField("Places") tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE) listing = models.BooleanField("Les places sont sur listing") - rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True, - null=True) + rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True, null=True) class Meta: verbose_name = "Spectacle" - ordering = ("date", "title",) + ordering = ("date", "title") def timestamp(self): return "%d" % calendar.timegm(self.date.utctimetuple()) @@ -92,7 +89,7 @@ class Spectacle(models.Model): self.title, formats.localize(timezone.template_localtime(self.date)), self.location, - self.price + self.price, ) def getImgUrl(self): @@ -111,19 +108,21 @@ class Spectacle(models.Model): """ # On récupère la liste des participants + le BdA members = list( - User.objects - .filter(participant__attributions=self) - .annotate(nb_attr=Count("id")).order_by() + User.objects.filter(participant__attributions=self) + .annotate(nb_attr=Count("id")) + .order_by() ) bda_generic = get_generic_user() bda_generic.nb_attr = 1 members.append(bda_generic) # On écrit un mail personnalisé à chaque participant - datatuple = [( - 'bda-rappel', - {'member': member, "nb_attr": member.nb_attr, 'show': self}, - settings.MAIL_DATA['rappels']['FROM'], - [member.email]) + datatuple = [ + ( + "bda-rappel", + {"member": member, "nb_attr": member.nb_attr, "show": self}, + settings.MAIL_DATA["rappels"]["FROM"], + [member.email], + ) for member in members ] send_mass_custom_mail(datatuple) @@ -140,8 +139,8 @@ class Spectacle(models.Model): class Quote(models.Model): spectacle = models.ForeignKey(Spectacle, on_delete=models.CASCADE) - text = models.TextField('Citation') - author = models.CharField('Auteur', max_length=200) + text = models.TextField("Citation") + author = models.CharField("Auteur", max_length=200) PAYMENT_TYPES = ( @@ -154,20 +153,20 @@ PAYMENT_TYPES = ( class Participant(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) - choices = models.ManyToManyField(Spectacle, - through="ChoixSpectacle", - related_name="chosen_by") - attributions = models.ManyToManyField(Spectacle, - through="Attribution", - related_name="attributed_to") + choices = models.ManyToManyField( + Spectacle, through="ChoixSpectacle", related_name="chosen_by" + ) + attributions = models.ManyToManyField( + Spectacle, through="Attribution", related_name="attributed_to" + ) paid = models.BooleanField("A payé", default=False) - paymenttype = models.CharField("Moyen de paiement", - max_length=6, choices=PAYMENT_TYPES, - blank=True) + paymenttype = models.CharField( + "Moyen de paiement", max_length=6, choices=PAYMENT_TYPES, blank=True + ) tirage = models.ForeignKey(Tirage, on_delete=models.CASCADE) - choicesrevente = models.ManyToManyField(Spectacle, - related_name="subscribed", - blank=True) + choicesrevente = models.ManyToManyField( + Spectacle, related_name="subscribed", blank=True + ) def __str__(self): return "%s - %s" % (self.user, self.tirage.title) @@ -183,30 +182,32 @@ DOUBLE_CHOICES = ( class ChoixSpectacle(models.Model): participant = models.ForeignKey(Participant, on_delete=models.CASCADE) spectacle = models.ForeignKey( - Spectacle, on_delete=models.CASCADE, - related_name="participants", + Spectacle, on_delete=models.CASCADE, related_name="participants" ) priority = models.PositiveIntegerField("Priorité") - double_choice = models.CharField("Nombre de places", - default="1", choices=DOUBLE_CHOICES, - max_length=10) + double_choice = models.CharField( + "Nombre de places", default="1", choices=DOUBLE_CHOICES, max_length=10 + ) def get_double(self): return self.double_choice != "1" + double = property(get_double) def get_autoquit(self): return self.double_choice == "autoquit" + autoquit = property(get_autoquit) def __str__(self): return "Vœux de %s pour %s" % ( - self.participant.user.get_full_name(), - self.spectacle.title) + self.participant.user.get_full_name(), + self.spectacle.title, + ) class Meta: ordering = ("priority",) - unique_together = (("participant", "spectacle",),) + unique_together = (("participant", "spectacle"),) verbose_name = "voeu" verbose_name_plural = "voeux" @@ -214,48 +215,49 @@ class ChoixSpectacle(models.Model): class Attribution(models.Model): participant = models.ForeignKey(Participant, on_delete=models.CASCADE) spectacle = models.ForeignKey( - Spectacle, on_delete=models.CASCADE, - related_name="attribues", + Spectacle, on_delete=models.CASCADE, related_name="attribues" ) given = models.BooleanField("Donnée", default=False) def __str__(self): - return "%s -- %s, %s" % (self.participant.user, self.spectacle.title, - self.spectacle.date) + return "%s -- %s, %s" % ( + self.participant.user, + self.spectacle.title, + self.spectacle.date, + ) class SpectacleRevente(models.Model): attribution = models.OneToOneField( - Attribution, on_delete=models.CASCADE, - related_name="revente", + Attribution, on_delete=models.CASCADE, related_name="revente" + ) + date = models.DateTimeField("Date de mise en vente", default=timezone.now) + confirmed_entry = models.ManyToManyField( + Participant, related_name="entered", blank=True ) - date = models.DateTimeField("Date de mise en vente", - default=timezone.now) - confirmed_entry = models.ManyToManyField(Participant, - related_name="entered", - blank=True) seller = models.ForeignKey( - Participant, on_delete=models.CASCADE, + Participant, + on_delete=models.CASCADE, verbose_name="Vendeur", related_name="original_shows", ) soldTo = models.ForeignKey( - Participant, on_delete=models.CASCADE, + Participant, + on_delete=models.CASCADE, verbose_name="Vendue à", - blank=True, null=True, + blank=True, + null=True, ) - notif_sent = models.BooleanField("Notification envoyée", - default=False) + notif_sent = models.BooleanField("Notification envoyée", default=False) - notif_time = models.DateTimeField("Moment d'envoi de la notification", - blank=True, null=True) + notif_time = models.DateTimeField( + "Moment d'envoi de la notification", blank=True, null=True + ) - tirage_done = models.BooleanField("Tirage effectué", - default=False) + tirage_done = models.BooleanField("Tirage effectué", default=False) - shotgun = models.BooleanField("Disponible immédiatement", - default=False) + shotgun = models.BooleanField("Disponible immédiatement", default=False) #### # Some class attributes ### @@ -282,8 +284,9 @@ class SpectacleRevente(models.Model): def date_tirage(self): """Renvoie la date du tirage au sort de la revente.""" - remaining_time = (self.attribution.spectacle.date - - self.real_notif_time - self.min_margin) + remaining_time = ( + self.attribution.spectacle.date - self.real_notif_time - self.min_margin + ) delay = min(remaining_time, self.max_wait_time) @@ -296,16 +299,14 @@ class SpectacleRevente(models.Model): Plus précisément, on doit avoir min_margin + min_wait_time de marge. """ spectacle_date = self.attribution.spectacle.date - return (spectacle_date <= timezone.now() + self.min_margin - + self.min_wait_time) + return spectacle_date <= timezone.now() + self.min_margin + self.min_wait_time @property def can_notif(self): - return (timezone.now() >= self.date + self.remorse_time) + return timezone.now() >= self.date + self.remorse_time def __str__(self): - return "%s -- %s" % (self.seller, - self.attribution.spectacle.title) + return "%s -- %s" % (self.seller, self.attribution.spectacle.title) class Meta: verbose_name = "Revente" @@ -327,17 +328,19 @@ class SpectacleRevente(models.Model): Envoie une notification pour indiquer la mise en vente d'une place sur BdA-Revente à tous les intéressés. """ - inscrits = self.attribution.spectacle.subscribed.select_related('user') - datatuple = [( - 'bda-revente', - { - 'member': participant.user, - 'show': self.attribution.spectacle, - 'revente': self, - 'site': Site.objects.get_current() - }, - settings.MAIL_DATA['revente']['FROM'], - [participant.user.email]) + inscrits = self.attribution.spectacle.subscribed.select_related("user") + datatuple = [ + ( + "bda-revente", + { + "member": participant.user, + "show": self.attribution.spectacle, + "revente": self, + "site": Site.objects.get_current(), + }, + settings.MAIL_DATA["revente"]["FROM"], + [participant.user.email], + ) for participant in inscrits ] send_mass_custom_mail(datatuple) @@ -350,16 +353,18 @@ class SpectacleRevente(models.Model): Envoie un mail à toutes les personnes intéréssées par le spectacle pour leur indiquer qu'il est désormais disponible au shotgun. """ - inscrits = self.attribution.spectacle.subscribed.select_related('user') - datatuple = [( - 'bda-shotgun', - { - 'member': participant.user, - 'show': self.attribution.spectacle, - 'site': Site.objects.get_current(), - }, - settings.MAIL_DATA['revente']['FROM'], - [participant.user.email]) + inscrits = self.attribution.spectacle.subscribed.select_related("user") + datatuple = [ + ( + "bda-shotgun", + { + "member": participant.user, + "show": self.attribution.spectacle, + "site": Site.objects.get_current(), + }, + settings.MAIL_DATA["revente"]["FROM"], + [participant.user.email], + ) for participant in inscrits ] send_mass_custom_mail(datatuple) @@ -389,30 +394,33 @@ class SpectacleRevente(models.Model): mails = [] context = { - 'acheteur': winner.user, - 'vendeur': seller.user, - 'show': spectacle, + "acheteur": winner.user, + "vendeur": seller.user, + "show": spectacle, } - c_mails_qs = CustomMail.objects.filter(shortname__in=[ - 'bda-revente-winner', 'bda-revente-loser', - 'bda-revente-seller', - ]) + c_mails_qs = CustomMail.objects.filter( + shortname__in=[ + "bda-revente-winner", + "bda-revente-loser", + "bda-revente-seller", + ] + ) c_mails = {cm.shortname: cm for cm in c_mails_qs} mails.append( - c_mails['bda-revente-winner'].get_message( + c_mails["bda-revente-winner"].get_message( context, - from_email=settings.MAIL_DATA['revente']['FROM'], + from_email=settings.MAIL_DATA["revente"]["FROM"], to=[winner.user.email], ) ) mails.append( - c_mails['bda-revente-seller'].get_message( + c_mails["bda-revente-seller"].get_message( context, - from_email=settings.MAIL_DATA['revente']['FROM'], + from_email=settings.MAIL_DATA["revente"]["FROM"], to=[seller.user.email], reply_to=[winner.user.email], ) @@ -422,12 +430,12 @@ class SpectacleRevente(models.Model): for inscrit in inscrits: if inscrit != winner: new_context = dict(context) - new_context['acheteur'] = inscrit.user + new_context["acheteur"] = inscrit.user mails.append( - c_mails['bda-revente-loser'].get_message( + c_mails["bda-revente-loser"].get_message( new_context, - from_email=settings.MAIL_DATA['revente']['FROM'], + from_email=settings.MAIL_DATA["revente"]["FROM"], to=[inscrit.user.email], ) ) diff --git a/bda/tests/test_models.py b/bda/tests/test_models.py index 95ce8646..b8a23ba7 100644 --- a/bda/tests/test_models.py +++ b/bda/tests/test_models.py @@ -7,28 +7,33 @@ from django.test import TestCase from django.utils import timezone from bda.models import ( - Attribution, Participant, Salle, Spectacle, SpectacleRevente, Tirage, + Attribution, + Participant, + Salle, + Spectacle, + SpectacleRevente, + Tirage, ) User = get_user_model() class SpectacleReventeTests(TestCase): - fixtures = ['gestioncof/management/data/custommail.json'] + fixtures = ["gestioncof/management/data/custommail.json"] def setUp(self): now = timezone.now() self.t = Tirage.objects.create( - title='Tirage', + title="Tirage", ouverture=now - timedelta(days=7), fermeture=now - timedelta(days=3), active=True, ) self.s = Spectacle.objects.create( - title='Spectacle', + title="Spectacle", date=now + timedelta(days=20), - location=Salle.objects.create(name='Salle', address='Address'), + location=Salle.objects.create(name="Salle", address="Address"), price=10.5, slots=5, tirage=self.t, @@ -36,31 +41,28 @@ class SpectacleReventeTests(TestCase): ) self.seller = Participant.objects.create( - user=User.objects.create( - username='seller', email='seller@mail.net'), + user=User.objects.create(username="seller", email="seller@mail.net"), tirage=self.t, ) self.p1 = Participant.objects.create( - user=User.objects.create(username='part1', email='part1@mail.net'), + user=User.objects.create(username="part1", email="part1@mail.net"), tirage=self.t, ) self.p2 = Participant.objects.create( - user=User.objects.create(username='part2', email='part2@mail.net'), + user=User.objects.create(username="part2", email="part2@mail.net"), tirage=self.t, ) self.p3 = Participant.objects.create( - user=User.objects.create(username='part3', email='part3@mail.net'), + user=User.objects.create(username="part3", email="part3@mail.net"), tirage=self.t, ) self.attr = Attribution.objects.create( - participant=self.seller, - spectacle=self.s, + participant=self.seller, spectacle=self.s ) self.rev = SpectacleRevente.objects.create( - attribution=self.attr, - seller=self.seller, + attribution=self.attr, seller=self.seller ) def test_tirage(self): @@ -69,7 +71,7 @@ class SpectacleReventeTests(TestCase): wanted_by = [self.p1, self.p2, self.p3] revente.confirmed_entry = wanted_by - with mock.patch('bda.models.random.choice') as mc: + with mock.patch("bda.models.random.choice") as mc: # Set winner to self.p1. mc.return_value = self.p1 @@ -87,14 +89,14 @@ class SpectacleReventeTests(TestCase): self.assertEqual(len(mails), 4) - m_seller = mails['seller@mail.net'] - self.assertListEqual(m_seller.to, ['seller@mail.net']) - self.assertListEqual(m_seller.reply_to, ['part1@mail.net']) + m_seller = mails["seller@mail.net"] + self.assertListEqual(m_seller.to, ["seller@mail.net"]) + self.assertListEqual(m_seller.reply_to, ["part1@mail.net"]) - m_winner = mails['part1@mail.net'] - self.assertListEqual(m_winner.to, ['part1@mail.net']) + m_winner = mails["part1@mail.net"] + self.assertListEqual(m_winner.to, ["part1@mail.net"]) self.assertCountEqual( - [mails['part2@mail.net'].to, mails['part3@mail.net'].to], - [['part2@mail.net'], ['part3@mail.net']], + [mails["part2@mail.net"].to, mails["part3@mail.net"].to], + [["part2@mail.net"], ["part3@mail.net"]], ) diff --git a/bda/tests/test_revente.py b/bda/tests/test_revente.py index 8ef7be19..b0d69dc7 100644 --- a/bda/tests/test_revente.py +++ b/bda/tests/test_revente.py @@ -1,60 +1,71 @@ -from django.contrib.auth.models import User -from django.test import TestCase, Client -from django.utils import timezone - from datetime import timedelta -from bda.models import (Tirage, Spectacle, Salle, CategorieSpectacle, - SpectacleRevente, Attribution, Participant) +from django.contrib.auth.models import User +from django.test import Client, TestCase +from django.utils import timezone + +from bda.models import ( + Attribution, + CategorieSpectacle, + Participant, + Salle, + Spectacle, + SpectacleRevente, + Tirage, +) class TestModels(TestCase): def setUp(self): self.tirage = Tirage.objects.create( - title="Tirage test", - appear_catalogue=True, - ouverture=timezone.now(), - fermeture=timezone.now() + title="Tirage test", + appear_catalogue=True, + ouverture=timezone.now(), + fermeture=timezone.now(), ) self.category = CategorieSpectacle.objects.create(name="Category") self.location = Salle.objects.create(name="here") self.spectacle_soon = Spectacle.objects.create( - title="foo", date=timezone.now()+timedelta(days=1), - location=self.location, price=0, slots=42, - tirage=self.tirage, listing=False, category=self.category + title="foo", + date=timezone.now() + timedelta(days=1), + location=self.location, + price=0, + slots=42, + tirage=self.tirage, + listing=False, + category=self.category, ) self.spectacle_later = Spectacle.objects.create( - title="bar", date=timezone.now()+timedelta(days=30), - location=self.location, price=0, slots=42, - tirage=self.tirage, listing=False, category=self.category + title="bar", + date=timezone.now() + timedelta(days=30), + location=self.location, + price=0, + slots=42, + tirage=self.tirage, + listing=False, + category=self.category, ) user_buyer = User.objects.create_user( - username="bda_buyer", password="testbuyer" + username="bda_buyer", password="testbuyer" ) user_seller = User.objects.create_user( - username="bda_seller", password="testseller" - ) - self.buyer = Participant.objects.create( - user=user_buyer, tirage=self.tirage - ) - self.seller = Participant.objects.create( - user=user_seller, tirage=self.tirage + username="bda_seller", password="testseller" ) + self.buyer = Participant.objects.create(user=user_buyer, tirage=self.tirage) + self.seller = Participant.objects.create(user=user_seller, tirage=self.tirage) self.attr_soon = Attribution.objects.create( - participant=self.seller, spectacle=self.spectacle_soon + participant=self.seller, spectacle=self.spectacle_soon ) self.attr_later = Attribution.objects.create( - participant=self.seller, spectacle=self.spectacle_later + participant=self.seller, spectacle=self.spectacle_later ) self.revente_soon = SpectacleRevente.objects.create( - seller=self.seller, - attribution=self.attr_soon + seller=self.seller, attribution=self.attr_soon ) self.revente_later = SpectacleRevente.objects.create( - seller=self.seller, - attribution=self.attr_later + seller=self.seller, attribution=self.attr_later ) def test_urgent(self): @@ -64,6 +75,5 @@ class TestModels(TestCase): def test_tirage(self): self.revente_soon.confirmed_entry.add(self.buyer) - self.assertEqual(self.revente_soon.tirage(send_mails=False), - self.buyer) + self.assertEqual(self.revente_soon.tirage(send_mails=False), self.buyer) self.assertIsNone(self.revente_later.tirage(send_mails=False)) diff --git a/bda/tests/test_views.py b/bda/tests/test_views.py index 39c11c79..8bd5b462 100644 --- a/bda/tests/test_views.py +++ b/bda/tests/test_views.py @@ -1,14 +1,13 @@ import json - from datetime import timedelta from unittest import mock from urllib.parse import urlencode from django.contrib.auth.models import User -from django.test import TestCase, Client +from django.test import Client, TestCase from django.utils import timezone -from ..models import Tirage, Spectacle, Salle, CategorieSpectacle +from ..models import CategorieSpectacle, Salle, Spectacle, Tirage def create_user(username, is_cof=False, is_buro=False): @@ -46,16 +45,17 @@ class BdATestHelpers: (staff, staff_c), (member, member_c), (other, other_c), - (None, Client()) + (None, Client()), ] def require_custommails(self): from django.core.management import call_command + call_command("syncmails") - def check_restricted_access(self, url, - validate_user=user_is_cof, - redirect_url=None): + def check_restricted_access( + self, url, validate_user=user_is_cof, redirect_url=None + ): def craft_redirect_url(user): if redirect_url: return redirect_url @@ -63,8 +63,7 @@ class BdATestHelpers: # client is not logged in login_url = "/login" if url: - login_url += "?{}".format(urlencode({"next": url}, - safe="/")) + login_url += "?{}".format(urlencode({"next": url}, safe="/")) return login_url else: return "/" @@ -82,7 +81,7 @@ class TestBdAViews(BdATestHelpers, TestCase): # Signals handlers on login/logout send messages. # Due to the way the Django' test Client performs login, this raise an # error. As workaround, we mock the Django' messages module. - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) # Set up the helpers @@ -96,23 +95,40 @@ class TestBdAViews(BdATestHelpers, TestCase): ) self.category = CategorieSpectacle.objects.create(name="Category") self.location = Salle.objects.create(name="here") - Spectacle.objects.bulk_create([ - Spectacle( - title="foo", date=timezone.now(), location=self.location, - price=0, slots=42, tirage=self.tirage, listing=False, - category=self.category - ), - Spectacle( - title="bar", date=timezone.now(), location=self.location, - price=1, slots=142, tirage=self.tirage, listing=False, - category=self.category - ), - Spectacle( - title="baz", date=timezone.now(), location=self.location, - price=2, slots=242, tirage=self.tirage, listing=False, - category=self.category - ), - ]) + Spectacle.objects.bulk_create( + [ + Spectacle( + title="foo", + date=timezone.now(), + location=self.location, + price=0, + slots=42, + tirage=self.tirage, + listing=False, + category=self.category, + ), + Spectacle( + title="bar", + date=timezone.now(), + location=self.location, + price=1, + slots=142, + tirage=self.tirage, + listing=False, + category=self.category, + ), + Spectacle( + title="baz", + date=timezone.now(), + location=self.location, + price=2, + slots=242, + tirage=self.tirage, + listing=False, + category=self.category, + ), + ] + ) def test_bda_inscriptions(self): # TODO: test the form @@ -195,7 +211,7 @@ class TestBdAViews(BdATestHelpers, TestCase): resp = client.get(url_list) self.assertJSONEqual( resp.content.decode("utf-8"), - [{"id": self.tirage.id, "title": self.tirage.title}] + [{"id": self.tirage.id, "title": self.tirage.title}], ) # Details @@ -203,15 +219,9 @@ class TestBdAViews(BdATestHelpers, TestCase): self.assertJSONEqual( resp.content.decode("utf-8"), { - "categories": [{ - "id": self.category.id, - "name": self.category.name - }], - "locations": [{ - "id": self.location.id, - "name": self.location.name - }], - } + "categories": [{"id": self.category.id, "name": self.category.name}], + "locations": [{"id": self.location.id, "name": self.location.name}], + }, ) # Descriptions @@ -224,7 +234,7 @@ class TestBdAViews(BdATestHelpers, TestCase): self.assertEqual(len(results), 3) self.assertEqual( {(s["title"], s["price"], s["slots"]) for s in results}, - {("foo", 0, 42), ("bar", 1, 142), ("baz", 2, 242)} + {("foo", 0, 42), ("bar", 1, 142), ("baz", 2, 242)}, ) diff --git a/bda/urls.py b/bda/urls.py index 7264d7b3..7ceccfe0 100644 --- a/bda/urls.py +++ b/bda/urls.py @@ -1,62 +1,75 @@ from django.conf.urls import url -from gestioncof.decorators import buro_required -from bda.views import SpectacleListView + from bda import views +from bda.views import SpectacleListView +from gestioncof.decorators import buro_required urlpatterns = [ - url(r'^inscription/(?P\d+)$', + url( + r"^inscription/(?P\d+)$", views.inscription, - name='bda-tirage-inscription'), - url(r'^places/(?P\d+)$', - views.places, - name="bda-places-attribuees"), - url(r'^etat-places/(?P\d+)$', - views.etat_places, - name='bda-etat-places'), - url(r'^tirage/(?P\d+)$', views.tirage), - url(r'^spectacles/(?P\d+)$', + name="bda-tirage-inscription", + ), + url(r"^places/(?P\d+)$", views.places, name="bda-places-attribuees"), + url(r"^etat-places/(?P\d+)$", views.etat_places, name="bda-etat-places"), + url(r"^tirage/(?P\d+)$", views.tirage), + url( + r"^spectacles/(?P\d+)$", buro_required(SpectacleListView.as_view()), - name="bda-liste-spectacles"), - url(r'^spectacles/(?P\d+)/(?P\d+)$', + name="bda-liste-spectacles", + ), + url( + r"^spectacles/(?P\d+)/(?P\d+)$", views.spectacle, - name="bda-spectacle"), - url(r'^spectacles/unpaid/(?P\d+)$', - views.unpaid, - name="bda-unpaid"), - url(r'^spectacles/autocomplete$', + name="bda-spectacle", + ), + url(r"^spectacles/unpaid/(?P\d+)$", views.unpaid, name="bda-unpaid"), + url( + r"^spectacles/autocomplete$", views.spectacle_autocomplete, - name="bda-spectacle-autocomplete"), - url(r'^participants/autocomplete$', + name="bda-spectacle-autocomplete", + ), + url( + r"^participants/autocomplete$", views.participant_autocomplete, - name="bda-participant-autocomplete"), - + name="bda-participant-autocomplete", + ), # Urls BdA-Revente - - url(r'^revente/(?P\d+)/manage$', + url( + r"^revente/(?P\d+)/manage$", views.revente_manage, - name='bda-revente-manage'), - url(r'^revente/(?P\d+)/subscribe$', + name="bda-revente-manage", + ), + url( + r"^revente/(?P\d+)/subscribe$", views.revente_subscribe, - name="bda-revente-subscribe"), - url(r'^revente/(?P\d+)/tirages$', + name="bda-revente-subscribe", + ), + url( + r"^revente/(?P\d+)/tirages$", views.revente_tirages, - name="bda-revente-tirages"), - url(r'^revente/(?P\d+)/buy$', + name="bda-revente-tirages", + ), + url( + r"^revente/(?P\d+)/buy$", views.revente_buy, - name="bda-revente-buy"), - url(r'^revente/(?P\d+)/confirm$', + name="bda-revente-buy", + ), + url( + r"^revente/(?P\d+)/confirm$", views.revente_confirm, - name='bda-revente-confirm'), - url(r'^revente/(?P\d+)/shotgun$', + name="bda-revente-confirm", + ), + url( + r"^revente/(?P\d+)/shotgun$", views.revente_shotgun, - name="bda-revente-shotgun"), - - url(r'^mails-rappel/(?P\d+)$', - views.send_rappel, - name="bda-rappels" - ), - url(r'^descriptions/(?P\d+)$', views.descriptions_spectacles, - name='bda-descriptions'), - url(r'^catalogue/(?P[a-z]+)$', views.catalogue, - name='bda-catalogue'), + name="bda-revente-shotgun", + ), + url(r"^mails-rappel/(?P\d+)$", views.send_rappel, name="bda-rappels"), + url( + r"^descriptions/(?P\d+)$", + views.descriptions_spectacles, + name="bda-descriptions", + ), + url(r"^catalogue/(?P[a-z]+)$", views.catalogue, name="bda-catalogue"), ] diff --git a/bda/views.py b/bda/views.py index b49eb030..cbad0b1b 100644 --- a/bda/views.py +++ b/bda/views.py @@ -1,36 +1,47 @@ -from collections import defaultdict -import random import hashlib -import time import json -from custommail.shortcuts import send_mass_custom_mail, send_custom_mail +import random +import time +from collections import defaultdict + from custommail.models import CustomMail -from django.shortcuts import render, get_object_or_404 -from django.contrib.auth.decorators import login_required -from django.contrib import messages -from django.db import transaction -from django.core import serializers -from django.db.models import Count, Q, Prefetch -from django.template.defaultfilters import pluralize -from django.forms.models import inlineformset_factory -from django.http import ( - HttpResponseBadRequest, HttpResponseRedirect, JsonResponse -) -from django.core.urlresolvers import reverse +from custommail.shortcuts import send_custom_mail, send_mass_custom_mail from django.conf import settings -from django.utils import timezone, formats +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.core import serializers +from django.core.urlresolvers import reverse +from django.db import transaction +from django.db.models import Count, Prefetch, Q +from django.forms.models import inlineformset_factory +from django.http import HttpResponseBadRequest, HttpResponseRedirect, JsonResponse +from django.shortcuts import get_object_or_404, render +from django.template.defaultfilters import pluralize +from django.utils import formats, timezone from django.views.generic.list import ListView -from gestioncof.decorators import cof_required, buro_required -from bda.models import ( - Spectacle, Participant, ChoixSpectacle, Attribution, Tirage, - SpectacleRevente, Salle, CategorieSpectacle -) + from bda.algorithm import Algorithm from bda.forms import ( - TokenForm, ResellForm, AnnulForm, InscriptionReventeForm, SoldForm, - InscriptionInlineFormSet, ReventeTirageForm, ReventeTirageAnnulForm + AnnulForm, + InscriptionInlineFormSet, + InscriptionReventeForm, + ResellForm, + ReventeTirageAnnulForm, + ReventeTirageForm, + SoldForm, + TokenForm, ) - +from bda.models import ( + Attribution, + CategorieSpectacle, + ChoixSpectacle, + Participant, + Salle, + Spectacle, + SpectacleRevente, + Tirage, +) +from gestioncof.decorators import buro_required, cof_required from utils.views.autocomplete import Select2QuerySetView @@ -45,7 +56,7 @@ def etat_places(request, tirage_id): """ tirage = get_object_or_404(Tirage, id=tirage_id) - spectacles = tirage.spectacle_set.select_related('location') + spectacles = tirage.spectacle_set.select_related("location") spectacles_dict = {} # index of spectacle by id for spectacle in spectacles: @@ -53,10 +64,9 @@ def etat_places(request, tirage_id): spectacles_dict[spectacle.id] = spectacle choices = ( - ChoixSpectacle.objects - .filter(spectacle__in=spectacles) - .values('spectacle') - .annotate(total=Count('spectacle')) + ChoixSpectacle.objects.filter(spectacle__in=spectacles) + .values("spectacle") + .annotate(total=Count("spectacle")) ) # choices *by spectacles* whose only 1 place is requested @@ -65,11 +75,11 @@ def etat_places(request, tirage_id): choices2 = choices.exclude(double_choice="1") for spectacle in choices1: - pk = spectacle['spectacle'] - spectacles_dict[pk].total += spectacle['total'] + pk = spectacle["spectacle"] + spectacles_dict[pk].total += spectacle["total"] for spectacle in choices2: - pk = spectacle['spectacle'] - spectacles_dict[pk].total += 2*spectacle['total'] + pk = spectacle["spectacle"] + spectacles_dict[pk].total += 2 * spectacle["total"] # here, each spectacle.total contains the number of requests @@ -84,13 +94,13 @@ def etat_places(request, tirage_id): "proposed": slots, "spectacles": spectacles, "total": total, - 'tirage': tirage + "tirage": tirage, } return render(request, "bda/etat-places.html", context) def _hash_queryset(queryset): - data = serializers.serialize("json", queryset).encode('utf-8') + data = serializers.serialize("json", queryset).encode("utf-8") hasher = hashlib.sha256() hasher.update(data) return hasher.hexdigest() @@ -99,15 +109,10 @@ def _hash_queryset(queryset): @cof_required def places(request, tirage_id): tirage = get_object_or_404(Tirage, id=tirage_id) - participant, _ = ( - Participant.objects - .get_or_create(user=request.user, tirage=tirage) - ) - places = ( - participant.attribution_set - .order_by("spectacle__date", "spectacle") - .select_related("spectacle", "spectacle__location") - ) + participant, _ = Participant.objects.get_or_create(user=request.user, tirage=tirage) + places = participant.attribution_set.order_by( + "spectacle__date", "spectacle" + ).select_related("spectacle", "spectacle__location") total = sum(place.spectacle.price for place in places) filtered_places = [] places_dict = {} @@ -129,13 +134,21 @@ def places(request, tirage_id): dates.append(date) # On prévient l'utilisateur s'il a deux places à la même date if warning: - messages.warning(request, "Attention, vous avez reçu des places pour " - "des spectacles différents à la même date.") - return render(request, "bda/resume_places.html", - {"participant": participant, - "places": filtered_places, - "tirage": tirage, - "total": total}) + messages.warning( + request, + "Attention, vous avez reçu des places pour " + "des spectacles différents à la même date.", + ) + return render( + request, + "bda/resume_places.html", + { + "participant": participant, + "places": filtered_places, + "tirage": tirage, + "total": total, + }, + ) @cof_required @@ -151,24 +164,24 @@ def inscription(request, tirage_id): tirage = get_object_or_404(Tirage, id=tirage_id) if timezone.now() < tirage.ouverture: # Le tirage n'est pas encore ouvert. - opening = formats.localize( - timezone.template_localtime(tirage.ouverture)) - messages.error(request, "Le tirage n'est pas encore ouvert : " - "ouverture le {:s}".format(opening)) - return render(request, 'bda/resume-inscription-tirage.html', {}) + opening = formats.localize(timezone.template_localtime(tirage.ouverture)) + messages.error( + request, + "Le tirage n'est pas encore ouvert : " "ouverture le {:s}".format(opening), + ) + return render(request, "bda/resume-inscription-tirage.html", {}) - participant, _ = ( - Participant.objects.select_related('tirage') - .get_or_create(user=request.user, tirage=tirage) + participant, _ = Participant.objects.select_related("tirage").get_or_create( + user=request.user, tirage=tirage ) if timezone.now() > tirage.fermeture: # Le tirage est fermé. choices = participant.choixspectacle_set.order_by("priority") - messages.error(request, - " C'est fini : tirage au sort dans la journée !") - return render(request, "bda/resume-inscription-tirage.html", - {"choices": choices}) + messages.error(request, " C'est fini : tirage au sort dans la journée !") + return render( + request, "bda/resume-inscription-tirage.html", {"choices": choices} + ) BdaFormSet = inlineformset_factory( Participant, @@ -196,27 +209,33 @@ def inscription(request, tirage_id): # use *this* queryset dbstate = _hash_queryset(participant.choixspectacle_set.all()) total_price = 0 - choices = ( - participant.choixspectacle_set - .select_related('spectacle') - ) + choices = participant.choixspectacle_set.select_related("spectacle") for choice in choices: total_price += choice.spectacle.price if choice.double: total_price += choice.spectacle.price # Messages if success: - messages.success(request, "Votre inscription a été mise à jour avec " - "succès !") + messages.success( + request, "Votre inscription a été mise à jour avec " "succès !" + ) if stateerror: - messages.error(request, "Impossible d'enregistrer vos modifications " - ": vous avez apporté d'autres modifications " - "entre temps.") - return render(request, "bda/inscription-tirage.html", - {"formset": formset, - "total_price": total_price, - "dbstate": dbstate, - 'tirage': tirage}) + messages.error( + request, + "Impossible d'enregistrer vos modifications " + ": vous avez apporté d'autres modifications " + "entre temps.", + ) + return render( + request, + "bda/inscription-tirage.html", + { + "formset": formset, + "total_price": total_price, + "dbstate": dbstate, + "tirage": tirage, + }, + ) def do_tirage(tirage_elt, token): @@ -229,40 +248,39 @@ def do_tirage(tirage_elt, token): # Initialisation du dictionnaire data qui va contenir les résultats start = time.time() data = { - 'shows': tirage_elt.spectacle_set.select_related('location'), - 'token': token, - 'members': tirage_elt.participant_set.select_related('user'), - 'total_slots': 0, - 'total_losers': 0, - 'total_sold': 0, - 'total_deficit': 0, - 'opera_deficit': 0, + "shows": tirage_elt.spectacle_set.select_related("location"), + "token": token, + "members": tirage_elt.participant_set.select_related("user"), + "total_slots": 0, + "total_losers": 0, + "total_sold": 0, + "total_deficit": 0, + "opera_deficit": 0, } # On lance le tirage choices = ( - ChoixSpectacle.objects - .filter(spectacle__tirage=tirage_elt) - .order_by('participant', 'priority') - .select_related('participant', 'participant__user', 'spectacle') + ChoixSpectacle.objects.filter(spectacle__tirage=tirage_elt) + .order_by("participant", "priority") + .select_related("participant", "participant__user", "spectacle") ) - results = Algorithm(data['shows'], data['members'], choices)(token) + results = Algorithm(data["shows"], data["members"], choices)(token) # On compte les places attribuées et les déçus for (_, members, losers) in results: - data['total_slots'] += len(members) - data['total_losers'] += len(losers) + data["total_slots"] += len(members) + data["total_losers"] += len(losers) # On calcule le déficit et les bénéfices pour le BdA # FIXME: le traitement de l'opéra est sale for (show, members, _) in results: deficit = (show.slots - len(members)) * show.price - data['total_sold'] += show.slots * show.price + data["total_sold"] += show.slots * show.price if deficit >= 0: if "Opéra" in show.location.name: - data['opera_deficit'] += deficit - data['total_deficit'] += deficit - data["total_sold"] -= data['total_deficit'] + data["opera_deficit"] += deficit + data["total_deficit"] += deficit + data["total_sold"] -= data["total_deficit"] # Participant objects are not shared accross spectacle results, # so assign a single object for each Participant id @@ -288,32 +306,30 @@ def do_tirage(tirage_elt, token): # désactive le tirage Attribution.objects.filter(spectacle__tirage=tirage_elt).delete() tirage_elt.tokens += '{:s}\n"""{:s}"""\n'.format( - timezone.now().strftime("%y-%m-%d %H:%M:%S"), - token) + timezone.now().strftime("%y-%m-%d %H:%M:%S"), token + ) tirage_elt.enable_do_tirage = False tirage_elt.save() # On enregistre les nouvelles attributions - Attribution.objects.bulk_create([ - Attribution(spectacle=show, participant=member) - for show, members, _ in results - for member, _, _, _ in members - ]) + Attribution.objects.bulk_create( + [ + Attribution(spectacle=show, participant=member) + for show, members, _ in results + for member, _, _, _ in members + ] + ) # On inscrit à BdA-Revente ceux qui n'ont pas eu les places voulues ChoixRevente = Participant.choicesrevente.through # Suppression des reventes demandées/enregistrées # (si le tirage est relancé) + (ChoixRevente.objects.filter(spectacle__tirage=tirage_elt).delete()) ( - ChoixRevente.objects - .filter(spectacle__tirage=tirage_elt) - .delete() - ) - ( - SpectacleRevente.objects - .filter(attribution__spectacle__tirage=tirage_elt) - .delete() + SpectacleRevente.objects.filter( + attribution__spectacle__tirage=tirage_elt + ).delete() ) lost_by = defaultdict(set) @@ -335,13 +351,12 @@ def do_tirage(tirage_elt, token): @buro_required def tirage(request, tirage_id): tirage_elt = get_object_or_404(Tirage, id=tirage_id) - if not (tirage_elt.enable_do_tirage - and tirage_elt.fermeture < timezone.now()): - return render(request, "tirage-failed.html", {'tirage': tirage_elt}) + if not (tirage_elt.enable_do_tirage and tirage_elt.fermeture < timezone.now()): + return render(request, "tirage-failed.html", {"tirage": tirage_elt}) if request.POST: form = TokenForm(request.POST) if form.is_valid(): - results = do_tirage(tirage_elt, form.cleaned_data['token']) + results = do_tirage(tirage_elt, form.cleaned_data["token"]) return render(request, "bda-attrib-extra.html", results) else: form = TokenForm() @@ -360,56 +375,59 @@ def revente_manage(request, tirage_id): """ tirage = get_object_or_404(Tirage, id=tirage_id) participant, created = Participant.objects.get_or_create( - user=request.user, tirage=tirage) + user=request.user, tirage=tirage + ) if not participant.paid: return render(request, "bda/revente/notpaid.html", {}) - resellform = ResellForm(participant, prefix='resell') - annulform = AnnulForm(participant, prefix='annul') - soldform = SoldForm(participant, prefix='sold') + resellform = ResellForm(participant, prefix="resell") + annulform = AnnulForm(participant, prefix="annul") + soldform = SoldForm(participant, prefix="sold") - if request.method == 'POST': + if request.method == "POST": # On met en vente une place - if 'resell' in request.POST: - resellform = ResellForm(participant, request.POST, prefix='resell') + if "resell" in request.POST: + resellform = ResellForm(participant, request.POST, prefix="resell") if resellform.is_valid(): datatuple = [] attributions = resellform.cleaned_data["attributions"] with transaction.atomic(): for attribution in attributions: - revente, created = \ - SpectacleRevente.objects.get_or_create( - attribution=attribution, - defaults={'seller': participant}) + revente, created = SpectacleRevente.objects.get_or_create( + attribution=attribution, defaults={"seller": participant} + ) if not created: revente.reset() context = { - 'vendeur': participant.user, - 'show': attribution.spectacle, - 'revente': revente + "vendeur": participant.user, + "show": attribution.spectacle, + "revente": revente, } - datatuple.append(( - 'bda-revente-new', context, - settings.MAIL_DATA['revente']['FROM'], - [participant.user.email] - )) + datatuple.append( + ( + "bda-revente-new", + context, + settings.MAIL_DATA["revente"]["FROM"], + [participant.user.email], + ) + ) revente.save() send_mass_custom_mail(datatuple) # On annule une revente - elif 'annul' in request.POST: - annulform = AnnulForm(participant, request.POST, prefix='annul') + elif "annul" in request.POST: + annulform = AnnulForm(participant, request.POST, prefix="annul") if annulform.is_valid(): reventes = annulform.cleaned_data["reventes"] for revente in reventes: revente.delete() # On confirme une vente en transférant la place à la personne qui a # gagné le tirage - elif 'transfer' in request.POST: - soldform = SoldForm(participant, request.POST, prefix='sold') + elif "transfer" in request.POST: + soldform = SoldForm(participant, request.POST, prefix="sold") if soldform.is_valid(): - reventes = soldform.cleaned_data['reventes'] + reventes = soldform.cleaned_data["reventes"] for revente in reventes: revente.attribution.participant = revente.soldTo revente.attribution.save() @@ -417,28 +435,34 @@ def revente_manage(request, tirage_id): # On annule la revente après le tirage au sort (par exemple si # la personne qui a gagné le tirage ne se manifeste pas). La place est # alors remise en vente - elif 'reinit' in request.POST: - soldform = SoldForm(participant, request.POST, prefix='sold') + elif "reinit" in request.POST: + soldform = SoldForm(participant, request.POST, prefix="sold") if soldform.is_valid(): - reventes = soldform.cleaned_data['reventes'] + reventes = soldform.cleaned_data["reventes"] for revente in reventes: if revente.attribution.spectacle.date > timezone.now(): # On antidate pour envoyer le mail plus vite - new_date = (timezone.now() - - SpectacleRevente.remorse_time) + new_date = timezone.now() - SpectacleRevente.remorse_time revente.reset(new_date=new_date) overdue = participant.attribution_set.filter( spectacle__date__gte=timezone.now(), revente__isnull=False, revente__seller=participant, - revente__notif_sent=True)\ - .filter( - Q(revente__soldTo__isnull=True) | Q(revente__soldTo=participant)) + revente__notif_sent=True, + ).filter(Q(revente__soldTo__isnull=True) | Q(revente__soldTo=participant)) - return render(request, "bda/revente/manage.html", - {'tirage': tirage, 'overdue': overdue, "soldform": soldform, - "annulform": annulform, "resellform": resellform}) + return render( + request, + "bda/revente/manage.html", + { + "tirage": tirage, + "overdue": overdue, + "soldform": soldform, + "annulform": annulform, + "resellform": resellform, + }, + ) @login_required @@ -448,58 +472,64 @@ def revente_tirages(request, tirage_id): tirage donné) et lui permet de s'inscrire et se désinscrire à ces reventes. """ tirage = get_object_or_404(Tirage, id=tirage_id) - participant, _ = Participant.objects.get_or_create( - user=request.user, tirage=tirage) + participant, _ = Participant.objects.get_or_create(user=request.user, tirage=tirage) subform = ReventeTirageForm(participant, prefix="subscribe") annulform = ReventeTirageAnnulForm(participant, prefix="annul") - if request.method == 'POST': + if request.method == "POST": if "subscribe" in request.POST: - subform = ReventeTirageForm(participant, request.POST, - prefix="subscribe") + subform = ReventeTirageForm(participant, request.POST, prefix="subscribe") if subform.is_valid(): - reventes = subform.cleaned_data['reventes'] + reventes = subform.cleaned_data["reventes"] count = reventes.count() for revente in reventes: revente.confirmed_entry.add(participant) if count > 0: messages.success( request, - "Tu as bien été inscrit à {} revente{}" - .format(count, pluralize(count)) + "Tu as bien été inscrit à {} revente{}".format( + count, pluralize(count) + ), ) elif "annul" in request.POST: - annulform = ReventeTirageAnnulForm(participant, request.POST, - prefix="annul") + annulform = ReventeTirageAnnulForm( + participant, request.POST, prefix="annul" + ) if annulform.is_valid(): - reventes = annulform.cleaned_data['reventes'] + reventes = annulform.cleaned_data["reventes"] count = reventes.count() for revente in reventes: revente.confirmed_entry.remove(participant) if count > 0: messages.success( request, - "Tu as bien été désinscrit de {} revente{}" - .format(count, pluralize(count)) + "Tu as bien été désinscrit de {} revente{}".format( + count, pluralize(count) + ), ) - return render(request, "bda/revente/tirages.html", - {"annulform": annulform, "subform": subform}) + return render( + request, + "bda/revente/tirages.html", + {"annulform": annulform, "subform": subform}, + ) @login_required def revente_confirm(request, revente_id): revente = get_object_or_404(SpectacleRevente, id=revente_id) participant, _ = Participant.objects.get_or_create( - user=request.user, tirage=revente.attribution.spectacle.tirage) + user=request.user, tirage=revente.attribution.spectacle.tirage + ) if not revente.notif_sent or revente.shotgun: - return render(request, "bda/revente/wrongtime.html", - {"revente": revente}) + return render(request, "bda/revente/wrongtime.html", {"revente": revente}) revente.confirmed_entry.add(participant) - return render(request, "bda/revente/confirmed.html", - {"spectacle": revente.attribution.spectacle, - "date": revente.date_tirage}) + return render( + request, + "bda/revente/confirmed.html", + {"spectacle": revente.attribution.spectacle, "date": revente.date_tirage}, + ) @login_required @@ -511,20 +541,18 @@ def revente_subscribe(request, tirage_id): spectacle à la liste des spectacles qui l'intéressent. """ tirage = get_object_or_404(Tirage, id=tirage_id) - participant, _ = Participant.objects.get_or_create( - user=request.user, tirage=tirage) + participant, _ = Participant.objects.get_or_create(user=request.user, tirage=tirage) deja_revente = False success = False inscrit_revente = [] - if request.method == 'POST': + if request.method == "POST": form = InscriptionReventeForm(tirage, request.POST) if form.is_valid(): - choices = form.cleaned_data['spectacles'] + choices = form.cleaned_data["spectacles"] participant.choicesrevente = choices participant.save() for spectacle in choices: - qset = SpectacleRevente.objects.filter( - attribution__spectacle=spectacle) + qset = SpectacleRevente.objects.filter(attribution__spectacle=spectacle) if qset.filter(shotgun=True, soldTo__isnull=True).exists(): # Une place est disponible au shotgun, on suggère à # l'utilisateur d'aller la récupérer @@ -535,8 +563,8 @@ def revente_subscribe(request, tirage_id): # la revente ayant le moins d'inscrits min_resell = ( qset.filter(shotgun=False) - .annotate(nb_subscribers=Count('confirmed_entry')) - .order_by('nb_subscribers') + .annotate(nb_subscribers=Count("confirmed_entry")) + .order_by("nb_subscribers") .first() ) if min_resell is not None: @@ -545,21 +573,23 @@ def revente_subscribe(request, tirage_id): success = True else: form = InscriptionReventeForm( - tirage, - initial={'spectacles': participant.choicesrevente.all()} + tirage, initial={"spectacles": participant.choicesrevente.all()} ) # Messages if success: messages.success(request, "Ton inscription a bien été prise en compte") if deja_revente: - messages.info(request, "Des reventes existent déjà pour certains de " - "ces spectacles, vérifie les places " - "disponibles sans tirage !") + messages.info( + request, + "Des reventes existent déjà pour certains de " + "ces spectacles, vérifie les places " + "disponibles sans tirage !", + ) if inscrit_revente: shows = map("
  • {!s}
  • ".format, inscrit_revente) msg = ( "Tu as été inscrit à des reventes en cours pour les spectacles " - "
      {:s}
    ".format('\n'.join(shows)) + "
      {:s}
    ".format("\n".join(shows)) ) messages.info(request, msg, extra_tags="safe") @@ -570,19 +600,17 @@ def revente_subscribe(request, tirage_id): def revente_buy(request, spectacle_id): spectacle = get_object_or_404(Spectacle, id=spectacle_id) tirage = spectacle.tirage - participant, _ = Participant.objects.get_or_create( - user=request.user, tirage=tirage) + participant, _ = Participant.objects.get_or_create(user=request.user, tirage=tirage) reventes = SpectacleRevente.objects.filter( - attribution__spectacle=spectacle, - soldTo__isnull=True) + attribution__spectacle=spectacle, soldTo__isnull=True + ) # Si l'utilisateur veut racheter une place qu'il est en train de revendre, # on supprime la revente en question. own_reventes = reventes.filter(seller=participant) if len(own_reventes) > 0: own_reventes[0].delete() - return HttpResponseRedirect(reverse("bda-revente-shotgun", - args=[tirage.id])) + return HttpResponseRedirect(reverse("bda-revente-shotgun", args=[tirage.id])) reventes_shotgun = reventes.filter(shotgun=True) @@ -594,94 +622,98 @@ def revente_buy(request, spectacle_id): revente.soldTo = participant revente.save() context = { - 'show': spectacle, - 'acheteur': request.user, - 'vendeur': revente.seller.user + "show": spectacle, + "acheteur": request.user, + "vendeur": revente.seller.user, } send_custom_mail( - 'bda-buy-shotgun', - 'bda@ens.fr', + "bda-buy-shotgun", + "bda@ens.fr", [revente.seller.user.email], context=context, ) - return render(request, "bda/revente/mail-success.html", - {"seller": revente.attribution.participant.user, - "spectacle": spectacle}) + return render( + request, + "bda/revente/mail-success.html", + {"seller": revente.attribution.participant.user, "spectacle": spectacle}, + ) - return render(request, "bda/revente/confirm-shotgun.html", - {"spectacle": spectacle, - "user": request.user}) + return render( + request, + "bda/revente/confirm-shotgun.html", + {"spectacle": spectacle, "user": request.user}, + ) @login_required def revente_shotgun(request, tirage_id): tirage = get_object_or_404(Tirage, id=tirage_id) spectacles = ( - tirage.spectacle_set - .filter(date__gte=timezone.now()) - .select_related('location') - .prefetch_related(Prefetch( - 'attribues', - queryset=( - Attribution.objects - .filter(revente__shotgun=True, - revente__soldTo__isnull=True) - ), - to_attr='shotguns', - )) + tirage.spectacle_set.filter(date__gte=timezone.now()) + .select_related("location") + .prefetch_related( + Prefetch( + "attribues", + queryset=( + Attribution.objects.filter( + revente__shotgun=True, revente__soldTo__isnull=True + ) + ), + to_attr="shotguns", + ) + ) ) shotgun = [sp for sp in spectacles if len(sp.shotguns) > 0] - return render(request, "bda/revente/shotgun.html", - {"shotgun": shotgun}) + return render(request, "bda/revente/shotgun.html", {"shotgun": shotgun}) @buro_required def spectacle(request, tirage_id, spectacle_id): tirage = get_object_or_404(Tirage, id=tirage_id) spectacle = get_object_or_404(Spectacle, id=spectacle_id, tirage=tirage) - attributions = ( - spectacle.attribues - .select_related('participant', 'participant__user') + attributions = spectacle.attribues.select_related( + "participant", "participant__user" ) participants = {} for attrib in attributions: participant = attrib.participant - participant_info = {'lastname': participant.user.last_name, - 'name': participant.user.get_full_name, - 'username': participant.user.username, - 'email': participant.user.email, - 'given': int(attrib.given), - 'paid': participant.paid, - 'nb_places': 1} + participant_info = { + "lastname": participant.user.last_name, + "name": participant.user.get_full_name, + "username": participant.user.username, + "email": participant.user.email, + "given": int(attrib.given), + "paid": participant.paid, + "nb_places": 1, + } if participant.id in participants: - participants[participant.id]['nb_places'] += 1 - participants[participant.id]['given'] += attrib.given + participants[participant.id]["nb_places"] += 1 + participants[participant.id]["given"] += attrib.given else: participants[participant.id] = participant_info - participants_info = sorted(participants.values(), - key=lambda part: part['lastname']) - return render(request, "bda/participants.html", - {"spectacle": spectacle, "participants": participants_info}) + participants_info = sorted(participants.values(), key=lambda part: part["lastname"]) + return render( + request, + "bda/participants.html", + {"spectacle": spectacle, "participants": participants_info}, + ) class SpectacleListView(ListView): model = Spectacle - template_name = 'spectacle_list.html' + template_name = "spectacle_list.html" def get_queryset(self): - self.tirage = get_object_or_404(Tirage, id=self.kwargs['tirage_id']) - categories = ( - self.tirage.spectacle_set - .select_related('location') - ) + self.tirage = get_object_or_404(Tirage, id=self.kwargs["tirage_id"]) + categories = self.tirage.spectacle_set.select_related("location") return categories def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['tirage_id'] = self.tirage.id - context['tirage_name'] = self.tirage.title + context["tirage_id"] = self.tirage.id + context["tirage_name"] = self.tirage.title return context @@ -689,10 +721,9 @@ class SpectacleListView(ListView): def unpaid(request, tirage_id): tirage = get_object_or_404(Tirage, id=tirage_id) unpaid = ( - tirage.participant_set - .annotate(nb_attributions=Count('attribution')) + tirage.participant_set.annotate(nb_attributions=Count("attribution")) .filter(paid=False, nb_attributions__gt=0) - .select_related('user') + .select_related("user") ) return render(request, "bda-unpaid.html", {"unpaid": unpaid}) @@ -702,51 +733,45 @@ def send_rappel(request, spectacle_id): show = get_object_or_404(Spectacle, id=spectacle_id) # Mails d'exemples custommail = CustomMail.objects.get(shortname="bda-rappel") - exemple_mail_1place = custommail.render({ - 'member': request.user, - 'show': show, - 'nb_attr': 1 - }) - exemple_mail_2places = custommail.render({ - 'member': request.user, - 'show': show, - 'nb_attr': 2 - }) + exemple_mail_1place = custommail.render( + {"member": request.user, "show": show, "nb_attr": 1} + ) + exemple_mail_2places = custommail.render( + {"member": request.user, "show": show, "nb_attr": 2} + ) # Contexte ctxt = { - 'show': show, - 'exemple_mail_1place': exemple_mail_1place, - 'exemple_mail_2places': exemple_mail_2places, - 'custommail': custommail, + "show": show, + "exemple_mail_1place": exemple_mail_1place, + "exemple_mail_2places": exemple_mail_2places, + "custommail": custommail, } # Envoi confirmé - if request.method == 'POST': + if request.method == "POST": members = show.send_rappel() - ctxt['sent'] = True - ctxt['members'] = members + ctxt["sent"] = True + ctxt["members"] = members # Demande de confirmation else: - ctxt['sent'] = False + ctxt["sent"] = False if show.rappel_sent: messages.warning( request, "Attention, un mail de rappel pour ce spectale a déjà été " - "envoyé le {}".format(formats.localize( - timezone.template_localtime(show.rappel_sent) - )) + "envoyé le {}".format( + formats.localize(timezone.template_localtime(show.rappel_sent)) + ), ) return render(request, "bda/mails-rappel.html", ctxt) def descriptions_spectacles(request, tirage_id): tirage = get_object_or_404(Tirage, id=tirage_id) - shows_qs = ( - tirage.spectacle_set - .select_related('location') - .prefetch_related('quote_set') + shows_qs = tirage.spectacle_set.select_related("location").prefetch_related( + "quote_set" ) - category_name = request.GET.get('category', '') - location_id = request.GET.get('location', '') + category_name = request.GET.get("category", "") + location_id = request.GET.get("location", "") if category_name: shows_qs = shows_qs.filter(category__name=category_name) if location_id: @@ -754,8 +779,9 @@ def descriptions_spectacles(request, tirage_id): shows_qs = shows_qs.filter(location__id=int(location_id)) except ValueError: return HttpResponseBadRequest( - "La variable GET 'location' doit contenir un entier") - return render(request, 'descriptions.html', {'shows': shows_qs}) + "La variable GET 'location' doit contenir un entier" + ) + return render(request, "descriptions.html", {"shows": shows_qs}) def catalogue(request, request_type): @@ -768,44 +794,36 @@ def catalogue(request, request_type): if request_type == "list": # Dans ce cas on retourne la liste des tirages et de leur id en JSON data_return = list( - Tirage.objects.filter(appear_catalogue=True).values('id', 'title') + Tirage.objects.filter(appear_catalogue=True).values("id", "title") ) return JsonResponse(data_return, safe=False) if request_type == "details": # Dans ce cas on retourne une liste des catégories et des salles - tirage_id = request.GET.get('id', None) + tirage_id = request.GET.get("id", None) if tirage_id is None: - return HttpResponseBadRequest( - "Missing GET parameter: id " - ) + return HttpResponseBadRequest("Missing GET parameter: id ") try: tirage = get_object_or_404(Tirage, id=int(tirage_id)) except ValueError: - return HttpResponseBadRequest( - "Bad format: int expected for `id`" - ) + return HttpResponseBadRequest("Bad format: int expected for `id`") shows = tirage.spectacle_set.values_list("id", flat=True) categories = list( - CategorieSpectacle.objects - .filter(spectacle__in=shows) + CategorieSpectacle.objects.filter(spectacle__in=shows) .distinct() - .values('id', 'name') + .values("id", "name") ) locations = list( - Salle.objects - .filter(spectacle__in=shows) - .distinct() - .values('id', 'name') + Salle.objects.filter(spectacle__in=shows).distinct().values("id", "name") ) - data_return = {'categories': categories, 'locations': locations} + data_return = {"categories": categories, "locations": locations} return JsonResponse(data_return, safe=False) if request_type == "descriptions": # Ici on retourne les descriptions correspondant à la catégorie et # à la salle spécifiées - tirage_id = request.GET.get('id', '') - categories = request.GET.get('category', '[]') - locations = request.GET.get('location', '[]') + tirage_id = request.GET.get("id", "") + categories = request.GET.get("category", "[]") + locations = request.GET.get("location", "[]") try: tirage_id = int(tirage_id) categories_id = json.loads(categories) @@ -821,17 +839,16 @@ def catalogue(request, request_type): "following types:\n" "id: int, category: [int], location: [int]\n" "Data received:\n" - "id = {}, category = {}, locations = {}" - .format(request.GET.get('id', ''), - request.GET.get('category', '[]'), - request.GET.get('location', '[]')) + "id = {}, category = {}, locations = {}".format( + request.GET.get("id", ""), + request.GET.get("category", "[]"), + request.GET.get("location", "[]"), + ) ) tirage = get_object_or_404(Tirage, id=tirage_id) - shows_qs = ( - tirage.spectacle_set - .select_related('location') - .prefetch_related('quote_set') + shows_qs = tirage.spectacle_set.select_related("location").prefetch_related( + "quote_set" ) if categories_id and 0 not in categories_id: shows_qs = shows_qs.filter(category__id__in=categories_id) @@ -841,23 +858,27 @@ def catalogue(request, request_type): # On convertit les descriptions à envoyer en une liste facilement # JSONifiable (il devrait y avoir un moyen plus efficace en # redéfinissant le serializer de JSON) - data_return = [{ - 'title': spectacle.title, - 'category': str(spectacle.category), - 'date': str(formats.date_format( - timezone.localtime(spectacle.date), - "SHORT_DATETIME_FORMAT")), - 'location': str(spectacle.location), - 'vips': spectacle.vips, - 'description': spectacle.description, - 'slots_description': spectacle.slots_description, - 'quotes': [dict(author=quote.author, - text=quote.text) - for quote in spectacle.quote_set.all()], - 'image': spectacle.getImgUrl(), - 'ext_link': spectacle.ext_link, - 'price': spectacle.price, - 'slots': spectacle.slots + data_return = [ + { + "title": spectacle.title, + "category": str(spectacle.category), + "date": str( + formats.date_format( + timezone.localtime(spectacle.date), "SHORT_DATETIME_FORMAT" + ) + ), + "location": str(spectacle.location), + "vips": spectacle.vips, + "description": spectacle.description, + "slots_description": spectacle.slots_description, + "quotes": [ + dict(author=quote.author, text=quote.text) + for quote in spectacle.quote_set.all() + ], + "image": spectacle.getImgUrl(), + "ext_link": spectacle.ext_link, + "price": spectacle.price, + "slots": spectacle.slots, } for spectacle in shows_qs ] @@ -875,7 +896,7 @@ def catalogue(request, request_type): class ParticipantAutocomplete(Select2QuerySetView): model = Participant - search_fields = ('user__username', 'user__first_name', 'user__last_name') + search_fields = ("user__username", "user__first_name", "user__last_name") participant_autocomplete = buro_required(ParticipantAutocomplete.as_view()) @@ -883,7 +904,7 @@ participant_autocomplete = buro_required(ParticipantAutocomplete.as_view()) class SpectacleAutocomplete(Select2QuerySetView): model = Spectacle - search_fields = ('title',) + search_fields = ("title",) spectacle_autocomplete = buro_required(SpectacleAutocomplete.as_view()) diff --git a/cof/asgi.py b/cof/asgi.py index a34621c7..ab4ce291 100644 --- a/cof/asgi.py +++ b/cof/asgi.py @@ -1,4 +1,5 @@ import os + from channels.asgi import get_channel_layer if "DJANGO_SETTINGS_MODULE" not in os.environ: diff --git a/cof/locale/fr/formats.py b/cof/locale/fr/formats.py index 4b47ce3d..3120d5ce 100644 --- a/cof/locale/fr/formats.py +++ b/cof/locale/fr/formats.py @@ -2,4 +2,4 @@ Formats français. """ -DATETIME_FORMAT = r'l j F Y \à H:i' +DATETIME_FORMAT = r"l j F Y \à H:i" diff --git a/cof/routing.py b/cof/routing.py index c604adf4..3c2e5718 100644 --- a/cof/routing.py +++ b/cof/routing.py @@ -1,6 +1,3 @@ from channels.routing import include - -routing = [ - include('kfet.routing.routing', path=r'^/ws/k-fet'), -] +routing = [include("kfet.routing.routing", path=r"^/ws/k-fet")] diff --git a/cof/settings/common.py b/cof/settings/common.py index 02c796ad..ebc7fb2a 100644 --- a/cof/settings/common.py +++ b/cof/settings/common.py @@ -46,112 +46,106 @@ KFETOPEN_TOKEN = import_secret("KFETOPEN_TOKEN") LDAP_SERVER_URL = import_secret("LDAP_SERVER_URL") -BASE_DIR = os.path.dirname( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -TESTING = sys.argv[1] == 'test' +TESTING = sys.argv[1] == "test" # Application definition INSTALLED_APPS = [ - 'shared', - - 'gestioncof', - + "shared", + "gestioncof", # Must be before 'django.contrib.admin'. # https://django-autocomplete-light.readthedocs.io/en/master/install.html - 'dal', - 'dal_select2', - - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.admin', - 'django.contrib.admindocs', - - 'bda', - 'captcha', - 'django_cas_ng', - 'bootstrapform', - 'kfet', - 'kfet.open', - 'channels', - 'widget_tweaks', - 'custommail', - 'djconfig', - 'wagtail.wagtailforms', - 'wagtail.wagtailredirects', - 'wagtail.wagtailembeds', - 'wagtail.wagtailsites', - 'wagtail.wagtailusers', - 'wagtail.wagtailsnippets', - 'wagtail.wagtaildocs', - 'wagtail.wagtailimages', - 'wagtail.wagtailsearch', - 'wagtail.wagtailadmin', - 'wagtail.wagtailcore', - 'wagtail.contrib.modeladmin', - 'wagtailmenus', - 'modelcluster', - 'taggit', - 'kfet.auth', - 'kfet.cms', - 'corsheaders', + "dal", + "dal_select2", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.sites", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.admin", + "django.contrib.admindocs", + "bda", + "captcha", + "django_cas_ng", + "bootstrapform", + "kfet", + "kfet.open", + "channels", + "widget_tweaks", + "custommail", + "djconfig", + "wagtail.wagtailforms", + "wagtail.wagtailredirects", + "wagtail.wagtailembeds", + "wagtail.wagtailsites", + "wagtail.wagtailusers", + "wagtail.wagtailsnippets", + "wagtail.wagtaildocs", + "wagtail.wagtailimages", + "wagtail.wagtailsearch", + "wagtail.wagtailadmin", + "wagtail.wagtailcore", + "wagtail.contrib.modeladmin", + "wagtailmenus", + "modelcluster", + "taggit", + "kfet.auth", + "kfet.cms", + "corsheaders", ] MIDDLEWARE = [ - 'corsheaders.middleware.CorsMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', - 'kfet.auth.middleware.TemporaryAuthMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.security.SecurityMiddleware', - 'djconfig.middleware.DjConfigMiddleware', - 'wagtail.wagtailcore.middleware.SiteMiddleware', - 'wagtail.wagtailredirects.middleware.RedirectMiddleware', + "corsheaders.middleware.CorsMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.auth.middleware.SessionAuthenticationMiddleware", + "kfet.auth.middleware.TemporaryAuthMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django.middleware.security.SecurityMiddleware", + "djconfig.middleware.DjConfigMiddleware", + "wagtail.wagtailcore.middleware.SiteMiddleware", + "wagtail.wagtailredirects.middleware.RedirectMiddleware", ] -ROOT_URLCONF = 'cof.urls' +ROOT_URLCONF = "cof.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'wagtailmenus.context_processors.wagtailmenus', - 'djconfig.context_processors.config', - 'gestioncof.shared.context_processor', - 'kfet.auth.context_processors.temporary_auth', - 'kfet.context_processors.config', - ], + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "wagtailmenus.context_processors.wagtailmenus", + "djconfig.context_processors.config", + "gestioncof.shared.context_processor", + "kfet.auth.context_processors.temporary_auth", + "kfet.context_processors.config", + ] }, - }, + } ] DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': DBNAME, - 'USER': DBUSER, - 'PASSWORD': DBPASSWD, - 'HOST': os.environ.get('DBHOST', 'localhost'), + "default": { + "ENGINE": "django.db.backends.postgresql_psycopg2", + "NAME": DBNAME, + "USER": DBUSER, + "PASSWORD": DBPASSWD, + "HOST": os.environ.get("DBHOST", "localhost"), } } @@ -159,9 +153,9 @@ DATABASES = { # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ -LANGUAGE_CODE = 'fr-fr' +LANGUAGE_CODE = "fr-fr" -TIME_ZONE = 'Europe/Paris' +TIME_ZONE = "Europe/Paris" USE_I18N = True @@ -173,35 +167,35 @@ USE_TZ = True SITE_ID = 1 GRAPPELLI_ADMIN_HEADLINE = "GestioCOF" -GRAPPELLI_ADMIN_TITLE = "GestioCOF" +GRAPPELLI_ADMIN_TITLE = 'GestioCOF' MAIL_DATA = { - 'petits_cours': { - 'FROM': "Le COF ", - 'BCC': "archivescof@gmail.com", - 'REPLYTO': "cof@ens.fr"}, - 'rappels': { - 'FROM': 'Le BdA ', - 'REPLYTO': 'Le BdA '}, - 'revente': { - 'FROM': 'BdA-Revente ', - 'REPLYTO': 'BdA-Revente '}, + "petits_cours": { + "FROM": "Le COF ", + "BCC": "archivescof@gmail.com", + "REPLYTO": "cof@ens.fr", + }, + "rappels": {"FROM": "Le BdA ", "REPLYTO": "Le BdA "}, + "revente": { + "FROM": "BdA-Revente ", + "REPLYTO": "BdA-Revente ", + }, } LOGIN_URL = "cof-login" LOGIN_REDIRECT_URL = "home" -CAS_SERVER_URL = 'https://cas.eleves.ens.fr/' -CAS_VERSION = '3' +CAS_SERVER_URL = "https://cas.eleves.ens.fr/" +CAS_VERSION = "3" CAS_LOGIN_MSG = None CAS_IGNORE_REFERER = True -CAS_REDIRECT_URL = '/' +CAS_REDIRECT_URL = "/" CAS_EMAIL_FORMAT = "%s@clipper.ens.fr" AUTHENTICATION_BACKENDS = ( - 'django.contrib.auth.backends.ModelBackend', - 'gestioncof.shared.COFCASBackend', - 'kfet.auth.backends.GenericBackend', + "django.contrib.auth.backends.ModelBackend", + "gestioncof.shared.COFCASBackend", + "kfet.auth.backends.GenericBackend", ) @@ -214,21 +208,16 @@ AUTHENTICATION_BACKENDS = ( NOCAPTCHA = True RECAPTCHA_USE_SSL = True -CORS_ORIGIN_WHITELIST = ( - 'bda.ens.fr', - 'www.bda.ens.fr' - 'cof.ens.fr', - 'www.cof.ens.fr', -) +CORS_ORIGIN_WHITELIST = ("bda.ens.fr", "www.bda.ens.fr" "cof.ens.fr", "www.cof.ens.fr") # Cache settings CACHES = { - 'default': { - 'BACKEND': 'redis_cache.RedisCache', - 'LOCATION': 'redis://:{passwd}@{host}:{port}/db' - .format(passwd=REDIS_PASSWD, host=REDIS_HOST, - port=REDIS_PORT, db=REDIS_DB), + "default": { + "BACKEND": "redis_cache.RedisCache", + "LOCATION": "redis://:{passwd}@{host}:{port}/db".format( + passwd=REDIS_PASSWD, host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB + ), } } @@ -239,20 +228,25 @@ CHANNEL_LAYERS = { "default": { "BACKEND": "asgi_redis.RedisChannelLayer", "CONFIG": { - "hosts": [( - "redis://:{passwd}@{host}:{port}/{db}" - .format(passwd=REDIS_PASSWD, host=REDIS_HOST, - port=REDIS_PORT, db=REDIS_DB) - )], + "hosts": [ + ( + "redis://:{passwd}@{host}:{port}/{db}".format( + passwd=REDIS_PASSWD, + host=REDIS_HOST, + port=REDIS_PORT, + db=REDIS_DB, + ) + ) + ] }, "ROUTING": "cof.routing.routing", } } -FORMAT_MODULE_PATH = 'cof.locale' +FORMAT_MODULE_PATH = "cof.locale" # Wagtail settings -WAGTAIL_SITE_NAME = 'GestioCOF' +WAGTAIL_SITE_NAME = "GestioCOF" WAGTAIL_ENABLE_UPDATE_CHECK = False TAGGIT_CASE_INSENSITIVE = True diff --git a/cof/settings/dev.py b/cof/settings/dev.py index 114f37da..0fd367cd 100644 --- a/cof/settings/dev.py +++ b/cof/settings/dev.py @@ -6,32 +6,30 @@ The settings that are not listed here are imported from .common from .common import * # NOQA from .common import INSTALLED_APPS, MIDDLEWARE, TESTING - -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" DEBUG = True if TESTING: - PASSWORD_HASHERS = [ - 'django.contrib.auth.hashers.MD5PasswordHasher', - ] + PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] # --- # Apache static/media config # --- -STATIC_URL = '/static/' -STATIC_ROOT = '/srv/gestiocof/static/' +STATIC_URL = "/static/" +STATIC_ROOT = "/srv/gestiocof/static/" -MEDIA_ROOT = '/srv/gestiocof/media/' -MEDIA_URL = '/media/' +MEDIA_ROOT = "/srv/gestiocof/media/" +MEDIA_URL = "/media/" # --- # Debug tool bar # --- + def show_toolbar(request): """ On ne veut pas la vérification de INTERNAL_IPS faite par la debug-toolbar @@ -41,13 +39,10 @@ def show_toolbar(request): """ return DEBUG + if not TESTING: INSTALLED_APPS += ["debug_toolbar", "debug_panel"] - MIDDLEWARE = [ - "debug_panel.middleware.DebugPanelMiddleware" - ] + MIDDLEWARE + MIDDLEWARE = ["debug_panel.middleware.DebugPanelMiddleware"] + MIDDLEWARE - DEBUG_TOOLBAR_CONFIG = { - 'SHOW_TOOLBAR_CALLBACK': show_toolbar, - } + DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": show_toolbar} diff --git a/cof/settings/local.py b/cof/settings/local.py index 6e1f0802..06cdf4a0 100644 --- a/cof/settings/local.py +++ b/cof/settings/local.py @@ -8,21 +8,16 @@ import os from .dev import * # NOQA from .dev import BASE_DIR - # Use sqlite for local development DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", - "NAME": os.path.join(BASE_DIR, "db.sqlite3") + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), } } # Use the default cache backend for local development -CACHES = { - "default": { - "BACKEND": "django.core.cache.backends.locmem.LocMemCache" - } -} +CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}} # Use the default in memory asgi backend for local development CHANNEL_LAYERS = { diff --git a/cof/settings/prod.py b/cof/settings/prod.py index fcdb3fdb..9e355ccb 100644 --- a/cof/settings/prod.py +++ b/cof/settings/prod.py @@ -8,21 +8,13 @@ import os from .common import * # NOQA from .common import BASE_DIR, import_secret - DEBUG = False -ALLOWED_HOSTS = [ - "cof.ens.fr", - "www.cof.ens.fr", - "dev.cof.ens.fr" -] +ALLOWED_HOSTS = ["cof.ens.fr", "www.cof.ens.fr", "dev.cof.ens.fr"] STATIC_ROOT = os.path.join( - os.path.dirname(os.path.dirname(BASE_DIR)), - "public", - "gestion", - "static", + os.path.dirname(os.path.dirname(BASE_DIR)), "public", "gestion", "static" ) STATIC_URL = "/gestion/static/" diff --git a/cof/settings/secret_example.py b/cof/settings/secret_example.py index e966565a..7921d467 100644 --- a/cof/settings/secret_example.py +++ b/cof/settings/secret_example.py @@ -1,4 +1,4 @@ -SECRET_KEY = 'q()(zn4m63i%5cp4)f+ww4-28_w+ly3q9=6imw2ciu&_(5_4ah' +SECRET_KEY = "q()(zn4m63i%5cp4)f+ww4-28_w+ly3q9=6imw2ciu&_(5_4ah" ADMINS = None SERVER_EMAIL = "root@vagrant" EMAIL_HOST = "localhost" diff --git a/cof/urls.py b/cof/urls.py index a1d0c9bf..7a0bee4c 100644 --- a/cof/urls.py +++ b/cof/urls.py @@ -6,111 +6,130 @@ from django.conf import settings from django.conf.urls import include, url from django.conf.urls.static import static from django.contrib import admin -from django.views.generic.base import TemplateView from django.contrib.auth import views as django_views +from django.views.generic.base import TemplateView from django_cas_ng import views as django_cas_views - from wagtail.wagtailadmin import urls as wagtailadmin_urls from wagtail.wagtailcore import urls as wagtail_urls from wagtail.wagtaildocs import urls as wagtaildocs_urls -from gestioncof import views as gestioncof_views, csv_views -from gestioncof.urls import export_patterns, petitcours_patterns, \ - surveys_patterns, events_patterns, calendar_patterns, \ - clubs_patterns +from gestioncof import csv_views, views as gestioncof_views from gestioncof.autocomplete import autocomplete +from gestioncof.urls import ( + calendar_patterns, + clubs_patterns, + events_patterns, + export_patterns, + petitcours_patterns, + surveys_patterns, +) admin.autodiscover() urlpatterns = [ # Page d'accueil - url(r'^$', gestioncof_views.home, name='home'), + url(r"^$", gestioncof_views.home, name="home"), # Le BdA - url(r'^bda/', include('bda.urls')), + url(r"^bda/", include("bda.urls")), # Les exports - url(r'^export/', include(export_patterns)), + url(r"^export/", include(export_patterns)), # Les petits cours - url(r'^petitcours/', include(petitcours_patterns)), + url(r"^petitcours/", include(petitcours_patterns)), # Les sondages - url(r'^survey/', include(surveys_patterns)), + url(r"^survey/", include(surveys_patterns)), # Evenements - url(r'^event/', include(events_patterns)), + url(r"^event/", include(events_patterns)), # Calendrier - url(r'^calendar/', include(calendar_patterns)), + url(r"^calendar/", include(calendar_patterns)), # Clubs - url(r'^clubs/', include(clubs_patterns)), + url(r"^clubs/", include(clubs_patterns)), # Authentification - url(r'^cof/denied$', TemplateView.as_view(template_name='cof-denied.html'), - name="cof-denied"), - url(r'^cas/login$', django_cas_views.login, name="cas_login_view"), - url(r'^cas/logout$', django_cas_views.logout), - url(r'^outsider/login$', gestioncof_views.login_ext, - name="ext_login_view"), - url(r'^outsider/logout$', django_views.logout, {'next_page': 'home'}), - url(r'^login$', gestioncof_views.login, name="cof-login"), - url(r'^logout$', gestioncof_views.logout, name="cof-logout"), + url( + r"^cof/denied$", + TemplateView.as_view(template_name="cof-denied.html"), + name="cof-denied", + ), + url(r"^cas/login$", django_cas_views.login, name="cas_login_view"), + url(r"^cas/logout$", django_cas_views.logout), + url(r"^outsider/login$", gestioncof_views.login_ext, name="ext_login_view"), + url(r"^outsider/logout$", django_views.logout, {"next_page": "home"}), + url(r"^login$", gestioncof_views.login, name="cof-login"), + url(r"^logout$", gestioncof_views.logout, name="cof-logout"), # Infos persos - url(r'^profile$', gestioncof_views.profile, - name='profile'), - url(r'^outsider/password-change$', django_views.password_change, - name='password_change'), - url(r'^outsider/password-change-done$', + url(r"^profile$", gestioncof_views.profile, name="profile"), + url( + r"^outsider/password-change$", + django_views.password_change, + name="password_change", + ), + url( + r"^outsider/password-change-done$", django_views.password_change_done, - name='password_change_done'), + name="password_change_done", + ), # Inscription d'un nouveau membre - url(r'^registration$', gestioncof_views.registration, - name='registration'), - url(r'^registration/clipper/(?P[\w-]+)/' - r'(?P.*)$', - gestioncof_views.registration_form2, name="clipper-registration"), - url(r'^registration/user/(?P.+)$', - gestioncof_views.registration_form2, name="user-registration"), - url(r'^registration/empty$', gestioncof_views.registration_form2, - name="empty-registration"), + url(r"^registration$", gestioncof_views.registration, name="registration"), + url( + r"^registration/clipper/(?P[\w-]+)/" r"(?P.*)$", + gestioncof_views.registration_form2, + name="clipper-registration", + ), + url( + r"^registration/user/(?P.+)$", + gestioncof_views.registration_form2, + name="user-registration", + ), + url( + r"^registration/empty$", + gestioncof_views.registration_form2, + name="empty-registration", + ), # Autocompletion - url(r'^autocomplete/registration$', autocomplete, - name="cof.registration.autocomplete"), - url(r'^user/autocomplete$', gestioncof_views.user_autocomplete, - name='cof-user-autocomplete'), + url( + r"^autocomplete/registration$", + autocomplete, + name="cof.registration.autocomplete", + ), + url( + r"^user/autocomplete$", + gestioncof_views.user_autocomplete, + name="cof-user-autocomplete", + ), # Interface admin - url(r'^admin/logout/', gestioncof_views.logout), - url(r'^admin/doc/', include('django.contrib.admindocs.urls')), - url(r'^admin/(?P[\d\w]+)/(?P[\d\w]+)/csv/', + url(r"^admin/logout/", gestioncof_views.logout), + url(r"^admin/doc/", include("django.contrib.admindocs.urls")), + url( + r"^admin/(?P[\d\w]+)/(?P[\d\w]+)/csv/", csv_views.admin_list_export, - {'fields': ['username', ]}), - url(r'^admin/', include(admin.site.urls)), + {"fields": ["username"]}, + ), + url(r"^admin/", include(admin.site.urls)), # Liens utiles du COF et du BdA - url(r'^utile_cof$', gestioncof_views.utile_cof, - name='utile_cof'), - url(r'^utile_bda$', gestioncof_views.utile_bda, - name='utile_bda'), - url(r'^utile_bda/bda_diff$', gestioncof_views.liste_bdadiff, - name="ml_diffbda"), - url(r'^utile_cof/diff_cof$', gestioncof_views.liste_diffcof, - name='ml_diffcof'), - url(r'^utile_bda/bda_revente$', gestioncof_views.liste_bdarevente, - name="ml_bda_revente"), - url(r'^k-fet/', include('kfet.urls')), - url(r'^cms/', include(wagtailadmin_urls)), - url(r'^documents/', include(wagtaildocs_urls)), + url(r"^utile_cof$", gestioncof_views.utile_cof, name="utile_cof"), + url(r"^utile_bda$", gestioncof_views.utile_bda, name="utile_bda"), + url(r"^utile_bda/bda_diff$", gestioncof_views.liste_bdadiff, name="ml_diffbda"), + url(r"^utile_cof/diff_cof$", gestioncof_views.liste_diffcof, name="ml_diffcof"), + url( + r"^utile_bda/bda_revente$", + gestioncof_views.liste_bdarevente, + name="ml_bda_revente", + ), + url(r"^k-fet/", include("kfet.urls")), + url(r"^cms/", include(wagtailadmin_urls)), + url(r"^documents/", include(wagtaildocs_urls)), # djconfig - url(r"^config", gestioncof_views.ConfigUpdate.as_view(), - name='config.edit'), + url(r"^config", gestioncof_views.ConfigUpdate.as_view(), name="config.edit"), ] -if 'debug_toolbar' in settings.INSTALLED_APPS: +if "debug_toolbar" in settings.INSTALLED_APPS: import debug_toolbar - urlpatterns += [ - url(r'^__debug__/', include(debug_toolbar.urls)), - ] + + urlpatterns += [url(r"^__debug__/", include(debug_toolbar.urls))] if settings.DEBUG: # Si on est en production, MEDIA_ROOT est servi par Apache. # Il faut dire à Django de servir MEDIA_ROOT lui-même en développement. - urlpatterns += static(settings.MEDIA_URL, - document_root=settings.MEDIA_ROOT) + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # Wagtail for uncatched -urlpatterns += [ - url(r'', include(wagtail_urls)), -] +urlpatterns += [url(r"", include(wagtail_urls))] diff --git a/gestioncof/__init__.py b/gestioncof/__init__.py index b77fdb94..3bb260b9 100644 --- a/gestioncof/__init__.py +++ b/gestioncof/__init__.py @@ -1 +1 @@ -default_app_config = 'gestioncof.apps.GestioncofConfig' +default_app_config = "gestioncof.apps.GestioncofConfig" diff --git a/gestioncof/admin.py b/gestioncof/admin.py index 54a6a5a0..e89d4271 100644 --- a/gestioncof/admin.py +++ b/gestioncof/admin.py @@ -1,23 +1,35 @@ +from dal.autocomplete import ModelSelect2 from django import forms from django.contrib import admin -from django.utils.translation import ugettext_lazy as _ -from gestioncof.models import SurveyQuestionAnswer, SurveyQuestion, \ - CofProfile, EventOption, EventOptionChoice, Event, Club, \ - Survey, EventCommentField, EventRegistration -from gestioncof.petits_cours_models import PetitCoursDemande, \ - PetitCoursSubject, PetitCoursAbility, PetitCoursAttribution, \ - PetitCoursAttributionCounter -from django.contrib.auth.models import User, Group, Permission from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.models import Group, Permission, User from django.core.urlresolvers import reverse -from django.utils.safestring import mark_safe from django.db.models import Q +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ -from dal.autocomplete import ModelSelect2 +from gestioncof.models import ( + Club, + CofProfile, + Event, + EventCommentField, + EventOption, + EventOptionChoice, + EventRegistration, + Survey, + SurveyQuestion, + SurveyQuestionAnswer, +) +from gestioncof.petits_cours_models import ( + PetitCoursAbility, + PetitCoursAttribution, + PetitCoursAttributionCounter, + PetitCoursDemande, + PetitCoursSubject, +) -def add_link_field(target_model='', field='', link_text=str, - desc_text=str): +def add_link_field(target_model="", field="", link_text=str, desc_text=str): def add_link(cls): reverse_name = target_model or cls.model.__name__.lower() @@ -28,14 +40,14 @@ def add_link_field(target_model='', field='', link_text=str, if not link_obj.id: return "" url = reverse(reverse_path, args=(link_obj.id,)) - return mark_safe("%s" - % (url, link_text(link_obj))) + return mark_safe("%s" % (url, link_text(link_obj))) + link.allow_tags = True - link.short_description = desc_text(reverse_name + ' link') + link.short_description = desc_text(reverse_name + " link") cls.link = link - cls.readonly_fields =\ - list(getattr(cls, 'readonly_fields', [])) + ['link'] + cls.readonly_fields = list(getattr(cls, "readonly_fields", [])) + ["link"] return cls + return add_link @@ -43,32 +55,28 @@ class SurveyQuestionAnswerInline(admin.TabularInline): model = SurveyQuestionAnswer -@add_link_field(desc_text=lambda x: "Réponses", - link_text=lambda x: "Éditer les réponses") +@add_link_field( + desc_text=lambda x: "Réponses", link_text=lambda x: "Éditer les réponses" +) class SurveyQuestionInline(admin.TabularInline): model = SurveyQuestion class SurveyQuestionAdmin(admin.ModelAdmin): - search_fields = ('survey__title', 'answer') - inlines = [ - SurveyQuestionAnswerInline, - ] + search_fields = ("survey__title", "answer") + inlines = [SurveyQuestionAnswerInline] class SurveyAdmin(admin.ModelAdmin): - search_fields = ('title', 'details') - inlines = [ - SurveyQuestionInline, - ] + search_fields = ("title", "details") + inlines = [SurveyQuestionInline] class EventOptionChoiceInline(admin.TabularInline): model = EventOptionChoice -@add_link_field(desc_text=lambda x: "Choix", - link_text=lambda x: "Éditer les choix") +@add_link_field(desc_text=lambda x: "Choix", link_text=lambda x: "Éditer les choix") class EventOptionInline(admin.TabularInline): model = EventOption @@ -78,18 +86,13 @@ class EventCommentFieldInline(admin.TabularInline): class EventOptionAdmin(admin.ModelAdmin): - search_fields = ('event__title', 'name') - inlines = [ - EventOptionChoiceInline, - ] + search_fields = ("event__title", "name") + inlines = [EventOptionChoiceInline] class EventAdmin(admin.ModelAdmin): - search_fields = ('title', 'location', 'description') - inlines = [ - EventOptionInline, - EventCommentFieldInline, - ] + search_fields = ("title", "location", "description") + inlines = [EventOptionInline, EventCommentFieldInline] class CofProfileInline(admin.StackedInline): @@ -98,10 +101,9 @@ class CofProfileInline(admin.StackedInline): class FkeyLookup(object): - def __init__(self, fkeydecl, short_description=None, - admin_order_field=None): - self.fk, fkattrs = fkeydecl.split('__', 1) - self.fkattrs = fkattrs.split('__') + def __init__(self, fkeydecl, short_description=None, admin_order_field=None): + self.fk, fkattrs = fkeydecl.split("__", 1) + self.fkattrs = fkattrs.split("__") self.short_description = short_description or self.fkattrs[-1] self.admin_order_field = admin_order_field or fkeydecl @@ -126,19 +128,19 @@ def ProfileInfo(field, short_description, boolean=False): return getattr(self.profile, field) except CofProfile.DoesNotExist: return "" + getter.short_description = short_description getter.boolean = boolean return getter -User.profile_login_clipper = FkeyLookup("profile__login_clipper", - "Login clipper") + +User.profile_login_clipper = FkeyLookup("profile__login_clipper", "Login clipper") User.profile_phone = ProfileInfo("phone", "Téléphone") User.profile_occupation = ProfileInfo("occupation", "Occupation") User.profile_departement = ProfileInfo("departement", "Departement") User.profile_mailing_cof = ProfileInfo("mailing_cof", "ML COF", True) User.profile_mailing_bda = ProfileInfo("mailing_bda", "ML BdA", True) -User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente", - "ML BdA-R", True) +User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente", "ML BdA-R", True) class UserProfileAdmin(UserAdmin): @@ -147,7 +149,8 @@ class UserProfileAdmin(UserAdmin): return obj.profile.is_buro except CofProfile.DoesNotExist: return False - is_buro.short_description = 'Membre du Buro' + + is_buro.short_description = "Membre du Buro" is_buro.boolean = True def is_cof(self, obj): @@ -155,27 +158,33 @@ class UserProfileAdmin(UserAdmin): return obj.profile.is_cof except CofProfile.DoesNotExist: return False - is_cof.short_description = 'Membre du COF' + + is_cof.short_description = "Membre du COF" is_cof.boolean = True - list_display = ( - UserAdmin.list_display - + ('profile_login_clipper', 'profile_phone', 'profile_occupation', - 'profile_mailing_cof', 'profile_mailing_bda', - 'profile_mailing_bda_revente', 'is_cof', 'is_buro', ) + list_display = UserAdmin.list_display + ( + "profile_login_clipper", + "profile_phone", + "profile_occupation", + "profile_mailing_cof", + "profile_mailing_bda", + "profile_mailing_bda_revente", + "is_cof", + "is_buro", ) - list_display_links = ('username', 'email', 'first_name', 'last_name') - list_filter = UserAdmin.list_filter \ - + ('profile__is_cof', 'profile__is_buro', 'profile__mailing_cof', - 'profile__mailing_bda') - search_fields = UserAdmin.search_fields + ('profile__phone',) - inlines = [ - CofProfileInline, - ] + list_display_links = ("username", "email", "first_name", "last_name") + list_filter = UserAdmin.list_filter + ( + "profile__is_cof", + "profile__is_buro", + "profile__mailing_cof", + "profile__mailing_bda", + ) + search_fields = UserAdmin.search_fields + ("profile__phone",) + inlines = [CofProfileInline] staff_fieldsets = [ - (None, {'fields': ['username', 'password']}), - (_('Personal info'), {'fields': ['first_name', 'last_name', 'email']}), + (None, {"fields": ["username", "password"]}), + (_("Personal info"), {"fields": ["first_name", "last_name", "email"]}), ] def get_fieldsets(self, request, user=None): @@ -184,15 +193,15 @@ class UserProfileAdmin(UserAdmin): return super().get_fieldsets(request, user) def save_model(self, request, user, form, change): - cof_group, created = Group.objects.get_or_create(name='COF') + cof_group, created = Group.objects.get_or_create(name="COF") if created: # Si le groupe COF n'était pas déjà dans la bdd # On lui assigne les bonnes permissions perms = Permission.objects.filter( - Q(content_type__app_label='gestioncof') - | Q(content_type__app_label='bda') - | (Q(content_type__app_label='auth') - & Q(content_type__model='user'))) + Q(content_type__app_label="gestioncof") + | Q(content_type__app_label="bda") + | (Q(content_type__app_label="auth") & Q(content_type__model="user")) + ) cof_group.permissions = perms # On y associe les membres du Burô cof_group.user_set = User.objects.filter(profile__is_buro=True) @@ -214,72 +223,97 @@ def user_str(self): return "{} ({})".format(self.get_full_name(), self.username) else: return self.username + + User.__str__ = user_str class EventRegistrationAdminForm(forms.ModelForm): class Meta: - widgets = { - 'user': ModelSelect2(url='cof-user-autocomplete'), - } + widgets = {"user": ModelSelect2(url="cof-user-autocomplete")} class EventRegistrationAdmin(admin.ModelAdmin): form = EventRegistrationAdminForm - list_display = ('__str__', 'event', 'user', 'paid') - list_filter = ('paid',) - search_fields = ('user__username', 'user__first_name', 'user__last_name', - 'user__email', 'event__title') + list_display = ("__str__", "event", "user", "paid") + list_filter = ("paid",) + search_fields = ( + "user__username", + "user__first_name", + "user__last_name", + "user__email", + "event__title", + ) class PetitCoursAbilityAdmin(admin.ModelAdmin): - list_display = ('user', 'matiere', 'niveau', 'agrege') - search_fields = ('user__username', 'user__first_name', 'user__last_name', - 'user__email', 'matiere__name', 'niveau') - list_filter = ('matiere', 'niveau', 'agrege') + list_display = ("user", "matiere", "niveau", "agrege") + search_fields = ( + "user__username", + "user__first_name", + "user__last_name", + "user__email", + "matiere__name", + "niveau", + ) + list_filter = ("matiere", "niveau", "agrege") class PetitCoursAttributionAdmin(admin.ModelAdmin): - list_display = ('user', 'demande', 'matiere', 'rank', ) - search_fields = ('user__username', 'matiere__name') + list_display = ("user", "demande", "matiere", "rank") + search_fields = ("user__username", "matiere__name") class PetitCoursAttributionCounterAdmin(admin.ModelAdmin): - list_display = ('user', 'matiere', 'count', ) - list_filter = ('matiere',) - search_fields = ('user__username', 'user__first_name', 'user__last_name', - 'user__email', 'matiere__name') - actions = ['reset', ] + list_display = ("user", "matiere", "count") + list_filter = ("matiere",) + search_fields = ( + "user__username", + "user__first_name", + "user__last_name", + "user__email", + "matiere__name", + ) + actions = ["reset"] actions_on_bottom = True def reset(self, request, queryset): queryset.update(count=0) + reset.short_description = "Remise à zéro du compteur" class PetitCoursDemandeAdmin(admin.ModelAdmin): - list_display = ('name', 'email', 'agrege_requis', 'niveau', 'created', - 'traitee', 'processed') - list_filter = ('traitee', 'niveau') - search_fields = ('name', 'email', 'phone', 'lieu', 'remarques') + list_display = ( + "name", + "email", + "agrege_requis", + "niveau", + "created", + "traitee", + "processed", + ) + list_filter = ("traitee", "niveau") + search_fields = ("name", "email", "phone", "lieu", "remarques") class ClubAdminForm(forms.ModelForm): def clean(self): cleaned_data = super().clean() - respos = cleaned_data.get('respos') - members = cleaned_data.get('membres') + respos = cleaned_data.get("respos") + members = cleaned_data.get("membres") for respo in respos.all(): if respo not in members: raise forms.ValidationError( "Erreur : le respo %s n'est pas membre du club." - % respo.get_full_name()) + % respo.get_full_name() + ) return cleaned_data class ClubAdmin(admin.ModelAdmin): - list_display = ['name'] + list_display = ["name"] form = ClubAdminForm @@ -294,7 +328,6 @@ admin.site.register(Club, ClubAdmin) admin.site.register(PetitCoursSubject) admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin) admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin) -admin.site.register(PetitCoursAttributionCounter, - PetitCoursAttributionCounterAdmin) +admin.site.register(PetitCoursAttributionCounter, PetitCoursAttributionCounterAdmin) admin.site.register(PetitCoursDemande, PetitCoursDemandeAdmin) admin.site.register(EventRegistration, EventRegistrationAdmin) diff --git a/gestioncof/apps.py b/gestioncof/apps.py index 78120ef4..b132366a 100644 --- a/gestioncof/apps.py +++ b/gestioncof/apps.py @@ -2,14 +2,16 @@ from django.apps import AppConfig class GestioncofConfig(AppConfig): - name = 'gestioncof' + name = "gestioncof" verbose_name = "Gestion des adhérents du COF" def ready(self): from . import signals + self.register_config() def register_config(self): import djconfig from .forms import GestioncofConfigForm + djconfig.register(GestioncofConfigForm) diff --git a/gestioncof/autocomplete.py b/gestioncof/autocomplete.py index d6483869..0aa94ae9 100644 --- a/gestioncof/autocomplete.py +++ b/gestioncof/autocomplete.py @@ -1,13 +1,12 @@ +from django import shortcuts +from django.conf import settings +from django.contrib.auth.models import User +from django.db.models import Q +from django.http import Http404 from ldap3 import Connection -from django import shortcuts -from django.http import Http404 -from django.db.models import Q -from django.contrib.auth.models import User -from django.conf import settings - -from gestioncof.models import CofProfile from gestioncof.decorators import buro_required +from gestioncof.models import CofProfile class Clipper(object): @@ -20,74 +19,70 @@ class Clipper(object): self.fullname = fullname def __str__(self): - return '{} ({})'.format(self.clipper, self.fullname) + return "{} ({})".format(self.clipper, self.fullname) def __eq__(self, other): - return ( - self.clipper == other.clipper and self.fullname == other.fullname) + return self.clipper == other.clipper and self.fullname == other.fullname @buro_required def autocomplete(request): if "q" not in request.GET: raise Http404 - q = request.GET['q'] - data = { - 'q': q, - } + q = request.GET["q"] + data = {"q": q} queries = {} bits = q.split() # Fetching data from User and CofProfile tables - queries['members'] = CofProfile.objects.filter(is_cof=True) - queries['users'] = User.objects.filter(profile__is_cof=False) + queries["members"] = CofProfile.objects.filter(is_cof=True) + queries["users"] = User.objects.filter(profile__is_cof=False) for bit in bits: - queries['members'] = queries['members'].filter( + queries["members"] = queries["members"].filter( Q(user__first_name__icontains=bit) | Q(user__last_name__icontains=bit) | Q(user__username__icontains=bit) - | Q(login_clipper__icontains=bit)) - queries['users'] = queries['users'].filter( + | Q(login_clipper__icontains=bit) + ) + queries["users"] = queries["users"].filter( Q(first_name__icontains=bit) | Q(last_name__icontains=bit) - | Q(username__icontains=bit)) - queries['members'] = queries['members'].distinct() - queries['users'] = queries['users'].distinct() + | Q(username__icontains=bit) + ) + queries["members"] = queries["members"].distinct() + queries["users"] = queries["users"].distinct() # Clearing redundancies - usernames = ( - set(queries['members'].values_list('login_clipper', flat='True')) - | set(queries['users'].values_list('profile__login_clipper', - flat='True')) + usernames = set(queries["members"].values_list("login_clipper", flat="True")) | set( + queries["users"].values_list("profile__login_clipper", flat="True") ) # Fetching data from the SPI - if getattr(settings, 'LDAP_SERVER_URL', None): + if getattr(settings, "LDAP_SERVER_URL", None): # Fetching - ldap_query = '(&{:s})'.format(''.join( - '(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=bit) - for bit in bits if bit.isalnum() - )) + ldap_query = "(&{:s})".format( + "".join( + "(|(cn=*{bit:s}*)(uid=*{bit:s}*))".format(bit=bit) + for bit in bits + if bit.isalnum() + ) + ) if ldap_query != "(&)": # If none of the bits were legal, we do not perform the query entries = None with Connection(settings.LDAP_SERVER_URL) as conn: - conn.search( - 'dc=spi,dc=ens,dc=fr', ldap_query, - attributes=['uid', 'cn'] - ) + conn.search("dc=spi,dc=ens,dc=fr", ldap_query, attributes=["uid", "cn"]) entries = conn.entries # Clearing redundancies - queries['clippers'] = [ + queries["clippers"] = [ Clipper(entry.uid.value, entry.cn.value) for entry in entries - if entry.uid.value - and entry.uid.value not in usernames + if entry.uid.value and entry.uid.value not in usernames ] # Resulting data data.update(queries) - data['options'] = sum(len(query) for query in queries) + data["options"] = sum(len(query) for query in queries) return shortcuts.render(request, "autocomplete_user.html", data) diff --git a/gestioncof/csv_views.py b/gestioncof/csv_views.py index 733768dc..e85c78b0 100644 --- a/gestioncof/csv_views.py +++ b/gestioncof/csv_views.py @@ -1,14 +1,16 @@ import csv + +from django.apps import apps from django.http import HttpResponse, HttpResponseForbidden from django.template.defaultfilters import slugify -from django.apps import apps def export(qs, fields=None): model = qs.model - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename=%s.csv' \ - % slugify(model.__name__) + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = "attachment; filename=%s.csv" % slugify( + model.__name__ + ) writer = csv.writer(response) # Write headers to CSV file if fields: @@ -32,8 +34,9 @@ def export(qs, fields=None): return response -def admin_list_export(request, model_name, app_label, queryset=None, - fields=None, list_display=True): +def admin_list_export( + request, model_name, app_label, queryset=None, fields=None, list_display=True +): """ Put the following line in your urls.py BEFORE your admin include (r'^admin/(?P[\d\w]+)/(?P[\d\w]+)/csv/', diff --git a/gestioncof/decorators.py b/gestioncof/decorators.py index 3875b77d..fe5a7ccc 100644 --- a/gestioncof/decorators.py +++ b/gestioncof/decorators.py @@ -8,6 +8,7 @@ def is_cof(user): except: return False + cof_required = user_passes_test(is_cof) @@ -18,4 +19,5 @@ def is_buro(user): except: return False + buro_required = user_passes_test(is_buro) diff --git a/gestioncof/forms.py b/gestioncof/forms.py index 4ad9b058..aec5ce24 100644 --- a/gestioncof/forms.py +++ b/gestioncof/forms.py @@ -1,16 +1,13 @@ from django import forms -from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User -from django.forms.widgets import RadioSelect, CheckboxSelectMultiple from django.forms.formsets import BaseFormSet, formset_factory - +from django.forms.widgets import CheckboxSelectMultiple, RadioSelect +from django.utils.translation import ugettext_lazy as _ from djconfig.forms import ConfigForm -from gestioncof.models import CofProfile, EventCommentValue, \ - CalendarSubscription, Club -from gestioncof.widgets import TriStateCheckbox - from bda.models import Spectacle +from gestioncof.models import CalendarSubscription, Club, CofProfile, EventCommentValue +from gestioncof.widgets import TriStateCheckbox class EventForm(forms.Form): @@ -28,31 +25,33 @@ class EventForm(forms.Form): choices[choice.event_option.id].append(choice.id) all_choices = choices for option in event.options.all(): - choices = [(choice.id, choice.value) - for choice in option.choices.all()] + choices = [(choice.id, choice.value) for choice in option.choices.all()] if option.multi_choices: - initial = [] if option.id not in all_choices \ - else all_choices[option.id] + initial = [] if option.id not in all_choices else all_choices[option.id] field = forms.MultipleChoiceField( label=option.name, choices=choices, widget=CheckboxSelectMultiple, required=False, - initial=initial) + initial=initial, + ) else: - initial = None if option.id not in all_choices \ - else all_choices[option.id][0] - field = forms.ChoiceField(label=option.name, - choices=choices, - widget=RadioSelect, - required=False, - initial=initial) + initial = ( + None if option.id not in all_choices else all_choices[option.id][0] + ) + field = forms.ChoiceField( + label=option.name, + choices=choices, + widget=RadioSelect, + required=False, + initial=initial, + ) field.option_id = option.id self.fields["option_%d" % option.id] = field def choices(self): for name, value in self.cleaned_data.items(): - if name.startswith('option_'): + if name.startswith("option_"): yield (self.fields[name].option_id, value) @@ -69,31 +68,33 @@ class SurveyForm(forms.Form): else: answers[answer.survey_question.id].append(answer.id) for question in survey.questions.all(): - choices = [(answer.id, answer.answer) - for answer in question.answers.all()] + choices = [(answer.id, answer.answer) for answer in question.answers.all()] if question.multi_answers: - initial = [] if question.id not in answers\ - else answers[question.id] + initial = [] if question.id not in answers else answers[question.id] field = forms.MultipleChoiceField( label=question.question, choices=choices, widget=CheckboxSelectMultiple, required=False, - initial=initial) + initial=initial, + ) else: - initial = None if question.id not in answers\ - else answers[question.id][0] - field = forms.ChoiceField(label=question.question, - choices=choices, - widget=RadioSelect, - required=False, - initial=initial) + initial = ( + None if question.id not in answers else answers[question.id][0] + ) + field = forms.ChoiceField( + label=question.question, + choices=choices, + widget=RadioSelect, + required=False, + initial=initial, + ) field.question_id = question.id self.fields["question_%d" % question.id] = field def answers(self): for name, value in self.cleaned_data.items(): - if name.startswith('question_'): + if name.startswith("question_"): yield (self.fields[name].question_id, value) @@ -104,8 +105,7 @@ class SurveyStatusFilterForm(forms.Form): for question in survey.questions.all(): for answer in question.answers.all(): name = "question_%d_answer_%d" % (question.id, answer.id) - if self.is_bound \ - and self.data.get(self.add_prefix(name), None): + if self.is_bound and self.data.get(self.add_prefix(name), None): initial = self.data.get(self.add_prefix(name), None) else: initial = "none" @@ -114,16 +114,20 @@ class SurveyStatusFilterForm(forms.Form): choices=[("yes", "yes"), ("no", "no"), ("none", "none")], widget=TriStateCheckbox, required=False, - initial=initial) + initial=initial, + ) field.question_id = question.id field.answer_id = answer.id self.fields[name] = field def filters(self): for name, value in self.cleaned_data.items(): - if name.startswith('question_'): - yield (self.fields[name].question_id, - self.fields[name].answer_id, value) + if name.startswith("question_"): + yield ( + self.fields[name].question_id, + self.fields[name].answer_id, + value, + ) class EventStatusFilterForm(forms.Form): @@ -133,8 +137,7 @@ class EventStatusFilterForm(forms.Form): for option in event.options.all(): for choice in option.choices.all(): name = "option_%d_choice_%d" % (option.id, choice.id) - if self.is_bound \ - and self.data.get(self.add_prefix(name), None): + if self.is_bound and self.data.get(self.add_prefix(name), None): initial = self.data.get(self.add_prefix(name), None) else: initial = "none" @@ -143,7 +146,8 @@ class EventStatusFilterForm(forms.Form): choices=[("yes", "yes"), ("no", "no"), ("none", "none")], widget=TriStateCheckbox, required=False, - initial=initial) + initial=initial, + ) field.option_id = option.id field.choice_id = choice.id self.fields[name] = field @@ -153,19 +157,19 @@ class EventStatusFilterForm(forms.Form): initial = self.data.get(self.add_prefix(name), None) else: initial = "none" - field = forms.ChoiceField(label="Événement payé", - choices=[("yes", "yes"), ("no", "no"), - ("none", "none")], - widget=TriStateCheckbox, - required=False, - initial=initial) + field = forms.ChoiceField( + label="Événement payé", + choices=[("yes", "yes"), ("no", "no"), ("none", "none")], + widget=TriStateCheckbox, + required=False, + initial=initial, + ) self.fields[name] = field def filters(self): for name, value in self.cleaned_data.items(): - if name.startswith('option_'): - yield (self.fields[name].option_id, - self.fields[name].choice_id, value) + if name.startswith("option_"): + yield (self.fields[name].option_id, self.fields[name].choice_id, value) elif name == "event_has_paid": yield ("has_paid", None, value) @@ -184,14 +188,14 @@ class ProfileForm(forms.ModelForm): "mailing_cof", "mailing_bda", "mailing_bda_revente", - "mailing_unernestaparis" + "mailing_unernestaparis", ] class RegistrationUserForm(forms.ModelForm): def __init__(self, *args, **kw): super().__init__(*args, **kw) - self.fields['username'].help_text = "" + self.fields["username"].help_text = "" class Meta: model = User @@ -202,22 +206,23 @@ class RegistrationPassUserForm(RegistrationUserForm): """ Formulaire pour changer le mot de passe d'un utilisateur. """ - password1 = forms.CharField(label=_('Mot de passe'), - widget=forms.PasswordInput) - password2 = forms.CharField(label=_('Confirmation du mot de passe'), - widget=forms.PasswordInput) + + password1 = forms.CharField(label=_("Mot de passe"), widget=forms.PasswordInput) + password2 = forms.CharField( + label=_("Confirmation du mot de passe"), widget=forms.PasswordInput + ) def clean_password2(self): - pass1 = self.cleaned_data['password1'] - pass2 = self.cleaned_data['password2'] + pass1 = self.cleaned_data["password1"] + pass2 = self.cleaned_data["password2"] if pass1 and pass2: if pass1 != pass2: - raise forms.ValidationError(_('Mots de passe non identiques.')) + raise forms.ValidationError(_("Mots de passe non identiques.")) return pass2 def save(self, commit=True, *args, **kwargs): user = super().save(commit, *args, **kwargs) - user.set_password(self.cleaned_data['password2']) + user.set_password(self.cleaned_data["password2"]) if commit: user.save() return user @@ -226,48 +231,62 @@ class RegistrationPassUserForm(RegistrationUserForm): class RegistrationProfileForm(forms.ModelForm): def __init__(self, *args, **kw): super().__init__(*args, **kw) - self.fields['mailing_cof'].initial = True - self.fields['mailing_bda'].initial = True - self.fields['mailing_bda_revente'].initial = True - self.fields['mailing_unernestaparis'].initial = True + self.fields["mailing_cof"].initial = True + self.fields["mailing_bda"].initial = True + self.fields["mailing_bda_revente"].initial = True + self.fields["mailing_unernestaparis"].initial = True self.fields.keyOrder = [ - 'login_clipper', - 'phone', - 'occupation', - 'departement', - 'is_cof', - 'type_cotiz', - 'mailing_cof', - 'mailing_bda', - 'mailing_bda_revente', + "login_clipper", + "phone", + "occupation", + "departement", + "is_cof", + "type_cotiz", + "mailing_cof", + "mailing_bda", + "mailing_bda_revente", "mailing_unernestaparis", - 'comments' + "comments", ] class Meta: model = CofProfile - fields = ("login_clipper", "phone", "occupation", - "departement", "is_cof", "type_cotiz", "mailing_cof", - "mailing_bda", "mailing_bda_revente", - "mailing_unernestaparis", "comments") + fields = ( + "login_clipper", + "phone", + "occupation", + "departement", + "is_cof", + "type_cotiz", + "mailing_cof", + "mailing_bda", + "mailing_bda_revente", + "mailing_unernestaparis", + "comments", + ) -STATUS_CHOICES = (('no', 'Non'), - ('wait', 'Oui mais attente paiement'), - ('paid', 'Oui payé'),) +STATUS_CHOICES = ( + ("no", "Non"), + ("wait", "Oui mais attente paiement"), + ("paid", "Oui payé"), +) class AdminEventForm(forms.Form): - status = forms.ChoiceField(label="Inscription", initial="no", - choices=STATUS_CHOICES, widget=RadioSelect) + status = forms.ChoiceField( + label="Inscription", initial="no", choices=STATUS_CHOICES, widget=RadioSelect + ) def __init__(self, *args, **kwargs): self.event = kwargs.pop("event") registration = kwargs.pop("current_registration", None) - current_choices, paid = \ - (registration.options.all(), registration.paid) \ - if registration is not None else ([], None) + current_choices, paid = ( + (registration.options.all(), registration.paid) + if registration is not None + else ([], None) + ) if paid is True: kwargs["initial"] = {"status": "paid"} elif paid is False: @@ -283,66 +302,69 @@ class AdminEventForm(forms.Form): choices[choice.event_option.id].append(choice.id) all_choices = choices for option in self.event.options.all(): - choices = [(choice.id, choice.value) - for choice in option.choices.all()] + choices = [(choice.id, choice.value) for choice in option.choices.all()] if option.multi_choices: - initial = [] if option.id not in all_choices\ - else all_choices[option.id] + initial = [] if option.id not in all_choices else all_choices[option.id] field = forms.MultipleChoiceField( label=option.name, choices=choices, widget=CheckboxSelectMultiple, required=False, - initial=initial) + initial=initial, + ) else: - initial = None if option.id not in all_choices\ - else all_choices[option.id][0] - field = forms.ChoiceField(label=option.name, - choices=choices, - widget=RadioSelect, - required=False, - initial=initial) + initial = ( + None if option.id not in all_choices else all_choices[option.id][0] + ) + field = forms.ChoiceField( + label=option.name, + choices=choices, + widget=RadioSelect, + required=False, + initial=initial, + ) field.option_id = option.id self.fields["option_%d" % option.id] = field for commentfield in self.event.commentfields.all(): initial = commentfield.default if registration is not None: try: - initial = registration.comments \ - .get(commentfield=commentfield).content + initial = registration.comments.get( + commentfield=commentfield + ).content except EventCommentValue.DoesNotExist: pass - widget = forms.Textarea if commentfield.fieldtype == "text" \ - else forms.TextInput - field = forms.CharField(label=commentfield.name, - widget=widget, - required=False, - initial=initial) + widget = ( + forms.Textarea if commentfield.fieldtype == "text" else forms.TextInput + ) + field = forms.CharField( + label=commentfield.name, widget=widget, required=False, initial=initial + ) field.comment_id = commentfield.id self.fields["comment_%d" % commentfield.id] = field def choices(self): for name, value in self.cleaned_data.items(): - if name.startswith('option_'): + if name.startswith("option_"): yield (self.fields[name].option_id, value) def comments(self): for name, value in self.cleaned_data.items(): - if name.startswith('comment_'): + if name.startswith("comment_"): yield (self.fields[name].comment_id, value) class BaseEventRegistrationFormset(BaseFormSet): def __init__(self, *args, **kwargs): - self.events = kwargs.pop('events') - self.current_registrations = kwargs.pop('current_registrations', None) + self.events = kwargs.pop("events") + self.current_registrations = kwargs.pop("current_registrations", None) self.extra = len(self.events) super().__init__(*args, **kwargs) def _construct_form(self, index, **kwargs): - kwargs['event'] = self.events[index] + kwargs["event"] = self.events[index] if self.current_registrations is not None: - kwargs['current_registration'] = self.current_registrations[index] + kwargs["current_registration"] = self.current_registrations[index] return super()._construct_form(index, **kwargs) @@ -351,34 +373,36 @@ EventFormset = formset_factory(AdminEventForm, BaseEventRegistrationFormset) class CalendarForm(forms.ModelForm): subscribe_to_events = forms.BooleanField( - initial=True, - label="Événements du COF", - required=False) + initial=True, label="Événements du COF", required=False + ) subscribe_to_my_shows = forms.BooleanField( - initial=True, - label="Les spectacles pour lesquels j'ai obtenu une place", - required=False) + initial=True, + label="Les spectacles pour lesquels j'ai obtenu une place", + required=False, + ) other_shows = forms.ModelMultipleChoiceField( - label="Spectacles supplémentaires", - queryset=Spectacle.objects.filter(tirage__active=True), - widget=forms.CheckboxSelectMultiple, - required=False) + label="Spectacles supplémentaires", + queryset=Spectacle.objects.filter(tirage__active=True), + widget=forms.CheckboxSelectMultiple, + required=False, + ) class Meta: model = CalendarSubscription - fields = ['subscribe_to_events', 'subscribe_to_my_shows', - 'other_shows'] + fields = ["subscribe_to_events", "subscribe_to_my_shows", "other_shows"] class ClubsForm(forms.Form): """ Formulaire d'inscription d'un membre à plusieurs clubs du COF. """ + clubs = forms.ModelMultipleChoiceField( - label="Inscriptions aux clubs du COF", - queryset=Club.objects.all(), - widget=forms.CheckboxSelectMultiple, - required=False) + label="Inscriptions aux clubs du COF", + queryset=Club.objects.all(), + widget=forms.CheckboxSelectMultiple, + required=False, + ) # --- @@ -386,9 +410,10 @@ class ClubsForm(forms.Form): # TODO: move this to the `gestion` app once the supportBDS branch is merged # --- + class GestioncofConfigForm(ConfigForm): gestion_banner = forms.CharField( label=_("Announcements banner"), help_text=_("An empty banner disables annoucements"), - max_length=2048 + max_length=2048, ) diff --git a/gestioncof/management/base.py b/gestioncof/management/base.py index ab4d1a30..7d7bcc30 100644 --- a/gestioncof/management/base.py +++ b/gestioncof/management/base.py @@ -2,8 +2,8 @@ Un mixin à utiliser avec BaseCommand pour charger des objets depuis un json """ -import os import json +import os from django.core.management.base import BaseCommand @@ -13,15 +13,14 @@ class MyBaseCommand(BaseCommand): Ajoute une méthode ``from_json`` qui charge des objets à partir d'un json. """ - def from_json(self, filename, data_dir, klass, - callback=lambda obj: obj): + def from_json(self, filename, data_dir, klass, callback=lambda obj: obj): """ Charge les objets contenus dans le fichier json référencé par ``filename`` dans la base de donnée. La fonction callback est appelées sur chaque objet avant enregistrement. """ self.stdout.write("Chargement de {:s}".format(filename)) - with open(os.path.join(data_dir, filename), 'r') as file: + with open(os.path.join(data_dir, filename), "r") as file: descriptions = json.load(file) objects = [] nb_new = 0 @@ -36,6 +35,7 @@ class MyBaseCommand(BaseCommand): objects.append(obj) nb_new += 1 self.stdout.write("- {:d} objets créés".format(nb_new)) - self.stdout.write("- {:d} objets gardés en l'état" - .format(len(objects)-nb_new)) + self.stdout.write( + "- {:d} objets gardés en l'état".format(len(objects) - nb_new) + ) return objects diff --git a/gestioncof/management/commands/loaddevdata.py b/gestioncof/management/commands/loaddevdata.py index 7358c695..44d77065 100644 --- a/gestioncof/management/commands/loaddevdata.py +++ b/gestioncof/management/commands/loaddevdata.py @@ -15,13 +15,14 @@ from django.core.management import call_command from gestioncof.management.base import MyBaseCommand from gestioncof.petits_cours_models import ( - PetitCoursAbility, PetitCoursSubject, LEVELS_CHOICES, - PetitCoursAttributionCounter + LEVELS_CHOICES, + PetitCoursAbility, + PetitCoursAttributionCounter, + PetitCoursSubject, ) # Où sont stockés les fichiers json -DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), - 'data') +DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data") class Command(MyBaseCommand): @@ -32,11 +33,11 @@ class Command(MyBaseCommand): Permet de ne pas créer l'utilisateur "root". """ parser.add_argument( - '--no-root', - action='store_true', - dest='no-root', + "--no-root", + action="store_true", + dest="no-root", default=False, - help='Ne crée pas l\'utilisateur "root"' + help='Ne crée pas l\'utilisateur "root"', ) def handle(self, *args, **options): @@ -45,24 +46,25 @@ class Command(MyBaseCommand): # --- # Gaulois - gaulois = self.from_json('gaulois.json', DATA_DIR, User) + gaulois = self.from_json("gaulois.json", DATA_DIR, User) for user in gaulois: user.profile.is_cof = True user.profile.save() # Romains - self.from_json('romains.json', DATA_DIR, User) + self.from_json("romains.json", DATA_DIR, User) # Root - no_root = options.get('no-root', False) + no_root = options.get("no-root", False) if not no_root: self.stdout.write("Création de l'utilisateur root") root, _ = User.objects.get_or_create( - username='root', - first_name='super', - last_name='user', - email='root@localhost') - root.set_password('root') + username="root", + first_name="super", + last_name="user", + email="root@localhost", + ) + root.set_password("root") root.is_staff = True root.is_superuser = True root.profile.is_cof = True @@ -87,18 +89,17 @@ class Command(MyBaseCommand): # L'utilisateur est compétent dans une matière subject = random.choice(subjects) if not PetitCoursAbility.objects.filter( - user=user, - matiere=subject).exists(): + user=user, matiere=subject + ).exists(): PetitCoursAbility.objects.create( user=user, matiere=subject, niveau=random.choice(levels), - agrege=bool(random.randint(0, 1)) + agrege=bool(random.randint(0, 1)), ) # On initialise son compteur d'attributions PetitCoursAttributionCounter.objects.get_or_create( - user=user, - matiere=subject + user=user, matiere=subject ) self.stdout.write("- {:d} inscriptions".format(nb_of_teachers)) @@ -106,10 +107,10 @@ class Command(MyBaseCommand): # Le BdA # --- - call_command('loadbdadevdata') + call_command("loadbdadevdata") # --- # La K-Fêt # --- - call_command('loadkfetdevdata') + call_command("loadkfetdevdata") diff --git a/gestioncof/management/commands/syncmails.py b/gestioncof/management/commands/syncmails.py index 8f302186..0dd15d34 100644 --- a/gestioncof/management/commands/syncmails.py +++ b/gestioncof/management/commands/syncmails.py @@ -4,11 +4,10 @@ Import des mails de GestioCOF dans la base de donnée import json import os -from custommail.models import Type, CustomMail, Variable -from django.core.management.base import BaseCommand +from custommail.models import CustomMail, Type, Variable from django.contrib.contenttypes.models import ContentType - +from django.core.management.base import BaseCommand DATA_LOCATION = os.path.join(os.path.dirname(__file__), "..", "data", "custommail.json") @@ -19,15 +18,15 @@ def dummy_log(__): # XXX. this should probably be in the custommail package def load_from_file(log=dummy_log, verbosity=1): - with open(DATA_LOCATION, 'r') as jsonfile: + with open(DATA_LOCATION, "r") as jsonfile: mail_data = json.load(jsonfile) # On se souvient à quel objet correspond quel pk du json - assoc = {'types': {}, 'mails': {}} - status = {'synced': 0, 'unchanged': 0} + assoc = {"types": {}, "mails": {}} + status = {"synced": 0, "unchanged": 0} for obj in mail_data: - fields = obj['fields'] + fields = obj["fields"] # Pour les trois types d'objets : # - On récupère les objets référencés par les clefs étrangères @@ -36,58 +35,55 @@ def load_from_file(log=dummy_log, verbosity=1): # plus haut # Variable types - if obj['model'] == 'custommail.variabletype': - fields['inner1'] = assoc['types'].get(fields['inner1']) - fields['inner2'] = assoc['types'].get(fields['inner2']) - if fields['kind'] == 'model': - fields['content_type'] = ( - ContentType.objects - .get_by_natural_key(*fields['content_type']) + if obj["model"] == "custommail.variabletype": + fields["inner1"] = assoc["types"].get(fields["inner1"]) + fields["inner2"] = assoc["types"].get(fields["inner2"]) + if fields["kind"] == "model": + fields["content_type"] = ContentType.objects.get_by_natural_key( + *fields["content_type"] ) var_type, _ = Type.objects.get_or_create(**fields) - assoc['types'][obj['pk']] = var_type + assoc["types"][obj["pk"]] = var_type # Custom mails - if obj['model'] == 'custommail.custommail': + if obj["model"] == "custommail.custommail": mail = None try: - mail = CustomMail.objects.get(shortname=fields['shortname']) - status['unchanged'] += 1 + mail = CustomMail.objects.get(shortname=fields["shortname"]) + status["unchanged"] += 1 except CustomMail.DoesNotExist: mail = CustomMail.objects.create(**fields) - status['synced'] += 1 + status["synced"] += 1 if verbosity: - log('SYNCED {:s}'.format(fields['shortname'])) - assoc['mails'][obj['pk']] = mail + log("SYNCED {:s}".format(fields["shortname"])) + assoc["mails"][obj["pk"]] = mail # Variables - if obj['model'] == 'custommail.custommailvariable': - fields['custommail'] = assoc['mails'].get(fields['custommail']) - fields['type'] = assoc['types'].get(fields['type']) + if obj["model"] == "custommail.custommailvariable": + fields["custommail"] = assoc["mails"].get(fields["custommail"]) + fields["type"] = assoc["types"].get(fields["type"]) try: Variable.objects.get( - custommail=fields['custommail'], - name=fields['name'] + custommail=fields["custommail"], name=fields["name"] ) except Variable.DoesNotExist: Variable.objects.create(**fields) if verbosity: - log( - '{synced:d} mails synchronized {unchanged:d} unchanged' - .format(**status) - ) + log("{synced:d} mails synchronized {unchanged:d} unchanged".format(**status)) class Command(BaseCommand): - help = ("Va chercher les données mails de GestioCOF stocké au format json " - "dans /gestioncof/management/data/custommails.json. Le format des " - "données est celui donné par la commande :" - " `python manage.py dumpdata custommail --natural-foreign` " - "La bonne façon de mettre à jour ce fichier est donc de le " - "charger à l'aide de syncmails, le faire les modifications à " - "l'aide de l'interface administration et/ou du shell puis de le " - "remplacer par le nouveau résultat de la commande précédente.") + help = ( + "Va chercher les données mails de GestioCOF stocké au format json " + "dans /gestioncof/management/data/custommails.json. Le format des " + "données est celui donné par la commande :" + " `python manage.py dumpdata custommail --natural-foreign` " + "La bonne façon de mettre à jour ce fichier est donc de le " + "charger à l'aide de syncmails, le faire les modifications à " + "l'aide de l'interface administration et/ou du shell puis de le " + "remplacer par le nouveau résultat de la commande précédente." + ) def handle(self, *args, **options): load_from_file(log=self.stdout.write) diff --git a/gestioncof/migrations/0001_initial.py b/gestioncof/migrations/0001_initial.py index c6bb6151..b3c10b90 100644 --- a/gestioncof/migrations/0001_initial.py +++ b/gestioncof/migrations/0001_initial.py @@ -1,333 +1,856 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] + dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)] operations = [ migrations.CreateModel( - name='Clipper', + name="Clipper", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('username', models.CharField(max_length=20, verbose_name=b'Identifiant')), - ('fullname', models.CharField(max_length=200, verbose_name=b'Nom complet')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "username", + models.CharField(max_length=20, verbose_name=b"Identifiant"), + ), + ( + "fullname", + models.CharField(max_length=200, verbose_name=b"Nom complet"), + ), ], ), migrations.CreateModel( - name='Club', + name="Club", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=200, verbose_name=b'Nom')), - ('description', models.TextField(verbose_name=b'Description')), - ('membres', models.ManyToManyField(related_name='clubs', to=settings.AUTH_USER_MODEL)), - ('respos', models.ManyToManyField(related_name='clubs_geres', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=200, verbose_name=b"Nom")), + ("description", models.TextField(verbose_name=b"Description")), + ( + "membres", + models.ManyToManyField( + related_name="clubs", to=settings.AUTH_USER_MODEL + ), + ), + ( + "respos", + models.ManyToManyField( + related_name="clubs_geres", to=settings.AUTH_USER_MODEL + ), + ), ], ), migrations.CreateModel( - name='CofProfile', + name="CofProfile", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('login_clipper', models.CharField(max_length=8, verbose_name=b'Login clipper', blank=True)), - ('is_cof', models.BooleanField(default=False, verbose_name=b'Membre du COF')), - ('num', models.IntegerField(default=0, verbose_name=b"Num\xc3\xa9ro d'adh\xc3\xa9rent", blank=True)), - ('phone', models.CharField(max_length=20, verbose_name=b'T\xc3\xa9l\xc3\xa9phone', blank=True)), - ('occupation', models.CharField(default=b'1A', max_length=9, verbose_name='Occupation', choices=[(b'exterieur', 'Ext\xe9rieur'), (b'1A', '1A'), (b'2A', '2A'), (b'3A', '3A'), (b'4A', '4A'), (b'archicube', 'Archicube'), (b'doctorant', 'Doctorant'), (b'CST', 'CST')])), - ('departement', models.CharField(max_length=50, verbose_name='D\xe9partement', blank=True)), - ('type_cotiz', models.CharField(default=b'normalien', max_length=9, verbose_name='Type de cotisation', choices=[(b'etudiant', 'Normalien \xe9tudiant'), (b'normalien', 'Normalien \xe9l\xe8ve'), (b'exterieur', 'Ext\xe9rieur')])), - ('mailing_cof', models.BooleanField(default=False, verbose_name=b'Recevoir les mails COF')), - ('mailing_bda', models.BooleanField(default=False, verbose_name=b'Recevoir les mails BdA')), - ('mailing_bda_revente', models.BooleanField(default=False, verbose_name=b'Recevoir les mails de revente de places BdA')), - ('comments', models.TextField(verbose_name=b'Commentaires visibles uniquement par le Buro', blank=True)), - ('is_buro', models.BooleanField(default=False, verbose_name=b'Membre du Bur\xc3\xb4')), - ('petits_cours_accept', models.BooleanField(default=False, verbose_name=b'Recevoir des petits cours')), - ('petits_cours_remarques', models.TextField(default=b'', verbose_name='Remarques et pr\xe9cisions pour les petits cours', blank=True)), - ('user', models.OneToOneField(related_name='profile', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "login_clipper", + models.CharField( + max_length=8, verbose_name=b"Login clipper", blank=True + ), + ), + ( + "is_cof", + models.BooleanField(default=False, verbose_name=b"Membre du COF"), + ), + ( + "num", + models.IntegerField( + default=0, + verbose_name=b"Num\xc3\xa9ro d'adh\xc3\xa9rent", + blank=True, + ), + ), + ( + "phone", + models.CharField( + max_length=20, + verbose_name=b"T\xc3\xa9l\xc3\xa9phone", + blank=True, + ), + ), + ( + "occupation", + models.CharField( + default=b"1A", + max_length=9, + verbose_name="Occupation", + choices=[ + (b"exterieur", "Ext\xe9rieur"), + (b"1A", "1A"), + (b"2A", "2A"), + (b"3A", "3A"), + (b"4A", "4A"), + (b"archicube", "Archicube"), + (b"doctorant", "Doctorant"), + (b"CST", "CST"), + ], + ), + ), + ( + "departement", + models.CharField( + max_length=50, verbose_name="D\xe9partement", blank=True + ), + ), + ( + "type_cotiz", + models.CharField( + default=b"normalien", + max_length=9, + verbose_name="Type de cotisation", + choices=[ + (b"etudiant", "Normalien \xe9tudiant"), + (b"normalien", "Normalien \xe9l\xe8ve"), + (b"exterieur", "Ext\xe9rieur"), + ], + ), + ), + ( + "mailing_cof", + models.BooleanField( + default=False, verbose_name=b"Recevoir les mails COF" + ), + ), + ( + "mailing_bda", + models.BooleanField( + default=False, verbose_name=b"Recevoir les mails BdA" + ), + ), + ( + "mailing_bda_revente", + models.BooleanField( + default=False, + verbose_name=b"Recevoir les mails de revente de places BdA", + ), + ), + ( + "comments", + models.TextField( + verbose_name=b"Commentaires visibles uniquement par le Buro", + blank=True, + ), + ), + ( + "is_buro", + models.BooleanField( + default=False, verbose_name=b"Membre du Bur\xc3\xb4" + ), + ), + ( + "petits_cours_accept", + models.BooleanField( + default=False, verbose_name=b"Recevoir des petits cours" + ), + ), + ( + "petits_cours_remarques", + models.TextField( + default=b"", + verbose_name="Remarques et pr\xe9cisions pour les petits cours", + blank=True, + ), + ), + ( + "user", + models.OneToOneField( + related_name="profile", + to=settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'Profil COF', - 'verbose_name_plural': 'Profils COF', + "verbose_name": "Profil COF", + "verbose_name_plural": "Profils COF", }, ), migrations.CreateModel( - name='CustomMail', + name="CustomMail", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('shortname', models.SlugField()), - ('title', models.CharField(max_length=200, verbose_name=b'Titre')), - ('content', models.TextField(verbose_name=b'Contenu')), - ('comments', models.TextField(verbose_name=b'Informations contextuelles sur le mail', blank=True)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("shortname", models.SlugField()), + ("title", models.CharField(max_length=200, verbose_name=b"Titre")), + ("content", models.TextField(verbose_name=b"Contenu")), + ( + "comments", + models.TextField( + verbose_name=b"Informations contextuelles sur le mail", + blank=True, + ), + ), ], - options={ - 'verbose_name': 'Mails personnalisables', - }, + options={"verbose_name": "Mails personnalisables"}, ), migrations.CreateModel( - name='Event', + name="Event", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=200, verbose_name=b'Titre')), - ('location', models.CharField(max_length=200, verbose_name=b'Lieu')), - ('start_date', models.DateField(null=True, verbose_name=b'Date de d\xc3\xa9but', blank=True)), - ('end_date', models.DateField(null=True, verbose_name=b'Date de fin', blank=True)), - ('description', models.TextField(verbose_name=b'Description', blank=True)), - ('registration_open', models.BooleanField(default=True, verbose_name=b'Inscriptions ouvertes')), - ('old', models.BooleanField(default=False, verbose_name=b'Archiver (\xc3\xa9v\xc3\xa9nement fini)')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("title", models.CharField(max_length=200, verbose_name=b"Titre")), + ("location", models.CharField(max_length=200, verbose_name=b"Lieu")), + ( + "start_date", + models.DateField( + null=True, verbose_name=b"Date de d\xc3\xa9but", blank=True + ), + ), + ( + "end_date", + models.DateField( + null=True, verbose_name=b"Date de fin", blank=True + ), + ), + ( + "description", + models.TextField(verbose_name=b"Description", blank=True), + ), + ( + "registration_open", + models.BooleanField( + default=True, verbose_name=b"Inscriptions ouvertes" + ), + ), + ( + "old", + models.BooleanField( + default=False, + verbose_name=b"Archiver (\xc3\xa9v\xc3\xa9nement fini)", + ), + ), ], - options={ - 'verbose_name': '\xc9v\xe9nement', - }, + options={"verbose_name": "\xc9v\xe9nement"}, ), migrations.CreateModel( - name='EventCommentField', + name="EventCommentField", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=200, verbose_name=b'Champ')), - ('fieldtype', models.CharField(default=b'text', max_length=10, verbose_name=b'Type', choices=[(b'text', 'Texte long'), (b'char', 'Texte court')])), - ('default', models.TextField(verbose_name=b'Valeur par d\xc3\xa9faut', blank=True)), - ('event', models.ForeignKey(related_name='commentfields', to='gestioncof.Event', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=200, verbose_name=b"Champ")), + ( + "fieldtype", + models.CharField( + default=b"text", + max_length=10, + verbose_name=b"Type", + choices=[(b"text", "Texte long"), (b"char", "Texte court")], + ), + ), + ( + "default", + models.TextField( + verbose_name=b"Valeur par d\xc3\xa9faut", blank=True + ), + ), + ( + "event", + models.ForeignKey( + related_name="commentfields", + to="gestioncof.Event", + on_delete=models.CASCADE, + ), + ), ], - options={ - 'verbose_name': 'Champ', - }, + options={"verbose_name": "Champ"}, ), migrations.CreateModel( - name='EventCommentValue', + name="EventCommentValue", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('content', models.TextField(null=True, verbose_name=b'Contenu', blank=True)), - ('commentfield', models.ForeignKey(related_name='values', to='gestioncof.EventCommentField', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "content", + models.TextField(null=True, verbose_name=b"Contenu", blank=True), + ), + ( + "commentfield", + models.ForeignKey( + related_name="values", + to="gestioncof.EventCommentField", + on_delete=models.CASCADE, + ), + ), ], ), migrations.CreateModel( - name='EventOption', + name="EventOption", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=200, verbose_name=b'Option')), - ('multi_choices', models.BooleanField(default=False, verbose_name=b'Choix multiples')), - ('event', models.ForeignKey(related_name='options', to='gestioncof.Event', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=200, verbose_name=b"Option")), + ( + "multi_choices", + models.BooleanField(default=False, verbose_name=b"Choix multiples"), + ), + ( + "event", + models.ForeignKey( + related_name="options", + to="gestioncof.Event", + on_delete=models.CASCADE, + ), + ), + ], + options={"verbose_name": "Option"}, + ), + migrations.CreateModel( + name="EventOptionChoice", + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("value", models.CharField(max_length=200, verbose_name=b"Valeur")), + ( + "event_option", + models.ForeignKey( + related_name="choices", + to="gestioncof.EventOption", + on_delete=models.CASCADE, + ), + ), + ], + options={"verbose_name": "Choix"}, + ), + migrations.CreateModel( + name="EventRegistration", + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "paid", + models.BooleanField(default=False, verbose_name=b"A pay\xc3\xa9"), + ), + ( + "event", + models.ForeignKey(to="gestioncof.Event", on_delete=models.CASCADE), + ), + ( + "filledcomments", + models.ManyToManyField( + to="gestioncof.EventCommentField", + through="gestioncof.EventCommentValue", + ), + ), + ("options", models.ManyToManyField(to="gestioncof.EventOptionChoice")), + ( + "user", + models.ForeignKey( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), + ), + ], + options={"verbose_name": "Inscription"}, + ), + migrations.CreateModel( + name="PetitCoursAbility", + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "niveau", + models.CharField( + max_length=12, + verbose_name="Niveau", + choices=[ + (b"college", "Coll\xe8ge"), + (b"lycee", "Lyc\xe9e"), + (b"prepa1styear", "Pr\xe9pa 1\xe8re ann\xe9e / L1"), + (b"prepa2ndyear", "Pr\xe9pa 2\xe8me ann\xe9e / L2"), + (b"licence3", "Licence 3"), + (b"other", "Autre (pr\xe9ciser dans les commentaires)"), + ], + ), + ), + ( + "agrege", + models.BooleanField(default=False, verbose_name="Agr\xe9g\xe9"), + ), ], options={ - 'verbose_name': 'Option', + "verbose_name": "Comp\xe9tence petits cours", + "verbose_name_plural": "Comp\xe9tences des petits cours", }, ), migrations.CreateModel( - name='EventOptionChoice', + name="PetitCoursAttribution", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('value', models.CharField(max_length=200, verbose_name=b'Valeur')), - ('event_option', models.ForeignKey(related_name='choices', to='gestioncof.EventOption', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "date", + models.DateTimeField( + auto_now_add=True, verbose_name="Date d'attribution" + ), + ), + ("rank", models.IntegerField(verbose_name=b"Rang dans l'email")), + ( + "selected", + models.BooleanField( + default=False, verbose_name="S\xe9lectionn\xe9 par le demandeur" + ), + ), ], options={ - 'verbose_name': 'Choix', + "verbose_name": "Attribution de petits cours", + "verbose_name_plural": "Attributions de petits cours", }, ), migrations.CreateModel( - name='EventRegistration', + name="PetitCoursAttributionCounter", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('paid', models.BooleanField(default=False, verbose_name=b'A pay\xc3\xa9')), - ('event', models.ForeignKey(to='gestioncof.Event', on_delete=models.CASCADE)), - ('filledcomments', models.ManyToManyField(to='gestioncof.EventCommentField', through='gestioncof.EventCommentValue')), - ('options', models.ManyToManyField(to='gestioncof.EventOptionChoice')), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "count", + models.IntegerField(default=0, verbose_name=b"Nombre d'envois"), + ), ], options={ - 'verbose_name': 'Inscription', + "verbose_name": "Compteur d'attribution de petits cours", + "verbose_name_plural": "Compteurs d'attributions de petits cours", }, ), migrations.CreateModel( - name='PetitCoursAbility', + name="PetitCoursDemande", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('niveau', models.CharField(max_length=12, verbose_name='Niveau', choices=[(b'college', 'Coll\xe8ge'), (b'lycee', 'Lyc\xe9e'), (b'prepa1styear', 'Pr\xe9pa 1\xe8re ann\xe9e / L1'), (b'prepa2ndyear', 'Pr\xe9pa 2\xe8me ann\xe9e / L2'), (b'licence3', 'Licence 3'), (b'other', 'Autre (pr\xe9ciser dans les commentaires)')])), - ('agrege', models.BooleanField(default=False, verbose_name='Agr\xe9g\xe9')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField(max_length=200, verbose_name="Nom/pr\xe9nom"), + ), + ( + "email", + models.CharField(max_length=300, verbose_name="Adresse email"), + ), + ( + "phone", + models.CharField( + max_length=20, + verbose_name="T\xe9l\xe9phone (facultatif)", + blank=True, + ), + ), + ( + "quand", + models.CharField( + help_text="Indiquez ici la p\xe9riode d\xe9sir\xe9e pour les petits cours (vacances scolaires, semaine, week-end).", + max_length=300, + verbose_name="Quand ?", + blank=True, + ), + ), + ( + "freq", + models.CharField( + help_text="Indiquez ici la fr\xe9quence envisag\xe9e (hebdomadaire, 2 fois par semaine, ...)", + max_length=300, + verbose_name="Fr\xe9quence", + blank=True, + ), + ), + ( + "lieu", + models.CharField( + help_text="Si vous avez avez une pr\xe9f\xe9rence sur le lieu.", + max_length=300, + verbose_name="Lieu (si pr\xe9f\xe9rence)", + blank=True, + ), + ), + ( + "agrege_requis", + models.BooleanField( + default=False, verbose_name="Agr\xe9g\xe9 requis" + ), + ), + ( + "niveau", + models.CharField( + default=b"", + max_length=12, + verbose_name="Niveau", + choices=[ + (b"college", "Coll\xe8ge"), + (b"lycee", "Lyc\xe9e"), + (b"prepa1styear", "Pr\xe9pa 1\xe8re ann\xe9e / L1"), + (b"prepa2ndyear", "Pr\xe9pa 2\xe8me ann\xe9e / L2"), + (b"licence3", "Licence 3"), + (b"other", "Autre (pr\xe9ciser dans les commentaires)"), + ], + ), + ), + ( + "remarques", + models.TextField( + verbose_name="Remarques et pr\xe9cisions", blank=True + ), + ), + ( + "traitee", + models.BooleanField(default=False, verbose_name="Trait\xe9e"), + ), + ( + "processed", + models.DateTimeField(verbose_name="Date de traitement", blank=True), + ), + ( + "created", + models.DateTimeField( + auto_now_add=True, verbose_name="Date de cr\xe9ation" + ), + ), ], options={ - 'verbose_name': 'Comp\xe9tence petits cours', - 'verbose_name_plural': 'Comp\xe9tences des petits cours', + "verbose_name": "Demande de petits cours", + "verbose_name_plural": "Demandes de petits cours", }, ), migrations.CreateModel( - name='PetitCoursAttribution', + name="PetitCoursSubject", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(auto_now_add=True, verbose_name="Date d'attribution")), - ('rank', models.IntegerField(verbose_name=b"Rang dans l'email")), - ('selected', models.BooleanField(default=False, verbose_name='S\xe9lectionn\xe9 par le demandeur')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=30, verbose_name="Mati\xe8re")), + ( + "users", + models.ManyToManyField( + related_name="petits_cours_matieres", + through="gestioncof.PetitCoursAbility", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Attribution de petits cours', - 'verbose_name_plural': 'Attributions de petits cours', + "verbose_name": "Mati\xe8re de petits cours", + "verbose_name_plural": "Mati\xe8res des petits cours", }, ), migrations.CreateModel( - name='PetitCoursAttributionCounter', + name="Survey", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('count', models.IntegerField(default=0, verbose_name=b"Nombre d'envois")), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("title", models.CharField(max_length=200, verbose_name=b"Titre")), + ( + "details", + models.TextField(verbose_name=b"D\xc3\xa9tails", blank=True), + ), + ( + "survey_open", + models.BooleanField(default=True, verbose_name=b"Sondage ouvert"), + ), + ( + "old", + models.BooleanField( + default=False, verbose_name=b"Archiver (sondage fini)" + ), + ), ], - options={ - 'verbose_name': "Compteur d'attribution de petits cours", - 'verbose_name_plural': "Compteurs d'attributions de petits cours", - }, + options={"verbose_name": "Sondage"}, ), migrations.CreateModel( - name='PetitCoursDemande', + name="SurveyAnswer", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=200, verbose_name='Nom/pr\xe9nom')), - ('email', models.CharField(max_length=300, verbose_name='Adresse email')), - ('phone', models.CharField(max_length=20, verbose_name='T\xe9l\xe9phone (facultatif)', blank=True)), - ('quand', models.CharField(help_text='Indiquez ici la p\xe9riode d\xe9sir\xe9e pour les petits cours (vacances scolaires, semaine, week-end).', max_length=300, verbose_name='Quand ?', blank=True)), - ('freq', models.CharField(help_text='Indiquez ici la fr\xe9quence envisag\xe9e (hebdomadaire, 2 fois par semaine, ...)', max_length=300, verbose_name='Fr\xe9quence', blank=True)), - ('lieu', models.CharField(help_text='Si vous avez avez une pr\xe9f\xe9rence sur le lieu.', max_length=300, verbose_name='Lieu (si pr\xe9f\xe9rence)', blank=True)), - ('agrege_requis', models.BooleanField(default=False, verbose_name='Agr\xe9g\xe9 requis')), - ('niveau', models.CharField(default=b'', max_length=12, verbose_name='Niveau', choices=[(b'college', 'Coll\xe8ge'), (b'lycee', 'Lyc\xe9e'), (b'prepa1styear', 'Pr\xe9pa 1\xe8re ann\xe9e / L1'), (b'prepa2ndyear', 'Pr\xe9pa 2\xe8me ann\xe9e / L2'), (b'licence3', 'Licence 3'), (b'other', 'Autre (pr\xe9ciser dans les commentaires)')])), - ('remarques', models.TextField(verbose_name='Remarques et pr\xe9cisions', blank=True)), - ('traitee', models.BooleanField(default=False, verbose_name='Trait\xe9e')), - ('processed', models.DateTimeField(verbose_name='Date de traitement', blank=True)), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Date de cr\xe9ation')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ) ], - options={ - 'verbose_name': 'Demande de petits cours', - 'verbose_name_plural': 'Demandes de petits cours', - }, + options={"verbose_name": "R\xe9ponses"}, ), migrations.CreateModel( - name='PetitCoursSubject', + name="SurveyQuestion", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=30, verbose_name='Mati\xe8re')), - ('users', models.ManyToManyField(related_name='petits_cours_matieres', through='gestioncof.PetitCoursAbility', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "question", + models.CharField(max_length=200, verbose_name=b"Question"), + ), + ( + "multi_answers", + models.BooleanField(default=False, verbose_name=b"Choix multiples"), + ), + ( + "survey", + models.ForeignKey( + related_name="questions", + to="gestioncof.Survey", + on_delete=models.CASCADE, + ), + ), ], - options={ - 'verbose_name': 'Mati\xe8re de petits cours', - 'verbose_name_plural': 'Mati\xe8res des petits cours', - }, + options={"verbose_name": "Question"}, ), migrations.CreateModel( - name='Survey', + name="SurveyQuestionAnswer", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=200, verbose_name=b'Titre')), - ('details', models.TextField(verbose_name=b'D\xc3\xa9tails', blank=True)), - ('survey_open', models.BooleanField(default=True, verbose_name=b'Sondage ouvert')), - ('old', models.BooleanField(default=False, verbose_name=b'Archiver (sondage fini)')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "answer", + models.CharField(max_length=200, verbose_name=b"R\xc3\xa9ponse"), + ), + ( + "survey_question", + models.ForeignKey( + related_name="answers", + to="gestioncof.SurveyQuestion", + on_delete=models.CASCADE, + ), + ), ], - options={ - 'verbose_name': 'Sondage', - }, - ), - migrations.CreateModel( - name='SurveyAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ], - options={ - 'verbose_name': 'R\xe9ponses', - }, - ), - migrations.CreateModel( - name='SurveyQuestion', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('question', models.CharField(max_length=200, verbose_name=b'Question')), - ('multi_answers', models.BooleanField(default=False, verbose_name=b'Choix multiples')), - ('survey', models.ForeignKey(related_name='questions', to='gestioncof.Survey', on_delete=models.CASCADE)), - ], - options={ - 'verbose_name': 'Question', - }, - ), - migrations.CreateModel( - name='SurveyQuestionAnswer', - fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('answer', models.CharField(max_length=200, verbose_name=b'R\xc3\xa9ponse')), - ('survey_question', models.ForeignKey(related_name='answers', to='gestioncof.SurveyQuestion', on_delete=models.CASCADE)), - ], - options={ - 'verbose_name': 'R\xe9ponse', - }, + options={"verbose_name": "R\xe9ponse"}, ), migrations.AddField( - model_name='surveyanswer', - name='answers', - field=models.ManyToManyField(related_name='selected_by', to='gestioncof.SurveyQuestionAnswer'), + model_name="surveyanswer", + name="answers", + field=models.ManyToManyField( + related_name="selected_by", to="gestioncof.SurveyQuestionAnswer" + ), ), migrations.AddField( - model_name='surveyanswer', - name='survey', - field=models.ForeignKey(to='gestioncof.Survey', on_delete=models.CASCADE), + model_name="surveyanswer", + name="survey", + field=models.ForeignKey(to="gestioncof.Survey", on_delete=models.CASCADE), ), migrations.AddField( - model_name='surveyanswer', - name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + model_name="surveyanswer", + name="user", + field=models.ForeignKey( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), ), migrations.AddField( - model_name='petitcoursdemande', - name='matieres', - field=models.ManyToManyField(related_name='demandes', verbose_name='Mati\xe8res', to='gestioncof.PetitCoursSubject'), + model_name="petitcoursdemande", + name="matieres", + field=models.ManyToManyField( + related_name="demandes", + verbose_name="Mati\xe8res", + to="gestioncof.PetitCoursSubject", + ), ), migrations.AddField( - model_name='petitcoursdemande', - name='traitee_par', - field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), + model_name="petitcoursdemande", + name="traitee_par", + field=models.ForeignKey( + blank=True, + to=settings.AUTH_USER_MODEL, + null=True, + on_delete=models.CASCADE, + ), ), migrations.AddField( - model_name='petitcoursattributioncounter', - name='matiere', - field=models.ForeignKey(verbose_name='Matiere', to='gestioncof.PetitCoursSubject', on_delete=models.CASCADE), + model_name="petitcoursattributioncounter", + name="matiere", + field=models.ForeignKey( + verbose_name="Matiere", + to="gestioncof.PetitCoursSubject", + on_delete=models.CASCADE, + ), ), migrations.AddField( - model_name='petitcoursattributioncounter', - name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + model_name="petitcoursattributioncounter", + name="user", + field=models.ForeignKey( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), ), migrations.AddField( - model_name='petitcoursattribution', - name='demande', - field=models.ForeignKey(verbose_name='Demande', to='gestioncof.PetitCoursDemande', on_delete=models.CASCADE), + model_name="petitcoursattribution", + name="demande", + field=models.ForeignKey( + verbose_name="Demande", + to="gestioncof.PetitCoursDemande", + on_delete=models.CASCADE, + ), ), migrations.AddField( - model_name='petitcoursattribution', - name='matiere', - field=models.ForeignKey(verbose_name='Mati\xe8re', to='gestioncof.PetitCoursSubject', on_delete=models.CASCADE), + model_name="petitcoursattribution", + name="matiere", + field=models.ForeignKey( + verbose_name="Mati\xe8re", + to="gestioncof.PetitCoursSubject", + on_delete=models.CASCADE, + ), ), migrations.AddField( - model_name='petitcoursattribution', - name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + model_name="petitcoursattribution", + name="user", + field=models.ForeignKey( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), ), migrations.AddField( - model_name='petitcoursability', - name='matiere', - field=models.ForeignKey(verbose_name='Mati\xe8re', to='gestioncof.PetitCoursSubject', on_delete=models.CASCADE), + model_name="petitcoursability", + name="matiere", + field=models.ForeignKey( + verbose_name="Mati\xe8re", + to="gestioncof.PetitCoursSubject", + on_delete=models.CASCADE, + ), ), migrations.AddField( - model_name='petitcoursability', - name='user', - field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), + model_name="petitcoursability", + name="user", + field=models.ForeignKey( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), ), migrations.AddField( - model_name='eventcommentvalue', - name='registration', - field=models.ForeignKey(related_name='comments', to='gestioncof.EventRegistration', on_delete=models.CASCADE), + model_name="eventcommentvalue", + name="registration", + field=models.ForeignKey( + related_name="comments", + to="gestioncof.EventRegistration", + on_delete=models.CASCADE, + ), ), migrations.AlterUniqueTogether( - name='surveyanswer', - unique_together=set([('user', 'survey')]), + name="surveyanswer", unique_together=set([("user", "survey")]) ), migrations.AlterUniqueTogether( - name='eventregistration', - unique_together=set([('user', 'event')]), + name="eventregistration", unique_together=set([("user", "event")]) ), ] diff --git a/gestioncof/migrations/0002_enable_unprocessed_demandes.py b/gestioncof/migrations/0002_enable_unprocessed_demandes.py index 18006588..d8514036 100644 --- a/gestioncof/migrations/0002_enable_unprocessed_demandes.py +++ b/gestioncof/migrations/0002_enable_unprocessed_demandes.py @@ -6,14 +6,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0001_initial'), - ] + dependencies = [("gestioncof", "0001_initial")] operations = [ migrations.AlterField( - model_name='petitcoursdemande', - name='processed', - field=models.DateTimeField(null=True, verbose_name='Date de traitement', blank=True), - ), + model_name="petitcoursdemande", + name="processed", + field=models.DateTimeField( + null=True, verbose_name="Date de traitement", blank=True + ), + ) ] diff --git a/gestioncof/migrations/0003_event_image.py b/gestioncof/migrations/0003_event_image.py index 6d65b1a6..ac5d753b 100644 --- a/gestioncof/migrations/0003_event_image.py +++ b/gestioncof/migrations/0003_event_image.py @@ -6,14 +6,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0002_enable_unprocessed_demandes'), - ] + dependencies = [("gestioncof", "0002_enable_unprocessed_demandes")] operations = [ migrations.AddField( - model_name='event', - name='image', - field=models.ImageField(upload_to=b'imgs/events/', null=True, verbose_name=b'Image', blank=True), - ), + model_name="event", + name="image", + field=models.ImageField( + upload_to=b"imgs/events/", null=True, verbose_name=b"Image", blank=True + ), + ) ] diff --git a/gestioncof/migrations/0004_registration_mail.py b/gestioncof/migrations/0004_registration_mail.py index d72900bf..0ceba245 100644 --- a/gestioncof/migrations/0004_registration_mail.py +++ b/gestioncof/migrations/0004_registration_mail.py @@ -8,27 +8,28 @@ def create_mail(apps, schema_editor): CustomMail = apps.get_model("gestioncof", "CustomMail") db_alias = schema_editor.connection.alias if CustomMail.objects.filter(shortname="bienvenue").count() == 0: - CustomMail.objects.using(db_alias).bulk_create([ - CustomMail( - shortname="bienvenue", - title="Bienvenue au COF", - content="Mail de bienvenue au COF, envoyé automatiquement à " \ - + "l'inscription.\n\n" \ - + "Les balises {{ ... }} sont interprétées comme expliqué " \ + CustomMail.objects.using(db_alias).bulk_create( + [ + CustomMail( + shortname="bienvenue", + title="Bienvenue au COF", + content="Mail de bienvenue au COF, envoyé automatiquement à " + + "l'inscription.\n\n" + + "Les balises {{ ... }} sont interprétées comme expliqué " + "ci-dessous à l'envoi.", - comments="{{ nom }} \t fullname de la personne.\n"\ - + "{{ prenom }} \t prénom de la personne.") - ]) + comments="{{ nom }} \t fullname de la personne.\n" + + "{{ prenom }} \t prénom de la personne.", + ) + ] + ) class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0003_event_image'), - ] + dependencies = [("gestioncof", "0003_event_image")] operations = [ # Pas besoin de supprimer le mail lors de la migration dans l'autre # sens. - migrations.RunPython(create_mail, migrations.RunPython.noop), + migrations.RunPython(create_mail, migrations.RunPython.noop) ] diff --git a/gestioncof/migrations/0005_encoding.py b/gestioncof/migrations/0005_encoding.py index 4f565a5d..33c8502a 100644 --- a/gestioncof/migrations/0005_encoding.py +++ b/gestioncof/migrations/0005_encoding.py @@ -6,62 +6,71 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0004_registration_mail'), - ] + dependencies = [("gestioncof", "0004_registration_mail")] operations = [ migrations.AlterModelOptions( - name='custommail', - options={'verbose_name': 'Mail personnalisable', 'verbose_name_plural': 'Mails personnalisables'}, + name="custommail", + options={ + "verbose_name": "Mail personnalisable", + "verbose_name_plural": "Mails personnalisables", + }, ), migrations.AlterModelOptions( - name='eventoptionchoice', - options={'verbose_name': 'Choix', 'verbose_name_plural': 'Choix'}, + name="eventoptionchoice", + options={"verbose_name": "Choix", "verbose_name_plural": "Choix"}, ), migrations.AlterField( - model_name='cofprofile', - name='is_buro', - field=models.BooleanField(default=False, verbose_name='Membre du Bur\xf4'), + model_name="cofprofile", + name="is_buro", + field=models.BooleanField(default=False, verbose_name="Membre du Bur\xf4"), ), migrations.AlterField( - model_name='cofprofile', - name='num', - field=models.IntegerField(default=0, verbose_name="Num\xe9ro d'adh\xe9rent", blank=True), + model_name="cofprofile", + name="num", + field=models.IntegerField( + default=0, verbose_name="Num\xe9ro d'adh\xe9rent", blank=True + ), ), migrations.AlterField( - model_name='cofprofile', - name='phone', - field=models.CharField(max_length=20, verbose_name='T\xe9l\xe9phone', blank=True), + model_name="cofprofile", + name="phone", + field=models.CharField( + max_length=20, verbose_name="T\xe9l\xe9phone", blank=True + ), ), migrations.AlterField( - model_name='event', - name='old', - field=models.BooleanField(default=False, verbose_name='Archiver (\xe9v\xe9nement fini)'), + model_name="event", + name="old", + field=models.BooleanField( + default=False, verbose_name="Archiver (\xe9v\xe9nement fini)" + ), ), migrations.AlterField( - model_name='event', - name='start_date', - field=models.DateField(null=True, verbose_name='Date de d\xe9but', blank=True), + model_name="event", + name="start_date", + field=models.DateField( + null=True, verbose_name="Date de d\xe9but", blank=True + ), ), migrations.AlterField( - model_name='eventcommentfield', - name='default', - field=models.TextField(verbose_name='Valeur par d\xe9faut', blank=True), + model_name="eventcommentfield", + name="default", + field=models.TextField(verbose_name="Valeur par d\xe9faut", blank=True), ), migrations.AlterField( - model_name='eventregistration', - name='paid', - field=models.BooleanField(default=False, verbose_name='A pay\xe9'), + model_name="eventregistration", + name="paid", + field=models.BooleanField(default=False, verbose_name="A pay\xe9"), ), migrations.AlterField( - model_name='survey', - name='details', - field=models.TextField(verbose_name='D\xe9tails', blank=True), + model_name="survey", + name="details", + field=models.TextField(verbose_name="D\xe9tails", blank=True), ), migrations.AlterField( - model_name='surveyquestionanswer', - name='answer', - field=models.CharField(max_length=200, verbose_name='R\xe9ponse'), + model_name="surveyquestionanswer", + name="answer", + field=models.CharField(max_length=200, verbose_name="R\xe9ponse"), ), ] diff --git a/gestioncof/migrations/0006_add_calendar.py b/gestioncof/migrations/0006_add_calendar.py index 27852f61..760fe56c 100644 --- a/gestioncof/migrations/0006_add_calendar.py +++ b/gestioncof/migrations/0006_add_calendar.py @@ -1,51 +1,66 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('bda', '0004_mails-rappel'), + ("bda", "0004_mails-rappel"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('gestioncof', '0005_encoding'), + ("gestioncof", "0005_encoding"), ] operations = [ migrations.CreateModel( - name='CalendarSubscription', + name="CalendarSubscription", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('token', models.UUIDField()), - ('subscribe_to_events', models.BooleanField(default=True)), - ('subscribe_to_my_shows', models.BooleanField(default=True)), - ('other_shows', models.ManyToManyField(to='bda.Spectacle')), - ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("token", models.UUIDField()), + ("subscribe_to_events", models.BooleanField(default=True)), + ("subscribe_to_my_shows", models.BooleanField(default=True)), + ("other_shows", models.ManyToManyField(to="bda.Spectacle")), + ( + "user", + models.OneToOneField( + to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), + ), ], ), migrations.AlterModelOptions( - name='custommail', - options={'verbose_name': 'Mail personnalisable', - 'verbose_name_plural': 'Mails personnalisables'}, + name="custommail", + options={ + "verbose_name": "Mail personnalisable", + "verbose_name_plural": "Mails personnalisables", + }, ), migrations.AlterModelOptions( - name='eventoptionchoice', - options={'verbose_name': 'Choix', 'verbose_name_plural': 'Choix'}, + name="eventoptionchoice", + options={"verbose_name": "Choix", "verbose_name_plural": "Choix"}, ), migrations.AlterField( - model_name='event', - name='end_date', - field=models.DateTimeField(null=True, verbose_name=b'Date de fin', - blank=True), - ), - migrations.AlterField( - model_name='event', - name='start_date', + model_name="event", + name="end_date", field=models.DateTimeField( - null=True, verbose_name=b'Date de d\xc3\xa9but', blank=True), + null=True, verbose_name=b"Date de fin", blank=True + ), + ), + migrations.AlterField( + model_name="event", + name="start_date", + field=models.DateTimeField( + null=True, verbose_name=b"Date de d\xc3\xa9but", blank=True + ), ), ] diff --git a/gestioncof/migrations/0007_alter_club.py b/gestioncof/migrations/0007_alter_club.py index 324c59a6..13603370 100644 --- a/gestioncof/migrations/0007_alter_club.py +++ b/gestioncof/migrations/0007_alter_club.py @@ -1,47 +1,44 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0006_add_calendar'), - ] + dependencies = [("gestioncof", "0006_add_calendar")] operations = [ migrations.AlterField( - model_name='club', - name='name', - field=models.CharField(unique=True, max_length=200, - verbose_name='Nom') + model_name="club", + name="name", + field=models.CharField(unique=True, max_length=200, verbose_name="Nom"), ), migrations.AlterField( - model_name='club', - name='description', - field=models.TextField(verbose_name='Description', blank=True) + model_name="club", + name="description", + field=models.TextField(verbose_name="Description", blank=True), ), migrations.AlterField( - model_name='club', - name='membres', - field=models.ManyToManyField(related_name='clubs', - to=settings.AUTH_USER_MODEL, - blank=True), + model_name="club", + name="membres", + field=models.ManyToManyField( + related_name="clubs", to=settings.AUTH_USER_MODEL, blank=True + ), ), migrations.AlterField( - model_name='club', - name='respos', - field=models.ManyToManyField(related_name='clubs_geres', - to=settings.AUTH_USER_MODEL, - blank=True), + model_name="club", + name="respos", + field=models.ManyToManyField( + related_name="clubs_geres", to=settings.AUTH_USER_MODEL, blank=True + ), ), migrations.AlterField( - model_name='event', - name='start_date', - field=models.DateTimeField(null=True, - verbose_name='Date de d\xe9but', - blank=True), + model_name="event", + name="start_date", + field=models.DateTimeField( + null=True, verbose_name="Date de d\xe9but", blank=True + ), ), ] diff --git a/gestioncof/migrations/0008_py3.py b/gestioncof/migrations/0008_py3.py index 7d94d7ce..f2b89aa4 100644 --- a/gestioncof/migrations/0008_py3.py +++ b/gestioncof/migrations/0008_py3.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models def forwards(apps, schema_editor): @@ -11,243 +11,266 @@ def forwards(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0007_alter_club'), - ] + dependencies = [("gestioncof", "0007_alter_club")] operations = [ migrations.AlterField( - model_name='clipper', - name='fullname', - field=models.CharField(verbose_name='Nom complet', max_length=200), + model_name="clipper", + name="fullname", + field=models.CharField(verbose_name="Nom complet", max_length=200), ), migrations.AlterField( - model_name='clipper', - name='username', - field=models.CharField(verbose_name='Identifiant', max_length=20), + model_name="clipper", + name="username", + field=models.CharField(verbose_name="Identifiant", max_length=20), ), migrations.AlterField( - model_name='cofprofile', - name='comments', + model_name="cofprofile", + name="comments", field=models.TextField( - verbose_name="Commentaires visibles par l'utilisateur", - blank=True), + verbose_name="Commentaires visibles par l'utilisateur", blank=True + ), ), migrations.AlterField( - model_name='cofprofile', - name='is_cof', - field=models.BooleanField(verbose_name='Membre du COF', - default=False), + model_name="cofprofile", + name="is_cof", + field=models.BooleanField(verbose_name="Membre du COF", default=False), ), migrations.AlterField( - model_name='cofprofile', - name='login_clipper', - field=models.CharField(verbose_name='Login clipper', max_length=8, - blank=True), + model_name="cofprofile", + name="login_clipper", + field=models.CharField( + verbose_name="Login clipper", max_length=8, blank=True + ), ), migrations.AlterField( - model_name='cofprofile', - name='mailing_bda', - field=models.BooleanField(verbose_name='Recevoir les mails BdA', - default=False), - ), - migrations.AlterField( - model_name='cofprofile', - name='mailing_bda_revente', + model_name="cofprofile", + name="mailing_bda", field=models.BooleanField( - verbose_name='Recevoir les mails de revente de places BdA', - default=False), + verbose_name="Recevoir les mails BdA", default=False + ), ), migrations.AlterField( - model_name='cofprofile', - name='mailing_cof', - field=models.BooleanField(verbose_name='Recevoir les mails COF', - default=False), + model_name="cofprofile", + name="mailing_bda_revente", + field=models.BooleanField( + verbose_name="Recevoir les mails de revente de places BdA", + default=False, + ), ), migrations.AlterField( - model_name='cofprofile', - name='occupation', - field=models.CharField(verbose_name='Occupation', - choices=[('exterieur', 'Extérieur'), - ('1A', '1A'), - ('2A', '2A'), - ('3A', '3A'), - ('4A', '4A'), - ('archicube', 'Archicube'), - ('doctorant', 'Doctorant'), - ('CST', 'CST')], - max_length=9, default='1A'), + model_name="cofprofile", + name="mailing_cof", + field=models.BooleanField( + verbose_name="Recevoir les mails COF", default=False + ), ), migrations.AlterField( - model_name='cofprofile', - name='petits_cours_accept', - field=models.BooleanField(verbose_name='Recevoir des petits cours', - default=False), + model_name="cofprofile", + name="occupation", + field=models.CharField( + verbose_name="Occupation", + choices=[ + ("exterieur", "Extérieur"), + ("1A", "1A"), + ("2A", "2A"), + ("3A", "3A"), + ("4A", "4A"), + ("archicube", "Archicube"), + ("doctorant", "Doctorant"), + ("CST", "CST"), + ], + max_length=9, + default="1A", + ), ), migrations.AlterField( - model_name='cofprofile', - name='petits_cours_remarques', + model_name="cofprofile", + name="petits_cours_accept", + field=models.BooleanField( + verbose_name="Recevoir des petits cours", default=False + ), + ), + migrations.AlterField( + model_name="cofprofile", + name="petits_cours_remarques", field=models.TextField( blank=True, - verbose_name='Remarques et précisions pour les petits cours', - default=''), + verbose_name="Remarques et précisions pour les petits cours", + default="", + ), ), migrations.AlterField( - model_name='cofprofile', - name='type_cotiz', + model_name="cofprofile", + name="type_cotiz", field=models.CharField( - verbose_name='Type de cotisation', - choices=[('etudiant', 'Normalien étudiant'), - ('normalien', 'Normalien élève'), - ('exterieur', 'Extérieur')], - max_length=9, default='normalien'), + verbose_name="Type de cotisation", + choices=[ + ("etudiant", "Normalien étudiant"), + ("normalien", "Normalien élève"), + ("exterieur", "Extérieur"), + ], + max_length=9, + default="normalien", + ), ), migrations.AlterField( - model_name='custommail', - name='comments', + model_name="custommail", + name="comments", field=models.TextField( - verbose_name='Informations contextuelles sur le mail', - blank=True), + verbose_name="Informations contextuelles sur le mail", blank=True + ), ), migrations.AlterField( - model_name='custommail', - name='content', - field=models.TextField(verbose_name='Contenu'), + model_name="custommail", + name="content", + field=models.TextField(verbose_name="Contenu"), ), migrations.AlterField( - model_name='custommail', - name='title', - field=models.CharField(verbose_name='Titre', max_length=200), + model_name="custommail", + name="title", + field=models.CharField(verbose_name="Titre", max_length=200), ), migrations.AlterField( - model_name='event', - name='description', - field=models.TextField(verbose_name='Description', blank=True), + model_name="event", + name="description", + field=models.TextField(verbose_name="Description", blank=True), ), migrations.AlterField( - model_name='event', - name='end_date', - field=models.DateTimeField(null=True, verbose_name='Date de fin', - blank=True), + model_name="event", + name="end_date", + field=models.DateTimeField( + null=True, verbose_name="Date de fin", blank=True + ), ), migrations.AlterField( - model_name='event', - name='image', - field=models.ImageField(upload_to='imgs/events/', null=True, - verbose_name='Image', blank=True), + model_name="event", + name="image", + field=models.ImageField( + upload_to="imgs/events/", null=True, verbose_name="Image", blank=True + ), ), migrations.AlterField( - model_name='event', - name='location', - field=models.CharField(verbose_name='Lieu', max_length=200), + model_name="event", + name="location", + field=models.CharField(verbose_name="Lieu", max_length=200), ), migrations.AlterField( - model_name='event', - name='registration_open', - field=models.BooleanField(verbose_name='Inscriptions ouvertes', - default=True), + model_name="event", + name="registration_open", + field=models.BooleanField( + verbose_name="Inscriptions ouvertes", default=True + ), ), migrations.AlterField( - model_name='event', - name='title', - field=models.CharField(verbose_name='Titre', max_length=200), + model_name="event", + name="title", + field=models.CharField(verbose_name="Titre", max_length=200), ), migrations.AlterField( - model_name='eventcommentfield', - name='fieldtype', - field=models.CharField(verbose_name='Type', - choices=[('text', 'Texte long'), - ('char', 'Texte court')], - max_length=10, default='text'), - ), - migrations.AlterField( - model_name='eventcommentfield', - name='name', - field=models.CharField(verbose_name='Champ', max_length=200), - ), - migrations.AlterField( - model_name='eventcommentvalue', - name='content', - field=models.TextField(null=True, verbose_name='Contenu', - blank=True), - ), - migrations.AlterField( - model_name='eventoption', - name='multi_choices', - field=models.BooleanField(verbose_name='Choix multiples', - default=False), - ), - migrations.AlterField( - model_name='eventoption', - name='name', - field=models.CharField(verbose_name='Option', max_length=200), - ), - migrations.AlterField( - model_name='eventoptionchoice', - name='value', - field=models.CharField(verbose_name='Valeur', max_length=200), - ), - migrations.AlterField( - model_name='petitcoursability', - name='niveau', + model_name="eventcommentfield", + name="fieldtype", field=models.CharField( - choices=[('college', 'Collège'), ('lycee', 'Lycée'), - ('prepa1styear', 'Prépa 1ère année / L1'), - ('prepa2ndyear', 'Prépa 2ème année / L2'), - ('licence3', 'Licence 3'), - ('other', 'Autre (préciser dans les commentaires)')], - max_length=12, verbose_name='Niveau'), + verbose_name="Type", + choices=[("text", "Texte long"), ("char", "Texte court")], + max_length=10, + default="text", + ), ), migrations.AlterField( - model_name='petitcoursattribution', - name='rank', + model_name="eventcommentfield", + name="name", + field=models.CharField(verbose_name="Champ", max_length=200), + ), + migrations.AlterField( + model_name="eventcommentvalue", + name="content", + field=models.TextField(null=True, verbose_name="Contenu", blank=True), + ), + migrations.AlterField( + model_name="eventoption", + name="multi_choices", + field=models.BooleanField(verbose_name="Choix multiples", default=False), + ), + migrations.AlterField( + model_name="eventoption", + name="name", + field=models.CharField(verbose_name="Option", max_length=200), + ), + migrations.AlterField( + model_name="eventoptionchoice", + name="value", + field=models.CharField(verbose_name="Valeur", max_length=200), + ), + migrations.AlterField( + model_name="petitcoursability", + name="niveau", + field=models.CharField( + choices=[ + ("college", "Collège"), + ("lycee", "Lycée"), + ("prepa1styear", "Prépa 1ère année / L1"), + ("prepa2ndyear", "Prépa 2ème année / L2"), + ("licence3", "Licence 3"), + ("other", "Autre (préciser dans les commentaires)"), + ], + max_length=12, + verbose_name="Niveau", + ), + ), + migrations.AlterField( + model_name="petitcoursattribution", + name="rank", field=models.IntegerField(verbose_name="Rang dans l'email"), ), migrations.AlterField( - model_name='petitcoursattributioncounter', - name='count', - field=models.IntegerField(verbose_name="Nombre d'envois", - default=0), + model_name="petitcoursattributioncounter", + name="count", + field=models.IntegerField(verbose_name="Nombre d'envois", default=0), ), migrations.AlterField( - model_name='petitcoursdemande', - name='niveau', + model_name="petitcoursdemande", + name="niveau", field=models.CharField( - verbose_name='Niveau', - choices=[('college', 'Collège'), ('lycee', 'Lycée'), - ('prepa1styear', 'Prépa 1ère année / L1'), - ('prepa2ndyear', 'Prépa 2ème année / L2'), - ('licence3', 'Licence 3'), - ('other', 'Autre (préciser dans les commentaires)')], - max_length=12, default=''), + verbose_name="Niveau", + choices=[ + ("college", "Collège"), + ("lycee", "Lycée"), + ("prepa1styear", "Prépa 1ère année / L1"), + ("prepa2ndyear", "Prépa 2ème année / L2"), + ("licence3", "Licence 3"), + ("other", "Autre (préciser dans les commentaires)"), + ], + max_length=12, + default="", + ), ), migrations.AlterField( - model_name='survey', - name='old', - field=models.BooleanField(verbose_name='Archiver (sondage fini)', - default=False), + model_name="survey", + name="old", + field=models.BooleanField( + verbose_name="Archiver (sondage fini)", default=False + ), ), migrations.AlterField( - model_name='survey', - name='survey_open', - field=models.BooleanField(verbose_name='Sondage ouvert', - default=True), + model_name="survey", + name="survey_open", + field=models.BooleanField(verbose_name="Sondage ouvert", default=True), ), migrations.AlterField( - model_name='survey', - name='title', - field=models.CharField(verbose_name='Titre', max_length=200), + model_name="survey", + name="title", + field=models.CharField(verbose_name="Titre", max_length=200), ), migrations.AlterField( - model_name='surveyquestion', - name='multi_answers', - field=models.BooleanField(verbose_name='Choix multiples', - default=False), + model_name="surveyquestion", + name="multi_answers", + field=models.BooleanField(verbose_name="Choix multiples", default=False), ), migrations.AlterField( - model_name='surveyquestion', - name='question', - field=models.CharField(verbose_name='Question', max_length=200), + model_name="surveyquestion", + name="question", + field=models.CharField(verbose_name="Question", max_length=200), ), migrations.RunPython(forwards, migrations.RunPython.noop), ] diff --git a/gestioncof/migrations/0009_delete_clipper.py b/gestioncof/migrations/0009_delete_clipper.py index e537107b..35362716 100644 --- a/gestioncof/migrations/0009_delete_clipper.py +++ b/gestioncof/migrations/0009_delete_clipper.py @@ -6,12 +6,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0008_py3'), - ] + dependencies = [("gestioncof", "0008_py3")] - operations = [ - migrations.DeleteModel( - name='Clipper', - ), - ] + operations = [migrations.DeleteModel(name="Clipper")] diff --git a/gestioncof/migrations/0010_delete_custommail.py b/gestioncof/migrations/0010_delete_custommail.py index 63ebeca7..2434faf2 100644 --- a/gestioncof/migrations/0010_delete_custommail.py +++ b/gestioncof/migrations/0010_delete_custommail.py @@ -5,12 +5,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0009_delete_clipper'), - ] + dependencies = [("gestioncof", "0009_delete_clipper")] - operations = [ - migrations.DeleteModel( - name='CustomMail', - ), - ] + operations = [migrations.DeleteModel(name="CustomMail")] diff --git a/gestioncof/migrations/0011_longer_clippers.py b/gestioncof/migrations/0011_longer_clippers.py index 631d0ea8..777f79a8 100644 --- a/gestioncof/migrations/0011_longer_clippers.py +++ b/gestioncof/migrations/0011_longer_clippers.py @@ -6,14 +6,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0010_delete_custommail'), - ] + dependencies = [("gestioncof", "0010_delete_custommail")] operations = [ migrations.AlterField( - model_name='cofprofile', - name='login_clipper', - field=models.CharField(verbose_name='Login clipper', blank=True, max_length=32), - ), + model_name="cofprofile", + name="login_clipper", + field=models.CharField( + verbose_name="Login clipper", blank=True, max_length=32 + ), + ) ] diff --git a/gestioncof/migrations/0011_remove_cofprofile_num.py b/gestioncof/migrations/0011_remove_cofprofile_num.py index f39ce367..abf97768 100644 --- a/gestioncof/migrations/0011_remove_cofprofile_num.py +++ b/gestioncof/migrations/0011_remove_cofprofile_num.py @@ -6,13 +6,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0010_delete_custommail'), - ] + dependencies = [("gestioncof", "0010_delete_custommail")] - operations = [ - migrations.RemoveField( - model_name='cofprofile', - name='num', - ), - ] + operations = [migrations.RemoveField(model_name="cofprofile", name="num")] diff --git a/gestioncof/migrations/0012_merge.py b/gestioncof/migrations/0012_merge.py index 39879346..5e23119d 100644 --- a/gestioncof/migrations/0012_merge.py +++ b/gestioncof/migrations/0012_merge.py @@ -7,9 +7,8 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('gestioncof', '0011_remove_cofprofile_num'), - ('gestioncof', '0011_longer_clippers'), + ("gestioncof", "0011_remove_cofprofile_num"), + ("gestioncof", "0011_longer_clippers"), ] - operations = [ - ] + operations = [] diff --git a/gestioncof/migrations/0013_pei.py b/gestioncof/migrations/0013_pei.py index 2fbddf1f..186d458d 100644 --- a/gestioncof/migrations/0013_pei.py +++ b/gestioncof/migrations/0013_pei.py @@ -6,42 +6,42 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0012_merge'), - ] + dependencies = [("gestioncof", "0012_merge")] operations = [ migrations.AlterField( - model_name='cofprofile', - name='occupation', + model_name="cofprofile", + name="occupation", field=models.CharField( - verbose_name='Occupation', + verbose_name="Occupation", max_length=9, - default='1A', + default="1A", choices=[ - ('exterieur', 'Extérieur'), - ('1A', '1A'), - ('2A', '2A'), - ('3A', '3A'), - ('4A', '4A'), - ('archicube', 'Archicube'), - ('doctorant', 'Doctorant'), - ('CST', 'CST'), - ('PEI', 'PEI') - ]), + ("exterieur", "Extérieur"), + ("1A", "1A"), + ("2A", "2A"), + ("3A", "3A"), + ("4A", "4A"), + ("archicube", "Archicube"), + ("doctorant", "Doctorant"), + ("CST", "CST"), + ("PEI", "PEI"), + ], + ), ), migrations.AlterField( - model_name='cofprofile', - name='type_cotiz', + model_name="cofprofile", + name="type_cotiz", field=models.CharField( - verbose_name='Type de cotisation', + verbose_name="Type de cotisation", max_length=9, - default='normalien', + default="normalien", choices=[ - ('etudiant', 'Normalien étudiant'), - ('normalien', 'Normalien élève'), - ('exterieur', 'Extérieur'), - ('gratis', 'Gratuit') - ]), + ("etudiant", "Normalien étudiant"), + ("normalien", "Normalien élève"), + ("exterieur", "Extérieur"), + ("gratis", "Gratuit"), + ], + ), ), ] diff --git a/gestioncof/migrations/0014_cofprofile_mailing_unernestaparis.py b/gestioncof/migrations/0014_cofprofile_mailing_unernestaparis.py index 1d842329..b849bfca 100644 --- a/gestioncof/migrations/0014_cofprofile_mailing_unernestaparis.py +++ b/gestioncof/migrations/0014_cofprofile_mailing_unernestaparis.py @@ -7,14 +7,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0013_pei'), - ] + dependencies = [("gestioncof", "0013_pei")] operations = [ migrations.AddField( - model_name='cofprofile', - name='mailing_unernestaparis', - field=models.BooleanField(default=False, verbose_name='Recevoir les mails unErnestAParis'), - ), + model_name="cofprofile", + name="mailing_unernestaparis", + field=models.BooleanField( + default=False, verbose_name="Recevoir les mails unErnestAParis" + ), + ) ] diff --git a/gestioncof/models.py b/gestioncof/models.py index 8a5b6a53..227fa936 100644 --- a/gestioncof/models.py +++ b/gestioncof/models.py @@ -1,17 +1,13 @@ -from django.db import models -from django.dispatch import receiver from django.contrib.auth.models import User +from django.db import models +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ -from django.db.models.signals import post_save, post_delete - -from gestioncof.petits_cours_models import choices_length from bda.models import Spectacle +from gestioncof.petits_cours_models import choices_length -TYPE_COMMENT_FIELD = ( - ('text', _("Texte long")), - ('char', _("Texte court")), -) +TYPE_COMMENT_FIELD = (("text", _("Texte long")), ("char", _("Texte court"))) class CofProfile(models.Model): @@ -49,40 +45,39 @@ class CofProfile(models.Model): (COTIZ_GRATIS, _("Gratuit")), ) - user = models.OneToOneField( - User, on_delete=models.CASCADE, - related_name="profile", - ) - login_clipper = models.CharField( - "Login clipper", max_length=32, blank=True - ) + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="profile") + login_clipper = models.CharField("Login clipper", max_length=32, blank=True) is_cof = models.BooleanField("Membre du COF", default=False) phone = models.CharField("Téléphone", max_length=20, blank=True) - occupation = models.CharField(_("Occupation"), - default="1A", - choices=OCCUPATION_CHOICES, - max_length=choices_length( - OCCUPATION_CHOICES)) - departement = models.CharField(_("Département"), max_length=50, - blank=True) - type_cotiz = models.CharField(_("Type de cotisation"), - default="normalien", - choices=TYPE_COTIZ_CHOICES, - max_length=choices_length( - TYPE_COTIZ_CHOICES)) + occupation = models.CharField( + _("Occupation"), + default="1A", + choices=OCCUPATION_CHOICES, + max_length=choices_length(OCCUPATION_CHOICES), + ) + departement = models.CharField(_("Département"), max_length=50, blank=True) + type_cotiz = models.CharField( + _("Type de cotisation"), + default="normalien", + choices=TYPE_COTIZ_CHOICES, + max_length=choices_length(TYPE_COTIZ_CHOICES), + ) mailing_cof = models.BooleanField("Recevoir les mails COF", default=False) mailing_bda = models.BooleanField("Recevoir les mails BdA", default=False) - mailing_unernestaparis = models.BooleanField("Recevoir les mails unErnestAParis", default=False) + mailing_unernestaparis = models.BooleanField( + "Recevoir les mails unErnestAParis", default=False + ) mailing_bda_revente = models.BooleanField( - "Recevoir les mails de revente de places BdA", default=False) - comments = models.TextField( - "Commentaires visibles par l'utilisateur", blank=True) + "Recevoir les mails de revente de places BdA", default=False + ) + comments = models.TextField("Commentaires visibles par l'utilisateur", blank=True) is_buro = models.BooleanField("Membre du Burô", default=False) petits_cours_accept = models.BooleanField( - "Recevoir des petits cours", default=False) + "Recevoir des petits cours", default=False + ) petits_cours_remarques = models.TextField( - _("Remarques et précisions pour les petits cours"), - blank=True, default="") + _("Remarques et précisions pour les petits cours"), blank=True, default="" + ) class Meta: verbose_name = "Profil COF" @@ -106,8 +101,7 @@ def post_delete_user(sender, instance, *args, **kwargs): class Club(models.Model): name = models.CharField("Nom", max_length=200, unique=True) description = models.TextField("Description", blank=True) - respos = models.ManyToManyField(User, related_name="clubs_geres", - blank=True) + respos = models.ManyToManyField(User, related_name="clubs_geres", blank=True) membres = models.ManyToManyField(User, related_name="clubs", blank=True) def __str__(self): @@ -120,10 +114,8 @@ class Event(models.Model): start_date = models.DateTimeField("Date de début", blank=True, null=True) end_date = models.DateTimeField("Date de fin", blank=True, null=True) description = models.TextField("Description", blank=True) - image = models.ImageField("Image", blank=True, null=True, - upload_to="imgs/events/") - registration_open = models.BooleanField("Inscriptions ouvertes", - default=True) + image = models.ImageField("Image", blank=True, null=True, upload_to="imgs/events/") + registration_open = models.BooleanField("Inscriptions ouvertes", default=True) old = models.BooleanField("Archiver (événement fini)", default=False) class Meta: @@ -135,12 +127,12 @@ class Event(models.Model): class EventCommentField(models.Model): event = models.ForeignKey( - Event, on_delete=models.CASCADE, - related_name="commentfields", + Event, on_delete=models.CASCADE, related_name="commentfields" ) name = models.CharField("Champ", max_length=200) - fieldtype = models.CharField("Type", max_length=10, - choices=TYPE_COMMENT_FIELD, default="text") + fieldtype = models.CharField( + "Type", max_length=10, choices=TYPE_COMMENT_FIELD, default="text" + ) default = models.TextField("Valeur par défaut", blank=True) class Meta: @@ -152,12 +144,10 @@ class EventCommentField(models.Model): class EventCommentValue(models.Model): commentfield = models.ForeignKey( - EventCommentField, on_delete=models.CASCADE, - related_name="values", + EventCommentField, on_delete=models.CASCADE, related_name="values" ) registration = models.ForeignKey( - "EventRegistration", on_delete=models.CASCADE, - related_name="comments", + "EventRegistration", on_delete=models.CASCADE, related_name="comments" ) content = models.TextField("Contenu", blank=True, null=True) @@ -166,10 +156,7 @@ class EventCommentValue(models.Model): class EventOption(models.Model): - event = models.ForeignKey( - Event, on_delete=models.CASCADE, - related_name="options", - ) + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="options") name = models.CharField("Option", max_length=200) multi_choices = models.BooleanField("Choix multiples", default=False) @@ -182,8 +169,7 @@ class EventOption(models.Model): class EventOptionChoice(models.Model): event_option = models.ForeignKey( - EventOption, on_delete=models.CASCADE, - related_name="choices", + EventOption, on_delete=models.CASCADE, related_name="choices" ) value = models.CharField("Valeur", max_length=200) @@ -199,8 +185,9 @@ class EventRegistration(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) event = models.ForeignKey(Event, on_delete=models.CASCADE) options = models.ManyToManyField(EventOptionChoice) - filledcomments = models.ManyToManyField(EventCommentField, - through=EventCommentValue) + filledcomments = models.ManyToManyField( + EventCommentField, through=EventCommentValue + ) paid = models.BooleanField("A payé", default=False) class Meta: @@ -226,8 +213,7 @@ class Survey(models.Model): class SurveyQuestion(models.Model): survey = models.ForeignKey( - Survey, on_delete=models.CASCADE, - related_name="questions", + Survey, on_delete=models.CASCADE, related_name="questions" ) question = models.CharField("Question", max_length=200) multi_answers = models.BooleanField("Choix multiples", default=False) @@ -241,8 +227,7 @@ class SurveyQuestion(models.Model): class SurveyQuestionAnswer(models.Model): survey_question = models.ForeignKey( - SurveyQuestion, on_delete=models.CASCADE, - related_name="answers", + SurveyQuestion, on_delete=models.CASCADE, related_name="answers" ) answer = models.CharField("Réponse", max_length=200) @@ -256,8 +241,7 @@ class SurveyQuestionAnswer(models.Model): class SurveyAnswer(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) survey = models.ForeignKey(Survey, on_delete=models.CASCADE) - answers = models.ManyToManyField(SurveyQuestionAnswer, - related_name="selected_by") + answers = models.ManyToManyField(SurveyQuestionAnswer, related_name="selected_by") class Meta: verbose_name = "Réponses" @@ -265,8 +249,9 @@ class SurveyAnswer(models.Model): def __str__(self): return "Réponse de %s sondage %s" % ( - self.user.get_full_name(), - self.survey.title) + self.user.get_full_name(), + self.survey.title, + ) class CalendarSubscription(models.Model): diff --git a/gestioncof/petits_cours_forms.py b/gestioncof/petits_cours_forms.py index e8f067bf..b9cfc067 100644 --- a/gestioncof/petits_cours_forms.py +++ b/gestioncof/petits_cours_forms.py @@ -1,11 +1,10 @@ from captcha.fields import ReCaptchaField - from django import forms -from django.forms import ModelForm -from django.forms.models import inlineformset_factory, BaseInlineFormSet from django.contrib.auth.models import User +from django.forms import ModelForm +from django.forms.models import BaseInlineFormSet, inlineformset_factory -from gestioncof.petits_cours_models import PetitCoursDemande, PetitCoursAbility +from gestioncof.petits_cours_models import PetitCoursAbility, PetitCoursDemande class BaseMatieresFormSet(BaseInlineFormSet): @@ -20,33 +19,44 @@ class BaseMatieresFormSet(BaseInlineFormSet): form = self.forms[i] if not form.cleaned_data: continue - matiere = form.cleaned_data['matiere'] - niveau = form.cleaned_data['niveau'] - delete = form.cleaned_data['DELETE'] + matiere = form.cleaned_data["matiere"] + niveau = form.cleaned_data["niveau"] + delete = form.cleaned_data["DELETE"] if not delete and (matiere, niveau) in matieres: raise forms.ValidationError( "Vous ne pouvez pas vous inscrire deux fois pour la " - "même matiere avec le même niveau.") + "même matiere avec le même niveau." + ) matieres.append((matiere, niveau)) class DemandeForm(ModelForm): - captcha = ReCaptchaField(attrs={'theme': 'clean', 'lang': 'fr'}) + captcha = ReCaptchaField(attrs={"theme": "clean", "lang": "fr"}) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['matieres'].help_text = '' + self.fields["matieres"].help_text = "" class Meta: model = PetitCoursDemande - fields = ('name', 'email', 'phone', 'quand', 'freq', 'lieu', - 'matieres', 'agrege_requis', 'niveau', 'remarques') - widgets = {'matieres': forms.CheckboxSelectMultiple} + fields = ( + "name", + "email", + "phone", + "quand", + "freq", + "lieu", + "matieres", + "agrege_requis", + "niveau", + "remarques", + ) + widgets = {"matieres": forms.CheckboxSelectMultiple} MatieresFormSet = inlineformset_factory( User, PetitCoursAbility, fields=("matiere", "niveau", "agrege"), - formset=BaseMatieresFormSet + formset=BaseMatieresFormSet, ) diff --git a/gestioncof/petits_cours_models.py b/gestioncof/petits_cours_models.py index 820f1292..40031877 100644 --- a/gestioncof/petits_cours_models.py +++ b/gestioncof/petits_cours_models.py @@ -1,28 +1,30 @@ from functools import reduce +from django.contrib.auth.models import User from django.db import models from django.db.models import Min -from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _ def choices_length(choices): return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) + LEVELS_CHOICES = ( - ('college', _("Collège")), - ('lycee', _("Lycée")), - ('prepa1styear', _("Prépa 1ère année / L1")), - ('prepa2ndyear', _("Prépa 2ème année / L2")), - ('licence3', _("Licence 3")), - ('other', _("Autre (préciser dans les commentaires)")), + ("college", _("Collège")), + ("lycee", _("Lycée")), + ("prepa1styear", _("Prépa 1ère année / L1")), + ("prepa2ndyear", _("Prépa 2ème année / L2")), + ("licence3", _("Licence 3")), + ("other", _("Autre (préciser dans les commentaires)")), ) class PetitCoursSubject(models.Model): name = models.CharField(_("Matière"), max_length=30) - users = models.ManyToManyField(User, related_name="petits_cours_matieres", - through="PetitCoursAbility") + users = models.ManyToManyField( + User, related_name="petits_cours_matieres", through="PetitCoursAbility" + ) class Meta: verbose_name = "Matière de petits cours" @@ -35,12 +37,11 @@ class PetitCoursSubject(models.Model): class PetitCoursAbility(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) matiere = models.ForeignKey( - PetitCoursSubject, on_delete=models.CASCADE, - verbose_name=_("Matière"), + PetitCoursSubject, on_delete=models.CASCADE, verbose_name=_("Matière") + ) + niveau = models.CharField( + _("Niveau"), choices=LEVELS_CHOICES, max_length=choices_length(LEVELS_CHOICES) ) - niveau = models.CharField(_("Niveau"), - choices=LEVELS_CHOICES, - max_length=choices_length(LEVELS_CHOICES)) agrege = models.BooleanField(_("Agrégé"), default=False) class Meta: @@ -56,41 +57,50 @@ class PetitCoursAbility(models.Model): class PetitCoursDemande(models.Model): name = models.CharField(_("Nom/prénom"), max_length=200) email = models.CharField(_("Adresse email"), max_length=300) - phone = models.CharField(_("Téléphone (facultatif)"), - max_length=20, blank=True) + phone = models.CharField(_("Téléphone (facultatif)"), max_length=20, blank=True) quand = models.CharField( _("Quand ?"), - help_text=_("Indiquez ici la période désirée pour les petits" - " cours (vacances scolaires, semaine, week-end)."), - max_length=300, blank=True) + help_text=_( + "Indiquez ici la période désirée pour les petits" + " cours (vacances scolaires, semaine, week-end)." + ), + max_length=300, + blank=True, + ) freq = models.CharField( _("Fréquence"), - help_text=_("Indiquez ici la fréquence envisagée " - "(hebdomadaire, 2 fois par semaine, ...)"), - max_length=300, blank=True) + help_text=_( + "Indiquez ici la fréquence envisagée " + "(hebdomadaire, 2 fois par semaine, ...)" + ), + max_length=300, + blank=True, + ) lieu = models.CharField( _("Lieu (si préférence)"), help_text=_("Si vous avez avez une préférence sur le lieu."), - max_length=300, blank=True) + max_length=300, + blank=True, + ) matieres = models.ManyToManyField( - PetitCoursSubject, verbose_name=_("Matières"), - related_name="demandes") + PetitCoursSubject, verbose_name=_("Matières"), related_name="demandes" + ) agrege_requis = models.BooleanField(_("Agrégé requis"), default=False) - niveau = models.CharField(_("Niveau"), - default="", - choices=LEVELS_CHOICES, - max_length=choices_length(LEVELS_CHOICES)) + niveau = models.CharField( + _("Niveau"), + default="", + choices=LEVELS_CHOICES, + max_length=choices_length(LEVELS_CHOICES), + ) remarques = models.TextField(_("Remarques et précisions"), blank=True) traitee = models.BooleanField(_("Traitée"), default=False) traitee_par = models.ForeignKey( - User, on_delete=models.CASCADE, - blank=True, null=True, + User, on_delete=models.CASCADE, blank=True, null=True ) - processed = models.DateTimeField(_("Date de traitement"), - blank=True, null=True) + processed = models.DateTimeField(_("Date de traitement"), blank=True, null=True) created = models.DateTimeField(_("Date de création"), auto_now_add=True) def get_candidates(self, redo=False): @@ -105,18 +115,15 @@ class PetitCoursDemande(models.Model): matiere=matiere, niveau=self.niveau, user__profile__is_cof=True, - user__profile__petits_cours_accept=True + user__profile__petits_cours_accept=True, ) if self.agrege_requis: candidates = candidates.filter(agrege=True) if redo: attrs = self.petitcoursattribution_set.filter(matiere=matiere) - already_proposed = [ - attr.user - for attr in attrs - ] + already_proposed = [attr.user for attr in attrs] candidates = candidates.exclude(user__in=already_proposed) - candidates = candidates.order_by('?').select_related().all() + candidates = candidates.order_by("?").select_related().all() yield (matiere, candidates) class Meta: @@ -124,25 +131,20 @@ class PetitCoursDemande(models.Model): verbose_name_plural = "Demandes de petits cours" def __str__(self): - return "Demande {:d} du {:s}".format( - self.id, self.created.strftime("%d %b %Y") - ) + return "Demande {:d} du {:s}".format(self.id, self.created.strftime("%d %b %Y")) class PetitCoursAttribution(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) demande = models.ForeignKey( - PetitCoursDemande, on_delete=models.CASCADE, - verbose_name=_("Demande"), + PetitCoursDemande, on_delete=models.CASCADE, verbose_name=_("Demande") ) matiere = models.ForeignKey( - PetitCoursSubject, on_delete=models.CASCADE, - verbose_name=_("Matière"), + PetitCoursSubject, on_delete=models.CASCADE, verbose_name=_("Matière") ) date = models.DateTimeField(_("Date d'attribution"), auto_now_add=True) rank = models.IntegerField("Rang dans l'email") - selected = models.BooleanField(_("Sélectionné par le demandeur"), - default=False) + selected = models.BooleanField(_("Sélectionné par le demandeur"), default=False) class Meta: verbose_name = "Attribution de petits cours" @@ -157,8 +159,7 @@ class PetitCoursAttribution(models.Model): class PetitCoursAttributionCounter(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) matiere = models.ForeignKey( - PetitCoursSubject, on_delete=models.CASCADE, - verbose_name=_("Matiere"), + PetitCoursSubject, on_delete=models.CASCADE, verbose_name=_("Matiere") ) count = models.IntegerField("Nombre d'envois", default=0) @@ -169,15 +170,12 @@ class PetitCoursAttributionCounter(models.Model): n'existe pas encore, il est initialisé avec le minimum des valeurs des compteurs de tout le monde. """ - counter, created = cls.objects.get_or_create( - user=user, - matiere=matiere, - ) + counter, created = cls.objects.get_or_create(user=user, matiere=matiere) if created: mincount = ( - cls.objects.filter(matiere=matiere).exclude(user=user) - .aggregate(Min('count')) - ['count__min'] + cls.objects.filter(matiere=matiere) + .exclude(user=user) + .aggregate(Min("count"))["count__min"] ) counter.count = mincount or 0 counter.save() diff --git a/gestioncof/petits_cours_views.py b/gestioncof/petits_cours_views.py index 6b8c8610..d640981a 100644 --- a/gestioncof/petits_cours_views.py +++ b/gestioncof/petits_cours_views.py @@ -1,31 +1,31 @@ import json + from custommail.shortcuts import render_custom_mail - -from django.shortcuts import render, get_object_or_404, redirect -from django.core import mail -from django.contrib.auth.models import User -from django.views.generic import ListView, DetailView -from django.views.decorators.csrf import csrf_exempt from django.conf import settings -from django.contrib.auth.decorators import login_required from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User +from django.core import mail from django.db import transaction +from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone +from django.views.decorators.csrf import csrf_exempt +from django.views.generic import DetailView, ListView -from gestioncof.models import CofProfile -from gestioncof.petits_cours_models import ( - PetitCoursDemande, PetitCoursAttribution, PetitCoursAttributionCounter, - PetitCoursAbility -) -from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet from gestioncof.decorators import buro_required +from gestioncof.models import CofProfile +from gestioncof.petits_cours_forms import DemandeForm, MatieresFormSet +from gestioncof.petits_cours_models import ( + PetitCoursAbility, + PetitCoursAttribution, + PetitCoursAttributionCounter, + PetitCoursDemande, +) class DemandeListView(ListView): - queryset = ( - PetitCoursDemande.objects - .prefetch_related('matieres') - .order_by('traitee', '-id') + queryset = PetitCoursDemande.objects.prefetch_related("matieres").order_by( + "traitee", "-id" ) template_name = "petits_cours_demandes_list.html" paginate_by = 20 @@ -33,10 +33,8 @@ class DemandeListView(ListView): class DemandeDetailView(DetailView): model = PetitCoursDemande - queryset = ( - PetitCoursDemande.objects - .prefetch_related('petitcoursattribution_set', - 'matieres') + queryset = PetitCoursDemande.objects.prefetch_related( + "petitcoursattribution_set", "matieres" ) template_name = "gestioncof/details_demande_petit_cours.html" context_object_name = "demande" @@ -44,7 +42,7 @@ class DemandeDetailView(DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) obj = self.object - context['attributions'] = obj.petitcoursattribution_set.all() + context["attributions"] = obj.petitcoursattribution_set.all() return context @@ -64,13 +62,15 @@ def traitement(request, demande_id, redo=False): tuples = [] for candidate in candidates: user = candidate.user - tuples.append(( - candidate, - PetitCoursAttributionCounter.get_uptodate(user, matiere) - )) + tuples.append( + ( + candidate, + PetitCoursAttributionCounter.get_uptodate(user, matiere), + ) + ) tuples = sorted(tuples, key=lambda c: c[1].count) candidates, _ = zip(*tuples) - candidates = candidates[0:min(3, len(candidates))] + candidates = candidates[0 : min(3, len(candidates))] attribdata[matiere.id] = [] proposals[matiere] = [] for candidate in candidates: @@ -83,8 +83,9 @@ def traitement(request, demande_id, redo=False): proposed_for[user].append(matiere) else: unsatisfied.append(matiere) - return _finalize_traitement(request, demande, proposals, - proposed_for, unsatisfied, attribdata, redo) + return _finalize_traitement( + request, demande, proposals, proposed_for, unsatisfied, attribdata, redo + ) @buro_required @@ -92,43 +93,56 @@ def retraitement(request, demande_id): return traitement(request, demande_id, redo=True) -def _finalize_traitement(request, demande, proposals, proposed_for, - unsatisfied, attribdata, redo=False, errors=None): +def _finalize_traitement( + request, + demande, + proposals, + proposed_for, + unsatisfied, + attribdata, + redo=False, + errors=None, +): proposals = proposals.items() proposed_for = proposed_for.items() attribdata = list(attribdata.items()) proposed_mails = _generate_eleve_email(demande, proposed_for) - mainmail = render_custom_mail("petits-cours-mail-demandeur", { - "proposals": proposals, - "unsatisfied": unsatisfied, - "extra": - '' - }) + "", + }, + ) if errors is not None: for error in errors: messages.error(request, error) - return render(request, "gestioncof/traitement_demande_petit_cours.html", - {"demande": demande, - "unsatisfied": unsatisfied, - "proposals": proposals, - "proposed_for": proposed_for, - "proposed_mails": proposed_mails, - "mainmail": mainmail, - "attribdata": json.dumps(attribdata), - "redo": redo, - }) + return render( + request, + "gestioncof/traitement_demande_petit_cours.html", + { + "demande": demande, + "unsatisfied": unsatisfied, + "proposals": proposals, + "proposed_for": proposed_for, + "proposed_mails": proposed_mails, + "mainmail": mainmail, + "attribdata": json.dumps(attribdata), + "redo": redo, + }, + ) def _generate_eleve_email(demande, proposed_for): return [ ( user, - render_custom_mail('petit-cours-mail-eleve', { - "demande": demande, - "matieres": matieres - }) + render_custom_mail( + "petit-cours-mail-eleve", {"demande": demande, "matieres": matieres} + ), ) for user, matieres in proposed_for ] @@ -143,25 +157,30 @@ def _traitement_other_preparing(request, demande): errors = [] for matiere, candidates in demande.get_candidates(redo): if candidates: - candidates = dict([(candidate.user.id, candidate.user) - for candidate in candidates]) + candidates = dict( + [(candidate.user.id, candidate.user) for candidate in candidates] + ) attribdata[matiere.id] = [] proposals[matiere] = [] for choice_id in range(min(3, len(candidates))): choice = int( - request.POST["proposal-{:d}-{:d}" - .format(matiere.id, choice_id)] + request.POST["proposal-{:d}-{:d}".format(matiere.id, choice_id)] ) if choice == -1: continue if choice not in candidates: - errors.append("Choix invalide pour la proposition {:d}" - "en {!s}".format(choice_id + 1, matiere)) + errors.append( + "Choix invalide pour la proposition {:d}" + "en {!s}".format(choice_id + 1, matiere) + ) continue user = candidates[choice] if user in proposals[matiere]: - errors.append("La proposition {:d} en {!s} est un doublon" - .format(choice_id + 1, matiere)) + errors.append( + "La proposition {:d} en {!s} est un doublon".format( + choice_id + 1, matiere + ) + ) continue proposals[matiere].append(user) attribdata[matiere.id].append(user.id) @@ -172,15 +191,24 @@ def _traitement_other_preparing(request, demande): if not proposals[matiere]: errors.append("Aucune proposition pour {!s}".format(matiere)) elif len(proposals[matiere]) < 3: - errors.append("Seulement {:d} proposition{:s} pour {!s}" - .format( - len(proposals[matiere]), - "s" if len(proposals[matiere]) > 1 else "", - matiere)) + errors.append( + "Seulement {:d} proposition{:s} pour {!s}".format( + len(proposals[matiere]), + "s" if len(proposals[matiere]) > 1 else "", + matiere, + ) + ) else: unsatisfied.append(matiere) - return _finalize_traitement(request, demande, proposals, proposed_for, - unsatisfied, attribdata, errors=errors) + return _finalize_traitement( + request, + demande, + proposals, + proposed_for, + unsatisfied, + attribdata, + errors=errors, + ) def _traitement_other(request, demande, redo): @@ -198,10 +226,12 @@ def _traitement_other(request, demande, redo): tuples = [] for candidate in candidates: user = candidate.user - tuples.append(( - candidate, - PetitCoursAttributionCounter.get_uptodate(user, matiere) - )) + tuples.append( + ( + candidate, + PetitCoursAttributionCounter.get_uptodate(user, matiere), + ) + ) tuples = sorted(tuples, key=lambda c: c[1].count) candidates, _ = zip(*tuples) attribdata[matiere.id] = [] @@ -218,13 +248,16 @@ def _traitement_other(request, demande, redo): unsatisfied.append(matiere) proposals = proposals.items() proposed_for = proposed_for.items() - return render(request, - "gestioncof/traitement_demande_petit_cours_autre_niveau.html", - {"demande": demande, - "unsatisfied": unsatisfied, - "proposals": proposals, - "proposed_for": proposed_for, - }) + return render( + request, + "gestioncof/traitement_demande_petit_cours_autre_niveau.html", + { + "demande": demande, + "unsatisfied": unsatisfied, + "proposals": proposals, + "proposed_for": proposed_for, + }, + ) def _traitement_post(request, demande): @@ -252,24 +285,32 @@ def _traitement_post(request, demande): proposed_mails = _generate_eleve_email(demande, proposed_for) mainmail_object, mainmail_body = render_custom_mail( "petits-cours-mail-demandeur", - { - "proposals": proposals_list, - "unsatisfied": unsatisfied, - "extra": extra - } + {"proposals": proposals_list, "unsatisfied": unsatisfied, "extra": extra}, ) - frommail = settings.MAIL_DATA['petits_cours']['FROM'] - bccaddress = settings.MAIL_DATA['petits_cours']['BCC'] - replyto = settings.MAIL_DATA['petits_cours']['REPLYTO'] + frommail = settings.MAIL_DATA["petits_cours"]["FROM"] + bccaddress = settings.MAIL_DATA["petits_cours"]["BCC"] + replyto = settings.MAIL_DATA["petits_cours"]["REPLYTO"] mails_to_send = [] for (user, (mail_object, body)) in proposed_mails: - msg = mail.EmailMessage(mail_object, body, frommail, [user.email], - [bccaddress], headers={'Reply-To': replyto}) + msg = mail.EmailMessage( + mail_object, + body, + frommail, + [user.email], + [bccaddress], + headers={"Reply-To": replyto}, + ) mails_to_send.append(msg) - mails_to_send.append(mail.EmailMessage(mainmail_object, mainmail_body, - frommail, [demande.email], - [bccaddress], - headers={'Reply-To': replyto})) + mails_to_send.append( + mail.EmailMessage( + mainmail_object, + mainmail_body, + frommail, + [demande.email], + [bccaddress], + headers={"Reply-To": replyto}, + ) + ) connection = mail.get_connection(fail_silently=False) connection.send_messages(mails_to_send) with transaction.atomic(): @@ -280,18 +321,19 @@ def _traitement_post(request, demande): ) counter.count += 1 counter.save() - attrib = PetitCoursAttribution(user=user, matiere=matiere, - demande=demande, rank=rank + 1) + attrib = PetitCoursAttribution( + user=user, matiere=matiere, demande=demande, rank=rank + 1 + ) attrib.save() demande.traitee = True demande.traitee_par = request.user demande.processed = timezone.now() demande.save() - return render(request, - "gestioncof/traitement_demande_petit_cours_success.html", - {"demande": demande, - "redo": redo, - }) + return render( + request, + "gestioncof/traitement_demande_petit_cours_success.html", + {"demande": demande, "redo": redo}, + ) @login_required @@ -308,22 +350,25 @@ def inscription(request): profile.petits_cours_remarques = request.POST["remarques"] profile.save() with transaction.atomic(): - abilities = ( - PetitCoursAbility.objects.filter(user=request.user).all() - ) + abilities = PetitCoursAbility.objects.filter(user=request.user).all() for ability in abilities: PetitCoursAttributionCounter.get_uptodate( - ability.user, - ability.matiere + ability.user, ability.matiere ) success = True formset = MatieresFormSet(instance=request.user) else: formset = MatieresFormSet(instance=request.user) - return render(request, "inscription-petit-cours.html", - {"formset": formset, "success": success, - "receive_proposals": profile.petits_cours_accept, - "remarques": profile.petits_cours_remarques}) + return render( + request, + "inscription-petit-cours.html", + { + "formset": formset, + "success": success, + "receive_proposals": profile.petits_cours_accept, + "remarques": profile.petits_cours_remarques, + }, + ) @csrf_exempt @@ -336,8 +381,9 @@ def demande(request): success = True else: form = DemandeForm() - return render(request, "demande-petit-cours.html", {"form": form, - "success": success}) + return render( + request, "demande-petit-cours.html", {"form": form, "success": success} + ) @csrf_exempt @@ -350,5 +396,6 @@ def demande_raw(request): success = True else: form = DemandeForm() - return render(request, "demande-petit-cours-raw.html", - {"form": form, "success": success}) + return render( + request, "demande-petit-cours-raw.html", {"form": form, "success": success} + ) diff --git a/gestioncof/shared.py b/gestioncof/shared.py index fdab9a45..87e19842 100644 --- a/gestioncof/shared.py +++ b/gestioncof/shared.py @@ -1,13 +1,11 @@ from django.conf import settings from django.contrib.sites.models import Site - from django_cas_ng.backends import CASBackend from gestioncof.models import CofProfile class COFCASBackend(CASBackend): - def clean_username(self, username): # Le CAS de l'ENS accepte les logins avec des espaces au début # et à la fin, ainsi qu’avec une casse variable. On normalise pour @@ -24,9 +22,6 @@ class COFCASBackend(CASBackend): def context_processor(request): - '''Append extra data to the context of the given request''' - data = { - "user": request.user, - "site": Site.objects.get_current(), - } + """Append extra data to the context of the given request""" + data = {"user": request.user, "site": Site.objects.get_current()} return data diff --git a/gestioncof/signals.py b/gestioncof/signals.py index 11cb55fc..3614b1c8 100644 --- a/gestioncof/signals.py +++ b/gestioncof/signals.py @@ -2,22 +2,21 @@ from django.contrib import messages from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ - from django_cas_ng.signals import cas_user_authenticated @receiver(user_logged_in) def messages_on_out_login(request, user, **kwargs): - if user.backend.startswith('django.contrib.auth'): - msg = _('Connexion à GestioCOF réussie. Bienvenue {}.').format( - user.get_short_name(), + if user.backend.startswith("django.contrib.auth"): + msg = _("Connexion à GestioCOF réussie. Bienvenue {}.").format( + user.get_short_name() ) messages.success(request, msg) @receiver(cas_user_authenticated) def mesagges_on_cas_login(request, user, **kwargs): - msg = _('Connexion à GestioCOF par CAS réussie. Bienvenue {}.').format( - user.get_short_name(), + msg = _("Connexion à GestioCOF par CAS réussie. Bienvenue {}.").format( + user.get_short_name() ) messages.success(request, msg) diff --git a/gestioncof/templatetags/utils.py b/gestioncof/templatetags/utils.py index 2b732aec..21518614 100644 --- a/gestioncof/templatetags/utils.py +++ b/gestioncof/templatetags/utils.py @@ -1,8 +1,8 @@ +import re + from django import template from django.utils.safestring import mark_safe -import re - register = template.Library() @@ -12,6 +12,7 @@ def key(d, key_name): value = d[key_name] except KeyError: from django.conf import settings + value = settings.TEMPLATE_STRING_IF_INVALID return value @@ -19,16 +20,15 @@ def key(d, key_name): def highlight_text(text, q): q2 = "|".join(re.escape(word) for word in q.split()) pattern = re.compile(r"(?P%s)" % q2, re.IGNORECASE) - return mark_safe(re.sub(pattern, - r"\g", - text)) + return mark_safe( + re.sub(pattern, r"\g", text) + ) @register.filter def highlight_user(user, q): if user.first_name and user.last_name: - text = "%s %s (%s)" % (user.first_name, user.last_name, - user.username) + text = "%s %s (%s)" % (user.first_name, user.last_name, user.username) else: text = user.username return highlight_text(text, q) diff --git a/gestioncof/tests/test_legacy.py b/gestioncof/tests/test_legacy.py index 85673edd..cc7ddbf7 100644 --- a/gestioncof/tests/test_legacy.py +++ b/gestioncof/tests/test_legacy.py @@ -12,17 +12,17 @@ from gestioncof.models import CofProfile, User class SimpleTest(TestCase): def test_delete_user(self): - u = User(username='foo', first_name='foo', last_name='bar') + u = User(username="foo", first_name="foo", last_name="bar") # to each user there's a cofprofile associated u.save() - self.assertTrue(CofProfile.objects.filter(user__username='foo').exists()) + self.assertTrue(CofProfile.objects.filter(user__username="foo").exists()) # there's no point in having a cofprofile without a user associated. u.delete() - self.assertFalse(CofProfile.objects.filter(user__username='foo').exists()) + self.assertFalse(CofProfile.objects.filter(user__username="foo").exists()) # there's no point in having a user without a cofprofile associated. u.save() - CofProfile.objects.get(user__username='foo').delete() - self.assertFalse(User.objects.filter(username='foo').exists()) + CofProfile.objects.get(user__username="foo").delete() + self.assertFalse(User.objects.filter(username="foo").exists()) diff --git a/gestioncof/tests/test_views.py b/gestioncof/tests/test_views.py index 1ab6a5f4..d65247c1 100644 --- a/gestioncof/tests/test_views.py +++ b/gestioncof/tests/test_views.py @@ -2,6 +2,7 @@ import csv import uuid from datetime import timedelta +from custommail.models import CustomMail from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.messages.api import get_messages @@ -13,29 +14,25 @@ from django.urls import reverse from bda.models import Salle, Tirage from gestioncof.autocomplete import Clipper -from gestioncof.models import ( - CalendarSubscription, Club, Event, Survey, SurveyAnswer -) +from gestioncof.models import CalendarSubscription, Club, Event, Survey, SurveyAnswer from gestioncof.tests.testcases import ViewTestCaseMixin -from custommail.models import CustomMail - from .utils import create_member, create_root, create_user User = get_user_model() class RegistrationViewTests(ViewTestCaseMixin, TestCase): - url_name = 'registration' - url_expected = '/registration' + url_name = "registration" + url_expected = "/registration" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def requires_mails(self): - call_command('syncmails', verbosity=0) + call_command("syncmails", verbosity=0) def test_get(self): r = self.client.get(self.url) @@ -44,96 +41,108 @@ class RegistrationViewTests(ViewTestCaseMixin, TestCase): @property def _minimal_data(self): return { - 'first_name': '', - 'last_name': '', - 'email': '', - + "first_name": "", + "last_name": "", + "email": "", # 'is_cof': '1', - 'login_clipper': '', - 'phone': '', - 'occupation': '1A', - 'departement': '', - 'type_cotiz': 'normalien', - 'comments': '', - + "login_clipper": "", + "phone": "", + "occupation": "1A", + "departement": "", + "type_cotiz": "normalien", + "comments": "", # 'user_exists': '1', - - 'events-TOTAL_FORMS': '0', - 'events-INITIAL_FORMS': '0', - 'events-MIN_NUM_FORMS': '0', - 'events-MAX_NUM_FORMS': '1000', + "events-TOTAL_FORMS": "0", + "events-INITIAL_FORMS": "0", + "events-MIN_NUM_FORMS": "0", + "events-MAX_NUM_FORMS": "1000", } def test_post_new(self): self.requires_mails() - r = self.client.post(self.url, dict(self._minimal_data, **{ - 'username': 'username', - 'first_name': 'first', - 'last_name': 'last', - 'email': 'username@mail.net', - 'is_cof': '1', - })) + r = self.client.post( + self.url, + dict( + self._minimal_data, + **{ + "username": "username", + "first_name": "first", + "last_name": "last", + "email": "username@mail.net", + "is_cof": "1", + } + ), + ) self.assertEqual(r.status_code, 200) - u = User.objects.get(username='username') - expected_message = Message(messages.SUCCESS, ( - "L'inscription de first last (username@mail.net) a été " - "enregistrée avec succès.\n" - "Il est désormais membre du COF n°{} !" - .format(u.pk) - )) + u = User.objects.get(username="username") + expected_message = Message( + messages.SUCCESS, + ( + "L'inscription de first last (username@mail.net) a été " + "enregistrée avec succès.\n" + "Il est désormais membre du COF n°{} !".format(u.pk) + ), + ) self.assertIn(expected_message, get_messages(r.wsgi_request)) - self.assertEqual(u.first_name, 'first') - self.assertEqual(u.last_name, 'last') - self.assertEqual(u.email, 'username@mail.net') + self.assertEqual(u.first_name, "first") + self.assertEqual(u.last_name, "last") + self.assertEqual(u.email, "username@mail.net") def test_post_edit(self): self.requires_mails() - u = self.users['user'] + u = self.users["user"] - r = self.client.post(self.url, dict(self._minimal_data, **{ - 'username': 'user', - 'first_name': 'first', - 'last_name': 'last', - 'email': 'user@mail.net', - 'is_cof': '1', - 'user_exists': '1', - })) + r = self.client.post( + self.url, + dict( + self._minimal_data, + **{ + "username": "user", + "first_name": "first", + "last_name": "last", + "email": "user@mail.net", + "is_cof": "1", + "user_exists": "1", + } + ), + ) self.assertEqual(r.status_code, 200) u.refresh_from_db() - expected_message = Message(messages.SUCCESS, ( - "L'inscription de first last (user@mail.net) a été " - "enregistrée avec succès.\n" - "Il est désormais membre du COF n°{} !" - .format(u.pk) - )) + expected_message = Message( + messages.SUCCESS, + ( + "L'inscription de first last (user@mail.net) a été " + "enregistrée avec succès.\n" + "Il est désormais membre du COF n°{} !".format(u.pk) + ), + ) self.assertIn(expected_message, get_messages(r.wsgi_request)) - self.assertEqual(u.first_name, 'first') - self.assertEqual(u.last_name, 'last') - self.assertEqual(u.email, 'user@mail.net') + self.assertEqual(u.first_name, "first") + self.assertEqual(u.last_name, "last") + self.assertEqual(u.email, "user@mail.net") def _test_mail_welcome(self, was_cof, is_cof, expect_mail): self.requires_mails() - u = self.users['member'] if was_cof else self.users['user'] + u = self.users["member"] if was_cof else self.users["user"] - data = dict(self._minimal_data, **{ - 'username': u.username, - 'email': 'user@mail.net', - 'user_exists': '1', - }) + data = dict( + self._minimal_data, + **{"username": u.username, "email": "user@mail.net", "user_exists": "1"} + ) if is_cof: - data['is_cof'] = '1' + data["is_cof"] = "1" self.client.post(self.url, data) u.refresh_from_db() def _is_sent(): - cm = CustomMail.objects.get(shortname='welcome') - welcome_msg = cm.get_message({'member': u}) + cm = CustomMail.objects.get(shortname="welcome") + welcome_msg = cm.get_message({"member": u}) for m in mail.outbox: if m.subject == welcome_msg.subject: return True @@ -156,197 +165,184 @@ class RegistrationViewTests(ViewTestCaseMixin, TestCase): def test_events(self): e = Event.objects.create() - cf1 = e.commentfields.create(name='Comment Field 1') - cf2 = e.commentfields.create( - name='Comment Field 2', fieldtype='char', + cf1 = e.commentfields.create(name="Comment Field 1") + cf2 = e.commentfields.create(name="Comment Field 2", fieldtype="char") + + o1 = e.options.create(name="Option 1") + o2 = e.options.create(name="Option 2", multi_choices=True) + + oc1 = o1.choices.create(value="O1 - Choice 1") + o1.choices.create(value="O1 - Choice 2") + oc3 = o2.choices.create(value="O2 - Choice 1") + o2.choices.create(value="O2 - Choice 2") + + self.client.post( + self.url, + dict( + self._minimal_data, + **{ + "username": "user", + "user_exists": "1", + "events-TOTAL_FORMS": "1", + "events-INITIAL_FORMS": "0", + "events-MIN_NUM_FORMS": "0", + "events-MAX_NUM_FORMS": "1000", + "events-0-status": "paid", + "events-0-option_{}".format(o1.pk): [str(oc1.pk)], + "events-0-option_{}".format(o2.pk): [str(oc3.pk)], + "events-0-comment_{}".format(cf1.pk): "comment 1", + "events-0-comment_{}".format(cf2.pk): "", + } + ), ) - o1 = e.options.create(name='Option 1') - o2 = e.options.create(name='Option 2', multi_choices=True) - - oc1 = o1.choices.create(value='O1 - Choice 1') - o1.choices.create(value='O1 - Choice 2') - oc3 = o2.choices.create(value='O2 - Choice 1') - o2.choices.create(value='O2 - Choice 2') - - self.client.post(self.url, dict(self._minimal_data, **{ - 'username': 'user', - 'user_exists': '1', - 'events-TOTAL_FORMS': '1', - 'events-INITIAL_FORMS': '0', - 'events-MIN_NUM_FORMS': '0', - 'events-MAX_NUM_FORMS': '1000', - 'events-0-status': 'paid', - 'events-0-option_{}'.format(o1.pk): [str(oc1.pk)], - 'events-0-option_{}'.format(o2.pk): [str(oc3.pk)], - 'events-0-comment_{}'.format(cf1.pk): 'comment 1', - 'events-0-comment_{}'.format(cf2.pk): '', - })) - - er = e.eventregistration_set.get(user=self.users['user']) - self.assertQuerysetEqual( - er.options.all(), map(repr, [oc1, oc3]), - ordered=False, + er = e.eventregistration_set.get(user=self.users["user"]) + self.assertQuerysetEqual(er.options.all(), map(repr, [oc1, oc3]), ordered=False) + self.assertCountEqual( + er.comments.values_list("content", flat=True), ["comment 1"] ) - self.assertCountEqual(er.comments.values_list('content', flat=True), [ - 'comment 1', - ]) class RegistrationFormViewTests(ViewTestCaseMixin, TestCase): urls_conf = [ + {"name": "empty-registration", "expected": "/registration/empty"}, { - 'name': 'empty-registration', - 'expected': '/registration/empty', + "name": "user-registration", + "kwargs": {"username": "user"}, + "expected": "/registration/user/user", }, { - 'name': 'user-registration', - 'kwargs': {'username': 'user'}, - 'expected': '/registration/user/user', - }, - { - 'name': 'clipper-registration', - 'kwargs': { - 'login_clipper': 'uid', - 'fullname': 'First Last1 Last2', - }, - 'expected': '/registration/clipper/uid/First%20Last1%20Last2', + "name": "clipper-registration", + "kwargs": {"login_clipper": "uid", "fullname": "First Last1 Last2"}, + "expected": "/registration/clipper/uid/First%20Last1%20Last2", }, ] - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test_empty(self): r = self.client.get(self.t_urls[0]) - self.assertIn('user_form', r.context) - self.assertIn('profile_form', r.context) - self.assertIn('event_formset', r.context) - self.assertIn('clubs_form', r.context) + self.assertIn("user_form", r.context) + self.assertIn("profile_form", r.context) + self.assertIn("event_formset", r.context) + self.assertIn("clubs_form", r.context) def test_username(self): - u = self.users['user'] - u.first_name = 'first' - u.last_name = 'last' + u = self.users["user"] + u.first_name = "first" + u.last_name = "last" u.save() r = self.client.get(self.t_urls[1]) - self.assertIn('user_form', r.context) - self.assertIn('profile_form', r.context) - self.assertIn('event_formset', r.context) - self.assertIn('clubs_form', r.context) - user_form = r.context['user_form'] - self.assertEqual(user_form['username'].initial, 'user') - self.assertEqual(user_form['first_name'].initial, 'first') - self.assertEqual(user_form['last_name'].initial, 'last') + self.assertIn("user_form", r.context) + self.assertIn("profile_form", r.context) + self.assertIn("event_formset", r.context) + self.assertIn("clubs_form", r.context) + user_form = r.context["user_form"] + self.assertEqual(user_form["username"].initial, "user") + self.assertEqual(user_form["first_name"].initial, "first") + self.assertEqual(user_form["last_name"].initial, "last") def test_clipper(self): r = self.client.get(self.t_urls[2]) - self.assertIn('user_form', r.context) - self.assertIn('profile_form', r.context) - self.assertIn('event_formset', r.context) - self.assertIn('clubs_form', r.context) - user_form = r.context['user_form'] - profile_form = r.context['profile_form'] - self.assertEqual(user_form['first_name'].initial, 'First') - self.assertEqual(user_form['last_name'].initial, 'Last1 Last2') - self.assertEqual(user_form['email'].initial, 'uid@clipper.ens.fr') - self.assertEqual(profile_form['login_clipper'].initial, 'uid') + self.assertIn("user_form", r.context) + self.assertIn("profile_form", r.context) + self.assertIn("event_formset", r.context) + self.assertIn("clubs_form", r.context) + user_form = r.context["user_form"] + profile_form = r.context["profile_form"] + self.assertEqual(user_form["first_name"].initial, "First") + self.assertEqual(user_form["last_name"].initial, "Last1 Last2") + self.assertEqual(user_form["email"].initial, "uid@clipper.ens.fr") + self.assertEqual(profile_form["login_clipper"].initial, "uid") -@override_settings(LDAP_SERVER_URL='ldap_url') +@override_settings(LDAP_SERVER_URL="ldap_url") class RegistrationAutocompleteViewTests(ViewTestCaseMixin, TestCase): - url_name = 'cof.registration.autocomplete' - url_expected = '/autocomplete/registration' + url_name = "cof.registration.autocomplete" + url_expected = "/autocomplete/registration" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def setUp(self): super().setUp() - self.u1 = create_user('uu_u1', attrs={ - 'first_name': 'abc', 'last_name': 'xyz', - }) - self.u2 = create_user('uu_u2', attrs={ - 'first_name': 'wyz', 'last_name': 'abd', - }) - self.m1 = create_member('uu_m1', attrs={ - 'first_name': 'ebd', 'last_name': 'wyv', - }) + self.u1 = create_user("uu_u1", attrs={"first_name": "abc", "last_name": "xyz"}) + self.u2 = create_user("uu_u2", attrs={"first_name": "wyz", "last_name": "abd"}) + self.m1 = create_member( + "uu_m1", attrs={"first_name": "ebd", "last_name": "wyv"} + ) self.mockLDAP([]) - def _test( - self, query, expected_users, expected_members, expected_clippers, - ): - r = self.client.get(self.url, {'q': query}) + def _test(self, query, expected_users, expected_members, expected_clippers): + r = self.client.get(self.url, {"q": query}) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['users'], map(repr, expected_users), - ordered=False, + r.context["users"], map(repr, expected_users), ordered=False ) self.assertQuerysetEqual( - r.context['members'], + r.context["members"], map(lambda u: repr(u.profile), expected_members), ordered=False, ) self.assertCountEqual( - map(str, r.context.get('clippers', [])), - map(str, expected_clippers), + map(str, r.context.get("clippers", [])), map(str, expected_clippers) ) def test_username(self): - self._test('uu', [self.u1, self.u2], [self.m1], []) + self._test("uu", [self.u1, self.u2], [self.m1], []) def test_firstname(self): - self._test('ab', [self.u1, self.u2], [], []) + self._test("ab", [self.u1, self.u2], [], []) def test_lastname(self): - self._test('wy', [self.u2], [self.m1], []) + self._test("wy", [self.u2], [self.m1], []) def test_multi_query(self): - self._test('wy bd', [self.u2], [self.m1], []) + self._test("wy bd", [self.u2], [self.m1], []) def test_clipper(self): - mock_ldap = self.mockLDAP([('uid', 'first last')]) + mock_ldap = self.mockLDAP([("uid", "first last")]) - self._test('aa bb', [], [], [Clipper('uid', 'first last')]) + self._test("aa bb", [], [], [Clipper("uid", "first last")]) mock_ldap.search.assert_called_once_with( - 'dc=spi,dc=ens,dc=fr', - '(&(|(cn=*aa*)(uid=*aa*))(|(cn=*bb*)(uid=*bb*)))', - attributes=['uid', 'cn'], + "dc=spi,dc=ens,dc=fr", + "(&(|(cn=*aa*)(uid=*aa*))(|(cn=*bb*)(uid=*bb*)))", + attributes=["uid", "cn"], ) def test_clipper_escaped(self): mock_ldap = self.mockLDAP([]) - self._test('; & | (', [], [], []) + self._test("; & | (", [], [], []) mock_ldap.search.assert_not_called() def test_clipper_no_duplicate(self): - self.mockLDAP([('uid', 'uu_u1')]) + self.mockLDAP([("uid", "uu_u1")]) - self._test('uu u1', [self.u1], [], [Clipper('uid', 'uu_u1')]) + self._test("uu u1", [self.u1], [], [Clipper("uid", "uu_u1")]) - self.u1.profile.login_clipper = 'uid' + self.u1.profile.login_clipper = "uid" self.u1.profile.save() - self._test('uu u1', [self.u1], [], []) + self._test("uu u1", [self.u1], [], []) class HomeViewTests(ViewTestCaseMixin, TestCase): - url_name = 'home' - url_expected = '/' + url_name = "home" + url_expected = "/" - auth_user = 'user' + auth_user = "user" auth_forbidden = [None] def test(self): @@ -355,49 +351,52 @@ class HomeViewTests(ViewTestCaseMixin, TestCase): class ProfileViewTests(ViewTestCaseMixin, TestCase): - url_name = 'profile' - url_expected = '/profile' + url_name = "profile" + url_expected = "/profile" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'member' - auth_forbidden = [None, 'user'] + auth_user = "member" + auth_forbidden = [None, "user"] def test_get(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def test_post(self): - u = self.users['member'] + u = self.users["member"] - r = self.client.post(self.url, { - 'u-first_name': 'First', - 'u-last_name': 'Last', - 'p-phone': '', - # 'mailing_cof': '1', - # 'mailing_bda': '1', - # 'mailing_bda_revente': '1', - }) + r = self.client.post( + self.url, + { + "u-first_name": "First", + "u-last_name": "Last", + "p-phone": "", + # 'mailing_cof': '1', + # 'mailing_bda': '1', + # 'mailing_bda_revente': '1', + }, + ) self.assertEqual(r.status_code, 200) - expected_message = Message(messages.SUCCESS, ( - "Votre profil a été mis à jour avec succès !" - )) + expected_message = Message( + messages.SUCCESS, ("Votre profil a été mis à jour avec succès !") + ) self.assertIn(expected_message, get_messages(r.wsgi_request)) u.refresh_from_db() - self.assertEqual(u.first_name, 'First') - self.assertEqual(u.last_name, 'Last') + self.assertEqual(u.first_name, "First") + self.assertEqual(u.last_name, "Last") self.assertFalse(u.profile.mailing_cof) self.assertFalse(u.profile.mailing_bda) self.assertFalse(u.profile.mailing_bda_revente) class UtilsViewTests(ViewTestCaseMixin, TestCase): - url_name = 'utile_cof' - url_expected = '/utile_cof' + url_name = "utile_cof" + url_expected = "/utile_cof" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): r = self.client.get(self.url) @@ -405,92 +404,95 @@ class UtilsViewTests(ViewTestCaseMixin, TestCase): class MailingListDiffCof(ViewTestCaseMixin, TestCase): - url_name = 'ml_diffcof' - url_expected = '/utile_cof/diff_cof' + url_name = "ml_diffcof" + url_expected = "/utile_cof/diff_cof" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def setUp(self): super().setUp() - self.u1 = create_member('u1', attrs={'mailing_cof': True}) - self.u2 = create_member('u2', attrs={'mailing_cof': False}) - self.u3 = create_user('u3', attrs={'mailing_cof': True}) + self.u1 = create_member("u1", attrs={"mailing_cof": True}) + self.u2 = create_member("u2", attrs={"mailing_cof": False}) + self.u3 = create_user("u3", attrs={"mailing_cof": True}) def test(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertEqual(r.context['personnes'].get(), self.u1.profile) + self.assertEqual(r.context["personnes"].get(), self.u1.profile) class ConfigUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'config.edit' - url_expected = '/config' + url_name = "config.edit" + url_expected = "/config" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'root' - auth_forbidden = [None, 'user', 'member', 'staff'] + auth_user = "root" + auth_forbidden = [None, "user", "member", "staff"] def get_users_extra(self): - return { - 'root': create_root('root'), - } + return {"root": create_root("root")} def test_get(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def test_post(self): - r = self.client.post(self.url, { - 'gestion_banner': 'Announcement !', - }) + r = self.client.post(self.url, {"gestion_banner": "Announcement !"}) - self.assertRedirects(r, reverse('home')) + self.assertRedirects(r, reverse("home")) class UserAutocompleteViewTests(ViewTestCaseMixin, TestCase): - url_name = 'cof-user-autocomplete' - url_expected = '/user/autocomplete' + url_name = "cof-user-autocomplete" + url_expected = "/user/autocomplete" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): - r = self.client.get(self.url, {'q': 'user'}) + r = self.client.get(self.url, {"q": "user"}) self.assertEqual(r.status_code, 200) class ExportMembersViewTests(ViewTestCaseMixin, TestCase): - url_name = 'cof.membres_export' - url_expected = '/export/members' + url_name = "cof.membres_export" + url_expected = "/export/members" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): - u1, u2 = self.users['member'], self.users['staff'] - u1.first_name = 'first' - u1.last_name = 'last' - u1.email = 'user@mail.net' + u1, u2 = self.users["member"], self.users["staff"] + u1.first_name = "first" + u1.last_name = "last" + u1.email = "user@mail.net" u1.save() - u1.profile.phone = '0123456789' - u1.profile.departement = 'Dept' + u1.profile.phone = "0123456789" + u1.profile.departement = "Dept" u1.profile.save() r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - data = list(csv.reader(r.content.decode('utf-8').split('\n')[:-1])) + data = list(csv.reader(r.content.decode("utf-8").split("\n")[:-1])) expected = [ [ - str(u1.pk), 'member', 'first', 'last', 'user@mail.net', - '0123456789', '1A', 'Dept', 'normalien', + str(u1.pk), + "member", + "first", + "last", + "user@mail.net", + "0123456789", + "1A", + "Dept", + "normalien", ], - [str(u2.pk), 'staff', '', '', '', '', '1A', '', 'normalien'], + [str(u2.pk), "staff", "", "", "", "", "1A", "", "normalien"], ] # Sort before checking equality, the order of the output of csv.reader # does not seem deterministic @@ -503,34 +505,32 @@ class MegaHelpers: def setUp(self): super().setUp() - u1 = create_user('u1') - u1.first_name = 'first' - u1.last_name = 'last' - u1.email = 'user@mail.net' + u1 = create_user("u1") + u1.first_name = "first" + u1.last_name = "last" + u1.email = "user@mail.net" u1.save() - u1.profile.phone = '0123456789' - u1.profile.departement = 'Dept' - u1.profile.comments = 'profile.comments' + u1.profile.phone = "0123456789" + u1.profile.departement = "Dept" + u1.profile.comments = "profile.comments" u1.profile.save() - u2 = create_user('u2') + u2 = create_user("u2") u2.profile.save() - m = Event.objects.create(title='MEGA 2018') + m = Event.objects.create(title="MEGA 2018") - cf1 = m.commentfields.create(name='Commentaires') - cf2 = m.commentfields.create( - name='Comment Field 2', fieldtype='char', - ) + cf1 = m.commentfields.create(name="Commentaires") + cf2 = m.commentfields.create(name="Comment Field 2", fieldtype="char") - option_type = m.options.create(name='Orga ? Conscrit ?') - choice_orga = option_type.choices.create(value='Orga') - choice_conscrit = option_type.choices.create(value='Conscrit') + option_type = m.options.create(name="Orga ? Conscrit ?") + choice_orga = option_type.choices.create(value="Orga") + choice_conscrit = option_type.choices.create(value="Conscrit") mr1 = m.eventregistration_set.create(user=u1) mr1.options.add(choice_orga) - mr1.comments.create(commentfield=cf1, content='Comment 1') - mr1.comments.create(commentfield=cf2, content='Comment 2') + mr1.comments.create(commentfield=cf1, content="Comment 1") + mr1.comments.create(commentfield=cf2, content="Comment 2") mr2 = m.eventregistration_set.create(user=u2) mr2.options.add(choice_conscrit) @@ -543,96 +543,122 @@ class MegaHelpers: class ExportMegaViewTests(MegaHelpers, ViewTestCaseMixin, TestCase): - url_name = 'cof.mega_export' - url_expected = '/export/mega' + url_name = "cof.mega_export" + url_expected = "/export/mega" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertListEqual(self.load_from_csv_response(r), [ + self.assertListEqual( + self.load_from_csv_response(r), [ - 'u1', 'first', 'last', 'user@mail.net', '0123456789', - str(self.u1.pk), 'profile.comments', 'Comment 1---Comment 2', + [ + "u1", + "first", + "last", + "user@mail.net", + "0123456789", + str(self.u1.pk), + "profile.comments", + "Comment 1---Comment 2", + ], + ["u2", "", "", "", "", str(self.u2.pk), "", ""], ], - ['u2', '', '', '', '', str(self.u2.pk), '', ''], - ]) + ) class ExportMegaOrgasViewTests(MegaHelpers, ViewTestCaseMixin, TestCase): - url_name = 'cof.mega_export_orgas' - url_expected = '/export/mega/orgas' + url_name = "cof.mega_export_orgas" + url_expected = "/export/mega/orgas" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertListEqual(self.load_from_csv_response(r), [ + self.assertListEqual( + self.load_from_csv_response(r), [ - 'u1', 'first', 'last', 'user@mail.net', '0123456789', - str(self.u1.pk), 'profile.comments', 'Comment 1---Comment 2', + [ + "u1", + "first", + "last", + "user@mail.net", + "0123456789", + str(self.u1.pk), + "profile.comments", + "Comment 1---Comment 2", + ] ], - ]) + ) -class ExportMegaParticipantsViewTests( - MegaHelpers, ViewTestCaseMixin, TestCase): - url_name = 'cof.mega_export_participants' - url_expected = '/export/mega/participants' +class ExportMegaParticipantsViewTests(MegaHelpers, ViewTestCaseMixin, TestCase): + url_name = "cof.mega_export_participants" + url_expected = "/export/mega/participants" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertListEqual(self.load_from_csv_response(r), [ - ['u2', '', '', '', '', str(self.u2.pk), '', ''], - ]) + self.assertListEqual( + self.load_from_csv_response(r), + [["u2", "", "", "", "", str(self.u2.pk), "", ""]], + ) -class ExportMegaRemarksViewTests( - MegaHelpers, ViewTestCaseMixin, TestCase): - url_name = 'cof.mega_export_remarks' - url_expected = '/export/mega/avecremarques' +class ExportMegaRemarksViewTests(MegaHelpers, ViewTestCaseMixin, TestCase): + url_name = "cof.mega_export_remarks" + url_expected = "/export/mega/avecremarques" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] def test(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertListEqual(self.load_from_csv_response(r), [ + self.assertListEqual( + self.load_from_csv_response(r), [ - 'u1', 'first', 'last', 'user@mail.net', '0123456789', - str(self.u1.pk), 'profile.comments', 'Comment 1', + [ + "u1", + "first", + "last", + "user@mail.net", + "0123456789", + str(self.u1.pk), + "profile.comments", + "Comment 1", + ] ], - ]) + ) class ClubListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'liste-clubs' - url_expected = '/clubs/liste' + url_name = "liste-clubs" + url_expected = "/clubs/liste" - auth_user = 'member' - auth_forbidden = [None, 'user'] + auth_user = "member" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() - self.c1 = Club.objects.create(name='Club1') - self.c2 = Club.objects.create(name='Club2') + self.c1 = Club.objects.create(name="Club1") + self.c2 = Club.objects.create(name="Club2") - m = self.users['member'] + m = self.users["member"] self.c1.membres.add(m) self.c1.respos.add(m) @@ -640,11 +666,11 @@ class ClubListViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertEqual(r.context['owned_clubs'].get(), self.c1) - self.assertEqual(r.context['other_clubs'].get(), self.c2) + self.assertEqual(r.context["owned_clubs"].get(), self.c1) + self.assertEqual(r.context["other_clubs"].get(), self.c2) def test_as_staff(self): - u = self.users['staff'] + u = self.users["staff"] c = Client() c.force_login(u) @@ -652,32 +678,31 @@ class ClubListViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['owned_clubs'], map(repr, [self.c1, self.c2]), - ordered=False, + r.context["owned_clubs"], map(repr, [self.c1, self.c2]), ordered=False ) class ClubMembersViewTests(ViewTestCaseMixin, TestCase): - url_name = 'membres-club' + url_name = "membres-club" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] @property def url_kwargs(self): - return {'name': self.c.name} + return {"name": self.c.name} @property def url_expected(self): - return '/clubs/membres/{}'.format(self.c.name) + return "/clubs/membres/{}".format(self.c.name) def setUp(self): super().setUp() - self.u1 = create_user('u1') - self.u2 = create_user('u2') + self.u1 = create_user("u1") + self.u2 = create_user("u2") - self.c = Club.objects.create(name='Club') + self.c = Club.objects.create(name="Club") self.c.membres.add(self.u1, self.u2) self.c.respos.add(self.u1) @@ -685,10 +710,10 @@ class ClubMembersViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertEqual(r.context['members_no_respo'].get(), self.u2) + self.assertEqual(r.context["members_no_respo"].get(), self.u2) def test_as_respo(self): - u = self.users['user'] + u = self.users["user"] self.c.respos.add(u) c = Client() @@ -699,31 +724,27 @@ class ClubMembersViewTests(ViewTestCaseMixin, TestCase): class ClubChangeRespoViewTests(ViewTestCaseMixin, TestCase): - url_name = 'change-respo' + url_name = "change-respo" - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] @property def url_kwargs(self): - return {'club_name': self.c.name, 'user_id': self.users['user'].pk} + return {"club_name": self.c.name, "user_id": self.users["user"].pk} @property def url_expected(self): - return '/clubs/change_respo/{}/{}'.format( - self.c.name, self.users['user'].pk, - ) + return "/clubs/change_respo/{}/{}".format(self.c.name, self.users["user"].pk) def setUp(self): super().setUp() - self.c = Club.objects.create(name='Club') + self.c = Club.objects.create(name="Club") def test(self): - u = self.users['user'] - expected_redirect = reverse('membres-club', kwargs={ - 'name': self.c.name, - }) + u = self.users["user"] + expected_redirect = reverse("membres-club", kwargs={"name": self.c.name}) self.c.membres.add(u) r = self.client.get(self.url) @@ -735,40 +756,42 @@ class ClubChangeRespoViewTests(ViewTestCaseMixin, TestCase): class CalendarViewTests(ViewTestCaseMixin, TestCase): - url_name = 'calendar' - url_expected = '/calendar/subscription' + url_name = "calendar" + url_expected = "/calendar/subscription" - auth_user = 'member' - auth_forbidden = [None, 'user'] + auth_user = "member" + auth_forbidden = [None, "user"] post_expected_message = Message( - messages.SUCCESS, "Calendrier mis à jour avec succès.") + messages.SUCCESS, "Calendrier mis à jour avec succès." + ) def test_get(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def test_post_new(self): - r = self.client.post(self.url, { - 'subscribe_to_events': True, - 'subscribe_to_my_shows': True, - 'other_shows': [], - }) + r = self.client.post( + self.url, + { + "subscribe_to_events": True, + "subscribe_to_my_shows": True, + "other_shows": [], + }, + ) self.assertEqual(r.status_code, 200) self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) - cs = self.users['member'].calendarsubscription + cs = self.users["member"].calendarsubscription self.assertTrue(cs.subscribe_to_events) self.assertTrue(cs.subscribe_to_my_shows) def test_post_edit(self): - u = self.users['member'] + u = self.users["member"] token = uuid.uuid4() cs = CalendarSubscription.objects.create(token=token, user=u) - r = self.client.post(self.url, { - 'other_shows': [], - }) + r = self.client.post(self.url, {"other_shows": []}) self.assertEqual(r.status_code, 200) self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) @@ -778,34 +801,30 @@ class CalendarViewTests(ViewTestCaseMixin, TestCase): self.assertFalse(cs.subscribe_to_my_shows) def test_post_other_shows(self): - t = Tirage.objects.create( - ouverture=self.now, - fermeture=self.now, - active=True, - ) + t = Tirage.objects.create(ouverture=self.now, fermeture=self.now, active=True) location = Salle.objects.create() s = t.spectacle_set.create( - date=self.now, price=3.5, slots=20, location=location, - listing=True) + date=self.now, price=3.5, slots=20, location=location, listing=True + ) - r = self.client.post(self.url, {'other_shows': [str(s.pk)]}) + r = self.client.post(self.url, {"other_shows": [str(s.pk)]}) self.assertEqual(r.status_code, 200) class CalendarICSViewTests(ViewTestCaseMixin, TestCase): - url_name = 'calendar.ics' + url_name = "calendar.ics" auth_user = None auth_forbidden = [] @property def url_kwargs(self): - return {'token': self.token} + return {"token": self.token} @property def url_expected(self): - return '/calendar/{}/calendar.ics'.format(self.token) + return "/calendar/{}/calendar.ics".format(self.token) def setUp(self): super().setUp() @@ -813,32 +832,44 @@ class CalendarICSViewTests(ViewTestCaseMixin, TestCase): self.token = uuid.uuid4() self.t = Tirage.objects.create( - ouverture=self.now, - fermeture=self.now, - active=True, + ouverture=self.now, fermeture=self.now, active=True ) - location = Salle.objects.create(name='Location') + location = Salle.objects.create(name="Location") self.s1 = self.t.spectacle_set.create( - price=1, slots=10, location=location, listing=True, - title='Spectacle 1', date=self.now + timedelta(days=1), + price=1, + slots=10, + location=location, + listing=True, + title="Spectacle 1", + date=self.now + timedelta(days=1), ) self.s2 = self.t.spectacle_set.create( - price=2, slots=20, location=location, listing=True, - title='Spectacle 2', date=self.now + timedelta(days=2), + price=2, + slots=20, + location=location, + listing=True, + title="Spectacle 2", + date=self.now + timedelta(days=2), ) self.s3 = self.t.spectacle_set.create( - price=3, slots=30, location=location, listing=True, - title='Spectacle 3', date=self.now + timedelta(days=3), + price=3, + slots=30, + location=location, + listing=True, + title="Spectacle 3", + date=self.now + timedelta(days=3), ) def test(self): - u = self.users['user'] + u = self.users["user"] p = u.participant_set.create(tirage=self.t) p.attribution_set.create(spectacle=self.s1) self.cs = CalendarSubscription.objects.create( - user=u, token=self.token, - subscribe_to_my_shows=True, subscribe_to_events=True, + user=u, + token=self.token, + subscribe_to_my_shows=True, + subscribe_to_events=True, ) self.cs.other_shows.add(self.s2) @@ -847,91 +878,107 @@ class CalendarICSViewTests(ViewTestCaseMixin, TestCase): def get_dt_from_ical(v): return v.dt - self.assertCalEqual(r.content.decode('utf-8'), [ - { - 'summary': 'Spectacle 1', - 'dtstart': (get_dt_from_ical, ( - (self.now + timedelta(days=1)).replace(microsecond=0) - )), - 'dtend': (get_dt_from_ical, ( - (self.now + timedelta(days=1, hours=2)).replace( - microsecond=0) - )), - 'location': 'Location', - 'uid': 'show-{}-{}@example.com'.format(self.s1.pk, self.t.pk), - }, - { - 'summary': 'Spectacle 2', - 'dtstart': (get_dt_from_ical, ( - (self.now + timedelta(days=2)).replace(microsecond=0) - )), - 'dtend': (get_dt_from_ical, ( - (self.now + timedelta(days=2, hours=2)).replace( - microsecond=0) - )), - 'location': 'Location', - 'uid': 'show-{}-{}@example.com'.format(self.s2.pk, self.t.pk), - }, - ]) + self.assertCalEqual( + r.content.decode("utf-8"), + [ + { + "summary": "Spectacle 1", + "dtstart": ( + get_dt_from_ical, + ((self.now + timedelta(days=1)).replace(microsecond=0)), + ), + "dtend": ( + get_dt_from_ical, + ( + (self.now + timedelta(days=1, hours=2)).replace( + microsecond=0 + ) + ), + ), + "location": "Location", + "uid": "show-{}-{}@example.com".format(self.s1.pk, self.t.pk), + }, + { + "summary": "Spectacle 2", + "dtstart": ( + get_dt_from_ical, + ((self.now + timedelta(days=2)).replace(microsecond=0)), + ), + "dtend": ( + get_dt_from_ical, + ( + (self.now + timedelta(days=2, hours=2)).replace( + microsecond=0 + ) + ), + ), + "location": "Location", + "uid": "show-{}-{}@example.com".format(self.s2.pk, self.t.pk), + }, + ], + ) class EventViewTests(ViewTestCaseMixin, TestCase): - url_name = 'event.details' - http_methods = ['GET', 'POST'] + url_name = "event.details" + http_methods = ["GET", "POST"] - auth_user = 'user' + auth_user = "user" auth_forbidden = [None] - post_expected_message = Message(messages.SUCCESS, ( - "Votre inscription a bien été enregistrée ! Vous pouvez cependant la " - "modifier jusqu'à la fin des inscriptions." - )) + post_expected_message = Message( + messages.SUCCESS, + ( + "Votre inscription a bien été enregistrée ! Vous pouvez cependant la " + "modifier jusqu'à la fin des inscriptions." + ), + ) @property def url_kwargs(self): - return {'event_id': self.e.pk} + return {"event_id": self.e.pk} @property def url_expected(self): - return '/event/{}'.format(self.e.pk) + return "/event/{}".format(self.e.pk) def setUp(self): super().setUp() self.e = Event.objects.create() - self.ecf1 = self.e.commentfields.create(name='Comment Field 1') + self.ecf1 = self.e.commentfields.create(name="Comment Field 1") self.ecf2 = self.e.commentfields.create( - name='Comment Field 2', fieldtype='char', + name="Comment Field 2", fieldtype="char" ) - self.o1 = self.e.options.create(name='Option 1') - self.o2 = self.e.options.create(name='Option 2', multi_choices=True) + self.o1 = self.e.options.create(name="Option 1") + self.o2 = self.e.options.create(name="Option 2", multi_choices=True) - self.oc1 = self.o1.choices.create(value='O1 - Choice 1') - self.oc2 = self.o1.choices.create(value='O1 - Choice 2') - self.oc3 = self.o2.choices.create(value='O2 - Choice 1') - self.oc4 = self.o2.choices.create(value='O2 - Choice 2') + self.oc1 = self.o1.choices.create(value="O1 - Choice 1") + self.oc2 = self.o1.choices.create(value="O1 - Choice 2") + self.oc3 = self.o2.choices.create(value="O2 - Choice 1") + self.oc4 = self.o2.choices.create(value="O2 - Choice 2") def test_get(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def test_post_new(self): - r = self.client.post(self.url, { - 'option_{}'.format(self.o1.pk): [str(self.oc1.pk)], - 'option_{}'.format(self.o2.pk): [ - str(self.oc3.pk), str(self.oc4.pk), - ], - }) + r = self.client.post( + self.url, + { + "option_{}".format(self.o1.pk): [str(self.oc1.pk)], + "option_{}".format(self.o2.pk): [str(self.oc3.pk), str(self.oc4.pk)], + }, + ) self.assertEqual(r.status_code, 200) self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) - er = self.e.eventregistration_set.get(user=self.users['user']) + er = self.e.eventregistration_set.get(user=self.users["user"]) self.assertQuerysetEqual( - er.options.all(), map(repr, [self.oc1, self.oc3, self.oc4]), - ordered=False, + er.options.all(), map(repr, [self.oc1, self.oc3, self.oc4]), ordered=False ) # TODO: Make the view care about comments. # self.assertQuerysetEqual( @@ -940,25 +987,23 @@ class EventViewTests(ViewTestCaseMixin, TestCase): # ) def test_post_edit(self): - er = self.e.eventregistration_set.create(user=self.users['user']) + er = self.e.eventregistration_set.create(user=self.users["user"]) er.options.add(self.oc1, self.oc3, self.oc4) - er.comments.create( - commentfield=self.ecf1, content='Comment 1', - ) + er.comments.create(commentfield=self.ecf1, content="Comment 1") - r = self.client.post(self.url, { - 'option_{}'.format(self.o1.pk): [], - 'option_{}'.format(self.o2.pk): [str(self.oc3.pk)], - }) + r = self.client.post( + self.url, + { + "option_{}".format(self.o1.pk): [], + "option_{}".format(self.o2.pk): [str(self.oc3.pk)], + }, + ) self.assertEqual(r.status_code, 200) self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) er.refresh_from_db() - self.assertQuerysetEqual( - er.options.all(), map(repr, [self.oc3]), - ordered=False, - ) + self.assertQuerysetEqual(er.options.all(), map(repr, [self.oc3]), ordered=False) # TODO: Make the view care about comments. # self.assertQuerysetEqual( # er.comments.all(), map(repr, []), @@ -967,153 +1012,149 @@ class EventViewTests(ViewTestCaseMixin, TestCase): class EventStatusViewTests(ViewTestCaseMixin, TestCase): - url_name = 'event.details.status' + url_name = "event.details.status" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] @property def url_kwargs(self): - return {'event_id': self.e.pk} + return {"event_id": self.e.pk} @property def url_expected(self): - return '/event/{}/status'.format(self.e.pk) + return "/event/{}/status".format(self.e.pk) def setUp(self): super().setUp() self.e = Event.objects.create() - self.cf1 = self.e.commentfields.create(name='Comment Field 1') - self.cf2 = self.e.commentfields.create( - name='Comment Field 2', fieldtype='char', - ) + self.cf1 = self.e.commentfields.create(name="Comment Field 1") + self.cf2 = self.e.commentfields.create(name="Comment Field 2", fieldtype="char") - self.o1 = self.e.options.create(name='Option 1') - self.o2 = self.e.options.create(name='Option 2', multi_choices=True) + self.o1 = self.e.options.create(name="Option 1") + self.o2 = self.e.options.create(name="Option 2", multi_choices=True) - self.oc1 = self.o1.choices.create(value='O1 - Choice 1') - self.oc2 = self.o1.choices.create(value='O1 - Choice 2') - self.oc3 = self.o2.choices.create(value='O2 - Choice 1') - self.oc4 = self.o2.choices.create(value='O2 - Choice 2') + self.oc1 = self.o1.choices.create(value="O1 - Choice 1") + self.oc2 = self.o1.choices.create(value="O1 - Choice 2") + self.oc3 = self.o2.choices.create(value="O2 - Choice 1") + self.oc4 = self.o2.choices.create(value="O2 - Choice 2") - self.er1 = self.e.eventregistration_set.create(user=self.users['user']) + self.er1 = self.e.eventregistration_set.create(user=self.users["user"]) self.er1.options.add(self.oc1) - self.er2 = self.e.eventregistration_set.create( - user=self.users['member'], - ) + self.er2 = self.e.eventregistration_set.create(user=self.users["member"]) def _get_oc_filter_name(self, oc): - return 'option_{}_choice_{}'.format(oc.event_option.pk, oc.pk) + return "option_{}_choice_{}".format(oc.event_option.pk, oc.pk) def _test_filters(self, filters, expected): - r = self.client.post(self.url, { - self._get_oc_filter_name(oc): v for oc, v in filters - }) + r = self.client.post( + self.url, {self._get_oc_filter_name(oc): v for oc, v in filters} + ) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['user_choices'], map(repr, expected), - ordered=False, + r.context["user_choices"], map(repr, expected), ordered=False ) def test_filter_none(self): - self._test_filters([(self.oc1, 'none')], [self.er1, self.er2]) + self._test_filters([(self.oc1, "none")], [self.er1, self.er2]) def test_filter_yes(self): - self._test_filters([(self.oc1, 'yes')], [self.er1]) + self._test_filters([(self.oc1, "yes")], [self.er1]) def test_filter_no(self): - self._test_filters([(self.oc1, 'no')], [self.er2]) + self._test_filters([(self.oc1, "no")], [self.er2]) class SurveyViewTests(ViewTestCaseMixin, TestCase): - url_name = 'survey.details' - http_methods = ['GET', 'POST'] + url_name = "survey.details" + http_methods = ["GET", "POST"] - auth_user = 'user' + auth_user = "user" auth_forbidden = [None] - post_expected_message = Message(messages.SUCCESS, ( - "Votre réponse a bien été enregistrée ! Vous pouvez cependant la " - "modifier jusqu'à la fin du sondage." - )) + post_expected_message = Message( + messages.SUCCESS, + ( + "Votre réponse a bien été enregistrée ! Vous pouvez cependant la " + "modifier jusqu'à la fin du sondage." + ), + ) @property def url_kwargs(self): - return {'survey_id': self.s.pk} + return {"survey_id": self.s.pk} @property def url_expected(self): - return '/survey/{}'.format(self.s.pk) + return "/survey/{}".format(self.s.pk) def setUp(self): super().setUp() - self.s = Survey.objects.create(title='Title') + self.s = Survey.objects.create(title="Title") - self.q1 = self.s.questions.create(question='Question 1 ?') - self.q2 = self.s.questions.create( - question='Question 2 ?', - multi_answers=True, - ) + self.q1 = self.s.questions.create(question="Question 1 ?") + self.q2 = self.s.questions.create(question="Question 2 ?", multi_answers=True) - self.qa1 = self.q1.answers.create(answer='Q1 - Answer 1') - self.qa2 = self.q1.answers.create(answer='Q1 - Answer 2') - self.qa3 = self.q2.answers.create(answer='Q2 - Answer 1') - self.qa4 = self.q2.answers.create(answer='Q2 - Answer 2') + self.qa1 = self.q1.answers.create(answer="Q1 - Answer 1") + self.qa2 = self.q1.answers.create(answer="Q1 - Answer 2") + self.qa3 = self.q2.answers.create(answer="Q2 - Answer 1") + self.qa4 = self.q2.answers.create(answer="Q2 - Answer 2") def test_get(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def test_post_new(self): - r = self.client.post(self.url, { - 'question_{}'.format(self.q1.pk): [str(self.qa1.pk)], - 'question_{}'.format(self.q2.pk): [ - str(self.qa3.pk), str(self.qa4.pk), - ], - }) + r = self.client.post( + self.url, + { + "question_{}".format(self.q1.pk): [str(self.qa1.pk)], + "question_{}".format(self.q2.pk): [str(self.qa3.pk), str(self.qa4.pk)], + }, + ) self.assertEqual(r.status_code, 200) self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) - a = self.s.surveyanswer_set.get(user=self.users['user']) + a = self.s.surveyanswer_set.get(user=self.users["user"]) self.assertQuerysetEqual( - a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]), - ordered=False, + a.answers.all(), map(repr, [self.qa1, self.qa3, self.qa4]), ordered=False ) def test_post_edit(self): - a = self.s.surveyanswer_set.create(user=self.users['user']) + a = self.s.surveyanswer_set.create(user=self.users["user"]) a.answers.add(self.qa1, self.qa1, self.qa4) - r = self.client.post(self.url, { - 'question_{}'.format(self.q1.pk): [], - 'question_{}'.format(self.q2.pk): [str(self.qa3.pk)], - }) + r = self.client.post( + self.url, + { + "question_{}".format(self.q1.pk): [], + "question_{}".format(self.q2.pk): [str(self.qa3.pk)], + }, + ) self.assertEqual(r.status_code, 200) self.assertIn(self.post_expected_message, get_messages(r.wsgi_request)) a.refresh_from_db() - self.assertQuerysetEqual( - a.answers.all(), map(repr, [self.qa3]), - ordered=False, - ) + self.assertQuerysetEqual(a.answers.all(), map(repr, [self.qa3]), ordered=False) def test_post_delete(self): - a = self.s.surveyanswer_set.create(user=self.users['user']) + a = self.s.surveyanswer_set.create(user=self.users["user"]) a.answers.add(self.qa1, self.qa4) - r = self.client.post(self.url, {'delete': '1'}) + r = self.client.post(self.url, {"delete": "1"}) self.assertEqual(r.status_code, 200) expected_message = Message( - messages.SUCCESS, "Votre réponse a bien été supprimée") + messages.SUCCESS, "Votre réponse a bien été supprimée" + ) self.assertIn(expected_message, get_messages(r.wsgi_request)) with self.assertRaises(SurveyAnswer.DoesNotExist): @@ -1137,64 +1178,60 @@ class SurveyViewTests(ViewTestCaseMixin, TestCase): class SurveyStatusViewTests(ViewTestCaseMixin, TestCase): - url_name = 'survey.details.status' + url_name = "survey.details.status" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'staff' - auth_forbidden = [None, 'user', 'member'] + auth_user = "staff" + auth_forbidden = [None, "user", "member"] @property def url_kwargs(self): - return {'survey_id': self.s.pk} + return {"survey_id": self.s.pk} @property def url_expected(self): - return '/survey/{}/status'.format(self.s.pk) + return "/survey/{}/status".format(self.s.pk) def setUp(self): super().setUp() - self.s = Survey.objects.create(title='Title') + self.s = Survey.objects.create(title="Title") - self.q1 = self.s.questions.create(question='Question 1 ?') - self.q2 = self.s.questions.create( - question='Question 2 ?', - multi_answers=True, - ) + self.q1 = self.s.questions.create(question="Question 1 ?") + self.q2 = self.s.questions.create(question="Question 2 ?", multi_answers=True) - self.qa1 = self.q1.answers.create(answer='Q1 - Answer 1') - self.qa2 = self.q1.answers.create(answer='Q1 - Answer 2') - self.qa3 = self.q2.answers.create(answer='Q2 - Answer 1') - self.qa4 = self.q2.answers.create(answer='Q2 - Answer 2') + self.qa1 = self.q1.answers.create(answer="Q1 - Answer 1") + self.qa2 = self.q1.answers.create(answer="Q1 - Answer 2") + self.qa3 = self.q2.answers.create(answer="Q2 - Answer 1") + self.qa4 = self.q2.answers.create(answer="Q2 - Answer 2") - self.a1 = self.s.surveyanswer_set.create(user=self.users['user']) + self.a1 = self.s.surveyanswer_set.create(user=self.users["user"]) self.a1.answers.add(self.qa1) - self.a2 = self.s.surveyanswer_set.create(user=self.users['member']) + self.a2 = self.s.surveyanswer_set.create(user=self.users["member"]) def test_get(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) def _get_qa_filter_name(self, qa): - return 'question_{}_answer_{}'.format(qa.survey_question.pk, qa.pk) + return "question_{}_answer_{}".format(qa.survey_question.pk, qa.pk) def _test_filters(self, filters, expected): - r = self.client.post(self.url, { - self._get_qa_filter_name(qa): v for qa, v in filters - }) + r = self.client.post( + self.url, {self._get_qa_filter_name(qa): v for qa, v in filters} + ) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['user_answers'], map(repr, expected), - ordered=False, + r.context["user_answers"], map(repr, expected), ordered=False ) def test_filter_none(self): - self._test_filters([(self.qa1, 'none')], [self.a1, self.a2]) + self._test_filters([(self.qa1, "none")], [self.a1, self.a2]) def test_filter_yes(self): - self._test_filters([(self.qa1, 'yes')], [self.a1]) + self._test_filters([(self.qa1, "yes")], [self.a1]) def test_filter_no(self): - self._test_filters([(self.qa1, 'no')], [self.a2]) + self._test_filters([(self.qa1, "no")], [self.a2]) diff --git a/gestioncof/tests/testcases.py b/gestioncof/tests/testcases.py index b53f2866..43f69bbc 100644 --- a/gestioncof/tests/testcases.py +++ b/gestioncof/tests/testcases.py @@ -1,6 +1,6 @@ from shared.tests.testcases import ViewTestCaseMixin as BaseViewTestCaseMixin -from .utils import create_user, create_member, create_staff +from .utils import create_member, create_staff, create_user class ViewTestCaseMixin(BaseViewTestCaseMixin): @@ -18,7 +18,7 @@ class ViewTestCaseMixin(BaseViewTestCaseMixin): def get_users_base(self): return { - 'user': create_user('user'), - 'member': create_member('member'), - 'staff': create_staff('staff'), + "user": create_user("user"), + "member": create_member("member"), + "staff": create_staff("staff"), } diff --git a/gestioncof/tests/utils.py b/gestioncof/tests/utils.py index 7ba361b7..7325e350 100644 --- a/gestioncof/tests/utils.py +++ b/gestioncof/tests/utils.py @@ -7,28 +7,35 @@ def _create_user(username, is_cof=False, is_staff=False, attrs=None): if attrs is None: attrs = {} - password = attrs.pop('password', username) + password = attrs.pop("password", username) - user_keys = [ - 'first_name', 'last_name', 'email', 'is_staff', 'is_superuser', - ] + user_keys = ["first_name", "last_name", "email", "is_staff", "is_superuser"] user_attrs = {k: v for k, v in attrs.items() if k in user_keys} profile_keys = [ - 'is_cof', 'login_clipper', 'phone', 'occupation', 'departement', - 'type_cotiz', 'mailing_cof', 'mailing_bda', 'mailing_bda_revente', - 'comments', 'is_buro', 'petit_cours_accept', - 'petit_cours_remarques', + "is_cof", + "login_clipper", + "phone", + "occupation", + "departement", + "type_cotiz", + "mailing_cof", + "mailing_bda", + "mailing_bda_revente", + "comments", + "is_buro", + "petit_cours_accept", + "petit_cours_remarques", ] profile_attrs = {k: v for k, v in attrs.items() if k in profile_keys} if is_cof: - profile_attrs['is_cof'] = True + profile_attrs["is_cof"] = True if is_staff: # At the moment, admin is accessible by COF staff. - user_attrs['is_staff'] = True - profile_attrs['is_buro'] = True + user_attrs["is_staff"] = True + profile_attrs["is_buro"] = True user = User(username=username, **user_attrs) user.set_password(password) @@ -56,6 +63,6 @@ def create_staff(username, attrs=None): def create_root(username, attrs=None): if attrs is None: attrs = {} - attrs.setdefault('is_staff', True) - attrs.setdefault('is_superuser', True) + attrs.setdefault("is_staff", True) + attrs.setdefault("is_superuser", True) return _create_user(username, attrs=attrs) diff --git a/gestioncof/urls.py b/gestioncof/urls.py index f8ce8f6d..c4414fa5 100644 --- a/gestioncof/urls.py +++ b/gestioncof/urls.py @@ -1,67 +1,87 @@ from django.conf.urls import url -from gestioncof.petits_cours_views import DemandeListView, DemandeDetailView -from gestioncof import views, petits_cours_views + +from gestioncof import petits_cours_views, views from gestioncof.decorators import buro_required +from gestioncof.petits_cours_views import DemandeDetailView, DemandeListView export_patterns = [ - url(r'^members$', views.export_members, - name='cof.membres_export'), - url(r'^mega/avecremarques$', views.export_mega_remarksonly, - name='cof.mega_export_remarks'), - url(r'^mega/participants$', views.export_mega_participants, - name='cof.mega_export_participants'), - url(r'^mega/orgas$', views.export_mega_orgas, - name='cof.mega_export_orgas'), + url(r"^members$", views.export_members, name="cof.membres_export"), + url( + r"^mega/avecremarques$", + views.export_mega_remarksonly, + name="cof.mega_export_remarks", + ), + url( + r"^mega/participants$", + views.export_mega_participants, + name="cof.mega_export_participants", + ), + url(r"^mega/orgas$", views.export_mega_orgas, name="cof.mega_export_orgas"), # url(r'^mega/(?P.+)$', views.export_mega_bytype), - url(r'^mega$', views.export_mega, - name='cof.mega_export'), + url(r"^mega$", views.export_mega, name="cof.mega_export"), ] petitcours_patterns = [ - url(r'^inscription$', petits_cours_views.inscription, - name='petits-cours-inscription'), - url(r'^demande$', petits_cours_views.demande, - name='petits-cours-demande'), - url(r'^demande-raw$', petits_cours_views.demande_raw, - name='petits-cours-demande-raw'), - url(r'^demandes$', + url( + r"^inscription$", + petits_cours_views.inscription, + name="petits-cours-inscription", + ), + url(r"^demande$", petits_cours_views.demande, name="petits-cours-demande"), + url( + r"^demande-raw$", + petits_cours_views.demande_raw, + name="petits-cours-demande-raw", + ), + url( + r"^demandes$", buro_required(DemandeListView.as_view()), - name='petits-cours-demandes-list'), - url(r'^demandes/(?P\d+)$', + name="petits-cours-demandes-list", + ), + url( + r"^demandes/(?P\d+)$", buro_required(DemandeDetailView.as_view()), - name='petits-cours-demande-details'), - url(r'^demandes/(?P\d+)/traitement$', + name="petits-cours-demande-details", + ), + url( + r"^demandes/(?P\d+)/traitement$", petits_cours_views.traitement, - name='petits-cours-demande-traitement'), - url(r'^demandes/(?P\d+)/retraitement$', + name="petits-cours-demande-traitement", + ), + url( + r"^demandes/(?P\d+)/retraitement$", petits_cours_views.retraitement, - name='petits-cours-demande-retraitement'), + name="petits-cours-demande-retraitement", + ), ] surveys_patterns = [ - url(r'^(?P\d+)/status$', views.survey_status, - name='survey.details.status'), - url(r'^(?P\d+)$', views.survey, - name='survey.details'), + url( + r"^(?P\d+)/status$", + views.survey_status, + name="survey.details.status", + ), + url(r"^(?P\d+)$", views.survey, name="survey.details"), ] events_patterns = [ - url(r'^(?P\d+)$', views.event, - name='event.details'), - url(r'^(?P\d+)/status$', views.event_status, - name='event.details.status'), + url(r"^(?P\d+)$", views.event, name="event.details"), + url(r"^(?P\d+)/status$", views.event_status, name="event.details.status"), ] calendar_patterns = [ - url(r'^subscription$', views.calendar, - name='calendar'), - url(r'^(?P[a-z0-9-]+)/calendar.ics$', views.calendar_ics, - name='calendar.ics'), + url(r"^subscription$", views.calendar, name="calendar"), + url( + r"^(?P[a-z0-9-]+)/calendar.ics$", views.calendar_ics, name="calendar.ics" + ), ] clubs_patterns = [ - url(r'^membres/(?P\w+)', views.membres_club, name='membres-club'), - url(r'^liste', views.liste_clubs, name='liste-clubs'), - url(r'^change_respo/(?P\w+)/(?P\d+)', - views.change_respo, name='change-respo'), + url(r"^membres/(?P\w+)", views.membres_club, name="membres-club"), + url(r"^liste", views.liste_clubs, name="liste-clubs"), + url( + r"^change_respo/(?P\w+)/(?P\d+)", + views.change_respo, + name="change-respo", + ), ] diff --git a/gestioncof/views.py b/gestioncof/views.py index d77794bb..618fb24a 100644 --- a/gestioncof/views.py +++ b/gestioncof/views.py @@ -1,59 +1,74 @@ -import unicodecsv import uuid from datetime import timedelta -from icalendar import Calendar, Event as Vevent -from custommail.shortcuts import send_custom_mail -from django.shortcuts import redirect, get_object_or_404, render -from django.http import Http404, HttpResponse, HttpResponseForbidden +import unicodecsv +from custommail.shortcuts import send_custom_mail +from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User from django.contrib.auth.views import ( - login as django_login_view, logout as django_logout_view, + login as django_login_view, + logout as django_logout_view, redirect_to_login, ) -from django.contrib.auth.models import User from django.contrib.sites.models import Site from django.core.urlresolvers import reverse_lazy -from django.views.generic import FormView +from django.http import Http404, HttpResponse, HttpResponseForbidden +from django.shortcuts import get_object_or_404, redirect, render from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from django.contrib import messages - +from django.views.generic import FormView from django_cas_ng.views import logout as cas_logout_view +from icalendar import Calendar, Event as Vevent -from utils.views.autocomplete import Select2QuerySetView - -from gestioncof.models import Survey, SurveyAnswer, SurveyQuestion, \ - SurveyQuestionAnswer -from gestioncof.models import Event, EventRegistration, EventOption, \ - EventOptionChoice -from gestioncof.models import EventCommentField, EventCommentValue, \ - CalendarSubscription -from gestioncof.models import CofProfile, Club +from bda.models import Spectacle, Tirage from gestioncof.decorators import buro_required, cof_required from gestioncof.forms import ( - UserForm, ProfileForm, - EventStatusFilterForm, SurveyForm, SurveyStatusFilterForm, - RegistrationUserForm, RegistrationProfileForm, EventForm, CalendarForm, - EventFormset, RegistrationPassUserForm, ClubsForm, GestioncofConfigForm + CalendarForm, + ClubsForm, + EventForm, + EventFormset, + EventStatusFilterForm, + GestioncofConfigForm, + ProfileForm, + RegistrationPassUserForm, + RegistrationProfileForm, + RegistrationUserForm, + SurveyForm, + SurveyStatusFilterForm, + UserForm, ) - -from bda.models import Tirage, Spectacle +from gestioncof.models import ( + CalendarSubscription, + Club, + CofProfile, + Event, + EventCommentField, + EventCommentValue, + EventOption, + EventOptionChoice, + EventRegistration, + Survey, + SurveyAnswer, + SurveyQuestion, + SurveyQuestionAnswer, +) +from utils.views.autocomplete import Select2QuerySetView @login_required def home(request): - data = {"surveys": Survey.objects.filter(old=False).all(), - "events": Event.objects.filter(old=False).all(), - "open_surveys": - Survey.objects.filter(survey_open=True, old=False).all(), - "open_events": - Event.objects.filter(registration_open=True, old=False).all(), - "active_tirages": Tirage.objects.filter(active=True).all(), - "open_tirages": - Tirage.objects.filter(active=True, - ouverture__lte=timezone.now()).all(), - "now": timezone.now()} + data = { + "surveys": Survey.objects.filter(old=False).all(), + "events": Event.objects.filter(old=False).all(), + "open_surveys": Survey.objects.filter(survey_open=True, old=False).all(), + "open_events": Event.objects.filter(registration_open=True, old=False).all(), + "active_tirages": Tirage.objects.filter(active=True).all(), + "open_tirages": Tirage.objects.filter( + active=True, ouverture__lte=timezone.now() + ).all(), + "now": timezone.now(), + } return render(request, "home.html", data) @@ -61,8 +76,8 @@ def login(request): if request.user.is_authenticated: return redirect("home") context = {} - if request.method == "GET" and 'next' in request.GET: - context['next'] = request.GET['next'] + if request.method == "GET" and "next" in request.GET: + context["next"] = request.GET["next"] return render(request, "login_switch.html", context) @@ -73,34 +88,33 @@ def login_ext(request): if not user.has_usable_password() or user.password in ("", "!"): profile, created = CofProfile.objects.get_or_create(user=user) if profile.login_clipper: - return render(request, "error.html", - {"error_type": "use_clipper_login"}) + return render( + request, "error.html", {"error_type": "use_clipper_login"} + ) else: - return render(request, "error.html", - {"error_type": "no_password"}) + return render(request, "error.html", {"error_type": "no_password"}) except User.DoesNotExist: pass context = {} - if request.method == "GET" and 'next' in request.GET: - context['next'] = request.GET['next'] - if request.method == "POST" and 'next' in request.POST: - context['next'] = request.POST['next'] - return django_login_view(request, template_name='login.html', - extra_context=context) + if request.method == "GET" and "next" in request.GET: + context["next"] = request.GET["next"] + if request.method == "POST" and "next" in request.POST: + context["next"] = request.POST["next"] + return django_login_view(request, template_name="login.html", extra_context=context) @login_required def logout(request, next_page=None): if next_page is None: - next_page = request.GET.get('next', None) + next_page = request.GET.get("next", None) - profile = getattr(request.user, 'profile', None) + profile = getattr(request.user, "profile", None) if profile and profile.login_clipper: - msg = _('Déconnexion de GestioCOF et CAS réussie. À bientôt {}.') + msg = _("Déconnexion de GestioCOF et CAS réussie. À bientôt {}.") logout_view = cas_logout_view else: - msg = _('Déconnexion de GestioCOF réussie. À bientôt {}.') + msg = _("Déconnexion de GestioCOF réussie. À bientôt {}.") logout_view = django_logout_view messages.success(request, msg.format(request.user.get_short_name())) @@ -110,8 +124,7 @@ def logout(request, next_page=None): @login_required def survey(request, survey_id): survey = get_object_or_404( - Survey.objects.prefetch_related('questions', 'questions__answers'), - id=survey_id, + Survey.objects.prefetch_related("questions", "questions__answers"), id=survey_id ) if not survey.survey_open or survey.old: raise Http404 @@ -119,10 +132,11 @@ def survey(request, survey_id): deleted = False if request.method == "POST": form = SurveyForm(request.POST, survey=survey) - if request.POST.get('delete'): + if request.POST.get("delete"): try: - current_answer = SurveyAnswer.objects.get(user=request.user, - survey=survey) + current_answer = SurveyAnswer.objects.get( + user=request.user, survey=survey + ) current_answer.delete() current_answer = None except SurveyAnswer.DoesNotExist: @@ -134,9 +148,9 @@ def survey(request, survey_id): if form.is_valid(): all_answers = [] for question_id, answers_ids in form.answers(): - question = get_object_or_404(SurveyQuestion, - id=question_id, - survey=survey) + question = get_object_or_404( + SurveyQuestion, id=question_id, survey=survey + ) if type(answers_ids) != list: answers_ids = [answers_ids] if not question.multi_answers and len(answers_ids) > 1: @@ -146,50 +160,48 @@ def survey(request, survey_id): continue answer_id = int(answer_id) answer = SurveyQuestionAnswer.objects.get( - id=answer_id, - survey_question=question) + id=answer_id, survey_question=question + ) all_answers.append(answer) try: current_answer = SurveyAnswer.objects.get( - user=request.user, survey=survey) + user=request.user, survey=survey + ) except SurveyAnswer.DoesNotExist: - current_answer = SurveyAnswer(user=request.user, - survey=survey) + current_answer = SurveyAnswer(user=request.user, survey=survey) current_answer.save() current_answer.answers = all_answers current_answer.save() success = True else: try: - current_answer = SurveyAnswer.objects.get(user=request.user, - survey=survey) - form = SurveyForm(survey=survey, - current_answers=current_answer.answers) + current_answer = SurveyAnswer.objects.get(user=request.user, survey=survey) + form = SurveyForm(survey=survey, current_answers=current_answer.answers) except SurveyAnswer.DoesNotExist: current_answer = None form = SurveyForm(survey=survey) # Messages if success: if deleted: - messages.success(request, - "Votre réponse a bien été supprimée") + messages.success(request, "Votre réponse a bien été supprimée") else: - messages.success(request, - "Votre réponse a bien été enregistrée ! Vous " - "pouvez cependant la modifier jusqu'à la fin " - "du sondage.") - return render(request, "gestioncof/survey.html", { - "survey": survey, - "form": form, - "current_answer": current_answer - }) + messages.success( + request, + "Votre réponse a bien été enregistrée ! Vous " + "pouvez cependant la modifier jusqu'à la fin " + "du sondage.", + ) + return render( + request, + "gestioncof/survey.html", + {"survey": survey, "form": form, "current_answer": current_answer}, + ) def get_event_form_choices(event, form): all_choices = [] for option_id, choices_ids in form.choices(): - option = get_object_or_404(EventOption, id=option_id, - event=event) + option = get_object_or_404(EventOption, id=option_id, event=event) if type(choices_ids) != list: choices_ids = [choices_ids] if not option.multi_choices and len(choices_ids) > 1: @@ -198,22 +210,19 @@ def get_event_form_choices(event, form): if not choice_id: continue choice_id = int(choice_id) - choice = EventOptionChoice.objects.get( - id=choice_id, - event_option=option) + choice = EventOptionChoice.objects.get(id=choice_id, event_option=option) all_choices.append(choice) return all_choices def update_event_form_comments(event, form, registration): for commentfield_id, value in form.comments(): - field = get_object_or_404(EventCommentField, id=commentfield_id, - event=event) + field = get_object_or_404(EventCommentField, id=commentfield_id, event=event) if value == field.default: continue (storage, _) = EventCommentValue.objects.get_or_create( - commentfield=field, - registration=registration) + commentfield=field, registration=registration + ) storage.content = value storage.save() @@ -228,27 +237,29 @@ def event(request, event_id): form = EventForm(request.POST, event=event) if form.is_valid(): all_choices = get_event_form_choices(event, form) - (current_registration, _) = \ - EventRegistration.objects.get_or_create(user=request.user, - event=event) + (current_registration, _) = EventRegistration.objects.get_or_create( + user=request.user, event=event + ) current_registration.options = all_choices current_registration.save() success = True else: try: - current_registration = \ - EventRegistration.objects.get(user=request.user, event=event) - form = EventForm(event=event, - current_choices=current_registration.options) + current_registration = EventRegistration.objects.get( + user=request.user, event=event + ) + form = EventForm(event=event, current_choices=current_registration.options) except EventRegistration.DoesNotExist: form = EventForm(event=event) # Messages if success: - messages.success(request, "Votre inscription a bien été enregistrée ! " - "Vous pouvez cependant la modifier jusqu'à " - "la fin des inscriptions.") - return render(request, "gestioncof/event.html", - {"event": event, "form": form}) + messages.success( + request, + "Votre inscription a bien été enregistrée ! " + "Vous pouvez cependant la modifier jusqu'à " + "la fin des inscriptions.", + ) + return render(request, "gestioncof/event.html", {"event": event, "form": form}) def clean_post_for_status(initial): @@ -272,19 +283,21 @@ def event_status(request, event_id): if value == "yes": registrations_query = registrations_query.filter(paid=True) elif value == "no": - registrations_query = registrations_query.filter( - paid=False) + registrations_query = registrations_query.filter(paid=False) continue - choice = get_object_or_404(EventOptionChoice, id=choice_id, - event_option__id=option_id) + choice = get_object_or_404( + EventOptionChoice, id=choice_id, event_option__id=option_id + ) if value == "none": continue if value == "yes": registrations_query = registrations_query.filter( - options__id__exact=choice.id) + options__id__exact=choice.id + ) elif value == "no": registrations_query = registrations_query.exclude( - options__id__exact=choice.id) + options__id__exact=choice.id + ) user_choices = registrations_query.prefetch_related("user").all() options = EventOption.objects.filter(event=event).all() choices_count = {} @@ -294,10 +307,17 @@ def event_status(request, event_id): for user_choice in user_choices: for choice in user_choice.options.all(): choices_count[choice.id] += 1 - return render(request, "event_status.html", - {"event": event, "user_choices": user_choices, - "options": options, "choices_count": choices_count, - "form": form}) + return render( + request, + "event_status.html", + { + "event": event, + "user_choices": user_choices, + "options": options, + "choices_count": choices_count, + "form": form, + }, + ) @buro_required @@ -308,16 +328,15 @@ def survey_status(request, survey_id): form = SurveyStatusFilterForm(post_data or None, survey=survey) if form.is_valid(): for question_id, answer_id, value in form.filters(): - answer = get_object_or_404(SurveyQuestionAnswer, id=answer_id, - survey_question__id=question_id) + answer = get_object_or_404( + SurveyQuestionAnswer, id=answer_id, survey_question__id=question_id + ) if value == "none": continue if value == "yes": - answers_query = answers_query.filter( - answers__id__exact=answer.id) + answers_query = answers_query.filter(answers__id__exact=answer.id) elif value == "no": - answers_query = answers_query.exclude( - answers__id__exact=answer.id) + answers_query = answers_query.exclude(answers__id__exact=answer.id) user_answers = answers_query.prefetch_related("user").all() questions = SurveyQuestion.objects.filter(survey=survey).all() answers_count = {} @@ -327,10 +346,17 @@ def survey_status(request, survey_id): for user_answer in user_answers: for answer in user_answer.answers.all(): answers_count[answer.id] += 1 - return render(request, "survey_status.html", - {"survey": survey, "user_answers": user_answers, - "questions": questions, "answers_count": answers_count, - "form": form}) + return render( + request, + "survey_status.html", + { + "survey": survey, + "user_answers": user_answers, + "questions": questions, + "answers_count": answers_count, + "form": form, + }, + ) @cof_required @@ -343,22 +369,18 @@ def profile(request): if user_form.is_valid() and profile_form.is_valid(): user_form.save() profile_form.save() - messages.success( - request, - _("Votre profil a été mis à jour avec succès !") - ) + messages.success(request, _("Votre profil a été mis à jour avec succès !")) context = {"user_form": user_form, "profile_form": profile_form} return render(request, "gestioncof/profile.html", context) def registration_set_ro_fields(user_form, profile_form): - user_form.fields['username'].widget.attrs['readonly'] = True - profile_form.fields['login_clipper'].widget.attrs['readonly'] = True + user_form.fields["username"].widget.attrs["readonly"] = True + profile_form.fields["login_clipper"].widget.attrs["readonly"] = True @buro_required -def registration_form2(request, login_clipper=None, username=None, - fullname=None): +def registration_form2(request, login_clipper=None, username=None, fullname=None): events = Event.objects.filter(old=False).all() member = None if login_clipper: @@ -369,20 +391,24 @@ def registration_form2(request, login_clipper=None, username=None, except User.DoesNotExist: # new user, but prefill # user - user_form = RegistrationUserForm(initial={ - 'username': login_clipper, - 'email': "%s@clipper.ens.fr" % login_clipper}) + user_form = RegistrationUserForm( + initial={ + "username": login_clipper, + "email": "%s@clipper.ens.fr" % login_clipper, + } + ) if fullname: bits = fullname.split(" ") - user_form.fields['first_name'].initial = bits[0] + user_form.fields["first_name"].initial = bits[0] if len(bits) > 1: - user_form.fields['last_name'].initial = " ".join(bits[1:]) + user_form.fields["last_name"].initial = " ".join(bits[1:]) # profile - profile_form = RegistrationProfileForm(initial={ - 'login_clipper': login_clipper}) + profile_form = RegistrationProfileForm( + initial={"login_clipper": login_clipper} + ) registration_set_ro_fields(user_form, profile_form) # events & clubs - event_formset = EventFormset(events=events, prefix='events') + event_formset = EventFormset(events=events, prefix="events") clubs_form = ClubsForm() if username: member = get_object_or_404(User, username=username) @@ -396,26 +422,33 @@ def registration_form2(request, login_clipper=None, username=None, for event in events: try: current_registrations.append( - EventRegistration.objects.get(user=member, event=event)) + EventRegistration.objects.get(user=member, event=event) + ) except EventRegistration.DoesNotExist: current_registrations.append(None) event_formset = EventFormset( - events=events, prefix='events', - current_registrations=current_registrations) + events=events, prefix="events", current_registrations=current_registrations + ) # Clubs - clubs_form = ClubsForm(initial={'clubs': member.clubs.all()}) + clubs_form = ClubsForm(initial={"clubs": member.clubs.all()}) elif not login_clipper: # new user user_form = RegistrationPassUserForm() profile_form = RegistrationProfileForm() - event_formset = EventFormset(events=events, prefix='events') + event_formset = EventFormset(events=events, prefix="events") clubs_form = ClubsForm() - return render(request, "gestioncof/registration_form.html", - {"member": member, "login_clipper": login_clipper, - "user_form": user_form, - "profile_form": profile_form, - "event_formset": event_formset, - "clubs_form": clubs_form}) + return render( + request, + "gestioncof/registration_form.html", + { + "member": member, + "login_clipper": login_clipper, + "user_form": user_form, + "profile_form": profile_form, + "event_formset": event_formset, + "clubs_form": clubs_form, + }, + ) @buro_required @@ -429,15 +462,14 @@ def registration(request): # Remplissage des formulaires # ----- - if 'password1' in request_dict or 'password2' in request_dict: + if "password1" in request_dict or "password2" in request_dict: user_form = RegistrationPassUserForm(request_dict) else: user_form = RegistrationUserForm(request_dict) profile_form = RegistrationProfileForm(request_dict) clubs_form = ClubsForm(request_dict) events = Event.objects.filter(old=False).all() - event_formset = EventFormset(events=events, data=request_dict, - prefix='events') + event_formset = EventFormset(events=events, data=request_dict, prefix="events") if "user_exists" in request_dict and request_dict["user_exists"]: username = request_dict["username"] try: @@ -459,38 +491,44 @@ def registration(request): profile, _ = CofProfile.objects.get_or_create(user=member) was_cof = profile.is_cof # Maintenant on remplit le formulaire de profil - profile_form = RegistrationProfileForm(request_dict, - instance=profile) - if (profile_form.is_valid() and event_formset.is_valid() - and clubs_form.is_valid()): + profile_form = RegistrationProfileForm(request_dict, instance=profile) + if ( + profile_form.is_valid() + and event_formset.is_valid() + and clubs_form.is_valid() + ): # Enregistrement du profil profile = profile_form.save() if profile.is_cof and not was_cof: send_custom_mail( - "welcome", "cof@ens.fr", [member.email], - context={'member': member}, + "welcome", + "cof@ens.fr", + [member.email], + context={"member": member}, ) # Enregistrement des inscriptions aux événements for form in event_formset: - if 'status' not in form.cleaned_data: - form.cleaned_data['status'] = 'no' - if form.cleaned_data['status'] == 'no': + if "status" not in form.cleaned_data: + form.cleaned_data["status"] = "no" + if form.cleaned_data["status"] == "no": try: - current_registration = EventRegistration.objects \ - .get(user=member, event=form.event) + current_registration = EventRegistration.objects.get( + user=member, event=form.event + ) current_registration.delete() except EventRegistration.DoesNotExist: pass continue all_choices = get_event_form_choices(form.event, form) - (current_registration, created_reg) = \ - EventRegistration.objects.get_or_create( - user=member, event=form.event) - update_event_form_comments(form.event, form, - current_registration) + ( + current_registration, + created_reg, + ) = EventRegistration.objects.get_or_create( + user=member, event=form.event + ) + update_event_form_comments(form.event, form, current_registration) current_registration.options = all_choices - current_registration.paid = \ - (form.cleaned_data['status'] == 'paid') + current_registration.paid = form.cleaned_data["status"] == "paid" current_registration.save() # if form.event.title == "Mega 15" and created_reg: # field = EventCommentField.objects.get( @@ -508,7 +546,7 @@ def registration(request): # send_custom_mail(...) # Enregistrement des inscriptions aux clubs member.clubs.clear() - for club in clubs_form.cleaned_data['clubs']: + for club in clubs_form.cleaned_data["clubs"]: club.membres.add(member) club.save() @@ -516,20 +554,29 @@ def registration(request): # Success # --- - msg = ("L'inscription de {:s} ({:s}) a été " - "enregistrée avec succès." - .format(member.get_full_name(), member.email)) + msg = ( + "L'inscription de {:s} ({:s}) a été " + "enregistrée avec succès.".format( + member.get_full_name(), member.email + ) + ) if profile.is_cof: msg += "\nIl est désormais membre du COF n°{:d} !".format( - member.profile.id) - messages.success(request, msg, extra_tags='safe') - return render(request, "gestioncof/registration_post.html", - {"user_form": user_form, - "profile_form": profile_form, - "member": member, - "login_clipper": login_clipper, - "event_formset": event_formset, - "clubs_form": clubs_form}) + member.profile.id + ) + messages.success(request, msg, extra_tags="safe") + return render( + request, + "gestioncof/registration_post.html", + { + "user_form": user_form, + "profile_form": profile_form, + "member": member, + "login_clipper": login_clipper, + "event_formset": event_formset, + "clubs_form": clubs_form, + }, + ) else: return render(request, "registration.html") @@ -545,13 +592,14 @@ def membres_club(request, name): # ou respo du club. user = request.user club = get_object_or_404(Club, name=name) - if not request.user.profile.is_buro \ - and club not in user.clubs_geres.all(): - return HttpResponseForbidden('

    Permission denied

    ') + if not request.user.profile.is_buro and club not in user.clubs_geres.all(): + return HttpResponseForbidden("

    Permission denied

    ") members_no_respo = club.membres.exclude(clubs_geres=club).all() - return render(request, 'membres_clubs.html', - {'club': club, - 'members_no_respo': members_no_respo}) + return render( + request, + "membres_clubs.html", + {"club": club, "members_no_respo": members_no_respo}, + ) @buro_required @@ -564,31 +612,41 @@ def change_respo(request, club_name, user_id): club.respos.add(user) else: raise Http404 - return redirect('membres-club', name=club_name) + return redirect("membres-club", name=club_name) @cof_required def liste_clubs(request): clubs = Club.objects if request.user.profile.is_buro: - data = {'owned_clubs': clubs.all()} + data = {"owned_clubs": clubs.all()} else: - data = {'owned_clubs': request.user.clubs_geres.all(), - 'other_clubs': clubs.exclude(respos=request.user)} - return render(request, 'liste_clubs.html', data) + data = { + "owned_clubs": request.user.clubs_geres.all(), + "other_clubs": clubs.exclude(respos=request.user), + } + return render(request, "liste_clubs.html", data) @buro_required def export_members(request): - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename=membres_cof.csv' + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = "attachment; filename=membres_cof.csv" writer = unicodecsv.writer(response) for profile in CofProfile.objects.filter(is_cof=True).all(): user = profile.user - bits = [user.id, user.username, user.first_name, user.last_name, - user.email, profile.phone, profile.occupation, - profile.departement, profile.type_cotiz] + bits = [ + user.id, + user.username, + user.first_name, + user.last_name, + user.email, + profile.phone, + profile.occupation, + profile.departement, + profile.type_cotiz, + ] writer.writerow([str(bit) for bit in bits]) return response @@ -608,18 +666,24 @@ MEGA_ORGA = "Orga" def csv_export_mega(filename, qs): - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename=' + filename + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = "attachment; filename=" + filename writer = unicodecsv.writer(response) for reg in qs.all(): user = reg.user profile = user.profile - comments = "---".join( - [comment.content for comment in reg.comments.all()]) - bits = [user.username, user.first_name, user.last_name, user.email, - profile.phone, user.id, - profile.comments if profile.comments else "", comments] + comments = "---".join([comment.content for comment in reg.comments.all()]) + bits = [ + user.username, + user.first_name, + user.last_name, + user.email, + profile.phone, + user.id, + profile.comments if profile.comments else "", + comments, + ] writer.writerow([str(bit) for bit in bits]) @@ -628,9 +692,9 @@ def csv_export_mega(filename, qs): @buro_required def export_mega_remarksonly(request): - filename = 'remarques_mega_{}.csv'.format(MEGA_YEAR) - response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment; filename=' + filename + filename = "remarques_mega_{}.csv".format(MEGA_YEAR) + response = HttpResponse(content_type="text/csv") + response["Content-Disposition"] = "attachment; filename=" + filename writer = unicodecsv.writer(response) event = Event.objects.get(title=MEGA_EVENT_NAME) @@ -639,8 +703,16 @@ def export_mega_remarksonly(request): reg = val.registration user = reg.user profile = user.profile - bits = [user.username, user.first_name, user.last_name, user.email, - profile.phone, profile.id, profile.comments, val.content] + bits = [ + user.username, + user.first_name, + user.last_name, + user.email, + profile.phone, + profile.id, + profile.comments, + val.content, + ] writer.writerow([str(bit) for bit in bits]) return response @@ -672,7 +744,7 @@ def export_mega_orgas(request): qs = EventRegistration.objects.filter(event=event).filter( options__id=participant_type ) - return csv_export_mega('orgas_mega_{}.csv'.format(MEGA_YEAR), qs) + return csv_export_mega("orgas_mega_{}.csv".format(MEGA_YEAR), qs) @buro_required @@ -683,15 +755,15 @@ def export_mega_participants(request): qs = EventRegistration.objects.filter(event=event).filter( options__id=participant_type ) - return csv_export_mega('conscrits_mega_{}.csv'.format(MEGA_YEAR), qs) + return csv_export_mega("conscrits_mega_{}.csv".format(MEGA_YEAR), qs) @buro_required def export_mega(request): event = Event.objects.filter(title=MEGA_EVENT_NAME) - qs = EventRegistration.objects.filter(event=event) \ - .order_by("user__username") - return csv_export_mega('all_mega_{}.csv'.format(MEGA_YEAR), qs) + qs = EventRegistration.objects.filter(event=event).order_by("user__username") + return csv_export_mega("all_mega_{}.csv".format(MEGA_YEAR), qs) + # ------------------------------ # Fin des exports Mega hardcodés @@ -706,32 +778,28 @@ def utile_cof(request): @buro_required def utile_bda(request): tirages = Tirage.objects.all() - return render(request, "utile_bda.html", {'tirages': tirages}) + return render(request, "utile_bda.html", {"tirages": tirages}) @buro_required def liste_bdadiff(request): titre = "BdA diffusion" personnes = CofProfile.objects.filter(mailing_bda=True, is_cof=True).all() - return render(request, "liste_mails.html", - {"titre": titre, "personnes": personnes}) + return render(request, "liste_mails.html", {"titre": titre, "personnes": personnes}) @buro_required def liste_bdarevente(request): titre = "BdA revente" - personnes = CofProfile.objects.filter(mailing_bda_revente=True, - is_cof=True).all() - return render(request, "liste_mails.html", {"titre": titre, - "personnes": personnes}) + personnes = CofProfile.objects.filter(mailing_bda_revente=True, is_cof=True).all() + return render(request, "liste_mails.html", {"titre": titre, "personnes": personnes}) @buro_required def liste_diffcof(request): titre = "Diffusion COF" personnes = CofProfile.objects.filter(mailing_cof=True, is_cof=True).all() - return render(request, "liste_mails.html", {"titre": titre, - "personnes": personnes}) + return render(request, "liste_mails.html", {"titre": titre, "personnes": personnes}) @cof_required @@ -740,7 +808,7 @@ def calendar(request): instance = CalendarSubscription.objects.get(user=request.user) except CalendarSubscription.DoesNotExist: instance = None - if request.method == 'POST': + if request.method == "POST": form = CalendarForm(request.POST, instance=instance) if form.is_valid(): subscription = form.save(commit=False) @@ -749,19 +817,26 @@ def calendar(request): subscription.token = uuid.uuid4() subscription.save() form.save_m2m() - messages.success(request, - "Calendrier mis à jour avec succès.") - return render(request, "gestioncof/calendar_subscription.html", - {'form': form, - 'token': str(subscription.token)}) + messages.success(request, "Calendrier mis à jour avec succès.") + return render( + request, + "gestioncof/calendar_subscription.html", + {"form": form, "token": str(subscription.token)}, + ) else: messages.error(request, "Formulaire incorrect.") - return render(request, "gestioncof/calendar_subscription.html", - {'form': form}) + return render( + request, "gestioncof/calendar_subscription.html", {"form": form} + ) else: - return render(request, "gestioncof/calendar_subscription.html", - {'form': CalendarForm(instance=instance), - 'token': instance.token if instance else None}) + return render( + request, + "gestioncof/calendar_subscription.html", + { + "form": CalendarForm(instance=instance), + "token": instance.token if instance else None, + }, + ) def calendar_ics(request, token): @@ -769,33 +844,33 @@ def calendar_ics(request, token): shows = subscription.other_shows.all() if subscription.subscribe_to_my_shows: shows |= Spectacle.objects.filter( - attribues__participant__user=subscription.user, - tirage__active=True) + attribues__participant__user=subscription.user, tirage__active=True + ) shows = shows.distinct() vcal = Calendar() site = Site.objects.get_current() for show in shows: vevent = Vevent() - vevent.add('dtstart', show.date) - vevent.add('dtend', show.date + timedelta(seconds=7200)) - vevent.add('summary', show.title) - vevent.add('location', show.location.name) - vevent.add('uid', 'show-{:d}-{:d}@{:s}'.format( - show.pk, show.tirage_id, site.domain)) + vevent.add("dtstart", show.date) + vevent.add("dtend", show.date + timedelta(seconds=7200)) + vevent.add("summary", show.title) + vevent.add("location", show.location.name) + vevent.add( + "uid", "show-{:d}-{:d}@{:s}".format(show.pk, show.tirage_id, site.domain) + ) vcal.add_component(vevent) if subscription.subscribe_to_events: for event in Event.objects.filter(old=False).all(): vevent = Vevent() - vevent.add('dtstart', event.start_date) - vevent.add('dtend', event.end_date) - vevent.add('summary', event.title) - vevent.add('location', event.location) - vevent.add('description', event.description) - vevent.add('uid', 'event-{:d}@{:s}'.format( - event.pk, site.domain)) + vevent.add("dtstart", event.start_date) + vevent.add("dtend", event.end_date) + vevent.add("summary", event.title) + vevent.add("location", event.location) + vevent.add("description", event.description) + vevent.add("uid", "event-{:d}@{:s}".format(event.pk, site.domain)) vcal.add_component(vevent) response = HttpResponse(content=vcal.to_ical()) - response['Content-Type'] = "text/calendar" + response["Content-Type"] = "text/calendar" return response @@ -823,7 +898,7 @@ class ConfigUpdate(FormView): class UserAutocomplete(Select2QuerySetView): model = User - search_fields = ('username', 'first_name', 'last_name') + search_fields = ("username", "first_name", "last_name") user_autocomplete = buro_required(UserAutocomplete.as_view()) diff --git a/gestioncof/widgets.py b/gestioncof/widgets.py index 906f7b15..49125896 100644 --- a/gestioncof/widgets.py +++ b/gestioncof/widgets.py @@ -1,5 +1,5 @@ -from django.forms.widgets import Widget from django.forms.utils import flatatt +from django.forms.widgets import Widget from django.utils.safestring import mark_safe @@ -13,8 +13,8 @@ class TriStateCheckbox(Widget): def render(self, name, value, attrs=None, choices=()): if value is None: - value = 'none' - attrs['value'] = value + value = "none" + attrs["value"] = value final_attrs = self.build_attrs(self.attrs, attrs) - output = ["" % flatatt(final_attrs)] - return mark_safe('\n'.join(output)) + output = ['' % flatatt(final_attrs)] + return mark_safe("\n".join(output)) diff --git a/kfet/__init__.py b/kfet/__init__.py index 5d6c8f97..42ea33b1 100644 --- a/kfet/__init__.py +++ b/kfet/__init__.py @@ -1 +1 @@ -default_app_config = 'kfet.apps.KFetConfig' +default_app_config = "kfet.apps.KFetConfig" diff --git a/kfet/apps.py b/kfet/apps.py index 7a6c97a2..f3c7b07b 100644 --- a/kfet/apps.py +++ b/kfet/apps.py @@ -2,7 +2,7 @@ from django.apps import AppConfig class KFetConfig(AppConfig): - name = 'kfet' + name = "kfet" verbose_name = "Application K-Fêt" def ready(self): @@ -11,4 +11,5 @@ class KFetConfig(AppConfig): def register_config(self): import djconfig from kfet.forms import KFetConfigForm + djconfig.register(KFetConfigForm) diff --git a/kfet/auth/__init__.py b/kfet/auth/__init__.py index 00926030..ef2486a7 100644 --- a/kfet/auth/__init__.py +++ b/kfet/auth/__init__.py @@ -1,4 +1,4 @@ -default_app_config = 'kfet.auth.apps.KFetAuthConfig' +default_app_config = "kfet.auth.apps.KFetAuthConfig" -KFET_GENERIC_USERNAME = 'kfet_genericteam' -KFET_GENERIC_TRIGRAMME = 'GNR' +KFET_GENERIC_USERNAME = "kfet_genericteam" +KFET_GENERIC_TRIGRAMME = "GNR" diff --git a/kfet/auth/apps.py b/kfet/auth/apps.py index d91931f5..5b4fe7fd 100644 --- a/kfet/auth/apps.py +++ b/kfet/auth/apps.py @@ -4,11 +4,12 @@ from django.utils.translation import ugettext_lazy as _ class KFetAuthConfig(AppConfig): - name = 'kfet.auth' - label = 'kfetauth' + name = "kfet.auth" + label = "kfetauth" verbose_name = _("K-Fêt - Authentification et Autorisation") def ready(self): from . import signals # noqa from .utils import setup_kfet_generic_user + post_migrate.connect(setup_kfet_generic_user, sender=self) diff --git a/kfet/auth/backends.py b/kfet/auth/backends.py index d8ef3001..55e18458 100644 --- a/kfet/auth/backends.py +++ b/kfet/auth/backends.py @@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model + from kfet.models import Account, GenericTeamToken from .utils import get_kfet_generic_user @@ -12,11 +13,7 @@ class BaseKFetBackend: Add extra select related up to Account. """ try: - return ( - User.objects - .select_related('profile__account_kfet') - .get(pk=user_id) - ) + return User.objects.select_related("profile__account_kfet").get(pk=user_id) except User.DoesNotExist: return None diff --git a/kfet/auth/context_processors.py b/kfet/auth/context_processors.py index 7b59b88b..183cc56a 100644 --- a/kfet/auth/context_processors.py +++ b/kfet/auth/context_processors.py @@ -2,9 +2,6 @@ from django.contrib.auth.context_processors import PermWrapper def temporary_auth(request): - if hasattr(request, 'real_user'): - return { - 'user': request.real_user, - 'perms': PermWrapper(request.real_user), - } + if hasattr(request, "real_user"): + return {"user": request.real_user, "perms": PermWrapper(request.real_user)} return {} diff --git a/kfet/auth/fields.py b/kfet/auth/fields.py index 28ba1c9e..a5544787 100644 --- a/kfet/auth/fields.py +++ b/kfet/auth/fields.py @@ -5,15 +5,12 @@ from django.forms import widgets class KFetPermissionsField(forms.ModelMultipleChoiceField): - def __init__(self, *args, **kwargs): queryset = Permission.objects.filter( - content_type__in=ContentType.objects.filter(app_label="kfet"), + content_type__in=ContentType.objects.filter(app_label="kfet") ) super().__init__( - queryset=queryset, - widget=widgets.CheckboxSelectMultiple, - *args, **kwargs + queryset=queryset, widget=widgets.CheckboxSelectMultiple, *args, **kwargs ) def label_from_instance(self, obj): diff --git a/kfet/auth/forms.py b/kfet/auth/forms.py index 876e8814..b1628af0 100644 --- a/kfet/auth/forms.py +++ b/kfet/auth/forms.py @@ -8,11 +8,11 @@ class GroupForm(forms.ModelForm): permissions = KFetPermissionsField() def clean_name(self): - name = self.cleaned_data['name'] - return 'K-Fêt %s' % name + name = self.cleaned_data["name"] + return "K-Fêt %s" % name def clean_permissions(self): - kfet_perms = self.cleaned_data['permissions'] + kfet_perms = self.cleaned_data["permissions"] # TODO: With Django >=1.11, the QuerySet method 'difference' can be # used. # other_groups = self.instance.permissions.difference( @@ -21,28 +21,29 @@ class GroupForm(forms.ModelForm): if self.instance.pk is None: return kfet_perms other_perms = self.instance.permissions.exclude( - pk__in=[p.pk for p in self.fields['permissions'].queryset], + pk__in=[p.pk for p in self.fields["permissions"].queryset] ) return list(kfet_perms) + list(other_perms) class Meta: model = Group - fields = ['name', 'permissions'] + fields = ["name", "permissions"] class UserGroupForm(forms.ModelForm): groups = forms.ModelMultipleChoiceField( - Group.objects.filter(name__icontains='K-Fêt'), - label='Statut équipe', - required=False) + Group.objects.filter(name__icontains="K-Fêt"), + label="Statut équipe", + required=False, + ) def clean_groups(self): - kfet_groups = self.cleaned_data.get('groups') + kfet_groups = self.cleaned_data.get("groups") if self.instance.pk is None: return kfet_groups - other_groups = self.instance.groups.exclude(name__icontains='K-Fêt') + other_groups = self.instance.groups.exclude(name__icontains="K-Fêt") return list(kfet_groups) + list(other_groups) class Meta: model = User - fields = ['groups'] + fields = ["groups"] diff --git a/kfet/auth/middleware.py b/kfet/auth/middleware.py index 2f3bd33b..43a920e1 100644 --- a/kfet/auth/middleware.py +++ b/kfet/auth/middleware.py @@ -12,21 +12,19 @@ class TemporaryAuthMiddleware: values from CofProfile and Account of this user. """ + def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.user.is_authenticated: # avoid multiple db accesses in views and templates - request.user = ( - User.objects - .select_related('profile__account_kfet') - .get(pk=request.user.pk) + request.user = User.objects.select_related("profile__account_kfet").get( + pk=request.user.pk ) temp_request_user = AccountBackend().authenticate( - request, - kfet_password=self.get_kfet_password(request), + request, kfet_password=self.get_kfet_password(request) ) if temp_request_user: @@ -36,7 +34,4 @@ class TemporaryAuthMiddleware: return self.get_response(request) def get_kfet_password(self, request): - return ( - request.META.get('HTTP_KFETPASSWORD') or - request.POST.get('KFETPASSWORD') - ) + return request.META.get("HTTP_KFETPASSWORD") or request.POST.get("KFETPASSWORD") diff --git a/kfet/auth/migrations/0001_initial.py b/kfet/auth/migrations/0001_initial.py index 061570a8..caf5d786 100644 --- a/kfet/auth/migrations/0001_initial.py +++ b/kfet/auth/migrations/0001_initial.py @@ -7,18 +7,26 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('auth', '0006_require_contenttypes_0002'), + ("auth", "0006_require_contenttypes_0002"), # Following dependency allows using Account model to set up the kfet # generic user in post_migrate receiver. - ('kfet', '0058_delete_genericteamtoken'), + ("kfet", "0058_delete_genericteamtoken"), ] operations = [ migrations.CreateModel( - name='GenericTeamToken', + name="GenericTeamToken", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)), - ('token', models.CharField(unique=True, max_length=50)), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + serialize=False, + primary_key=True, + ), + ), + ("token", models.CharField(unique=True, max_length=50)), ], - ), + ) ] diff --git a/kfet/auth/models.py b/kfet/auth/models.py index ecd40091..73a70c22 100644 --- a/kfet/auth/models.py +++ b/kfet/auth/models.py @@ -3,7 +3,6 @@ from django.utils.crypto import get_random_string class GenericTeamTokenManager(models.Manager): - def create_token(self): token = get_random_string(50) while self.filter(token=token).exists(): diff --git a/kfet/auth/signals.py b/kfet/auth/signals.py index 3d7af18b..c13cde09 100644 --- a/kfet/auth/signals.py +++ b/kfet/auth/signals.py @@ -19,22 +19,26 @@ def suggest_auth_generic(sender, request, user, **kwargs): - logged in user is a kfet staff member (except the generic user). """ # Filter against the next page. - if not(hasattr(request, 'GET') and 'next' in request.GET): + if not (hasattr(request, "GET") and "next" in request.GET): return - next_page = request.GET['next'] - generic_url = reverse('kfet.login.generic') + next_page = request.GET["next"] + generic_url = reverse("kfet.login.generic") - if not('k-fet' in next_page and not next_page.startswith(generic_url)): + if not ("k-fet" in next_page and not next_page.startswith(generic_url)): return # Filter against the logged in user. - if not(user.has_perm('kfet.is_team') and user != get_kfet_generic_user()): + if not (user.has_perm("kfet.is_team") and user != get_kfet_generic_user()): return # Seems legit to add message. text = _("K-Fêt — Ouvrir une session partagée ?") - messages.info(request, mark_safe( - '{}' - .format(generic_url, text) - )) + messages.info( + request, + mark_safe( + '{}'.format( + generic_url, text + ) + ), + ) diff --git a/kfet/auth/tests.py b/kfet/auth/tests.py index 62f870e8..e4330eef 100644 --- a/kfet/auth/tests.py +++ b/kfet/auth/tests.py @@ -1,8 +1,8 @@ from unittest import mock +from django.contrib.auth.models import AnonymousUser, Group, Permission, User from django.core import signing from django.core.urlresolvers import reverse -from django.contrib.auth.models import AnonymousUser, Group, Permission, User from django.test import RequestFactory, TestCase from kfet.forms import UserGroupForm @@ -15,11 +15,11 @@ from .models import GenericTeamToken from .utils import get_kfet_generic_user from .views import GenericLoginView - ## # Forms ## + class UserGroupFormTests(TestCase): """Test suite for UserGroupForm.""" @@ -31,8 +31,7 @@ class UserGroupFormTests(TestCase): prefix_name = "K-Fêt " names = ["Group 1", "Group 2", "Group 3"] self.kfet_groups = [ - Group.objects.create(name=prefix_name+name) - for name in names + Group.objects.create(name=prefix_name + name) for name in names ] # create a non-K-Fêt group @@ -41,11 +40,9 @@ class UserGroupFormTests(TestCase): def test_choices(self): """Only K-Fêt groups are selectable.""" form = UserGroupForm(instance=self.user) - groups_field = form.fields['groups'] + groups_field = form.fields["groups"] self.assertQuerysetEqual( - groups_field.queryset, - [repr(g) for g in self.kfet_groups], - ordered=False, + groups_field.queryset, [repr(g) for g in self.kfet_groups], ordered=False ) def test_keep_others(self): @@ -56,9 +53,7 @@ class UserGroupFormTests(TestCase): user.groups.add(self.other_group) # add user to some K-Fêt groups through UserGroupForm - data = { - 'groups': [group.pk for group in self.kfet_groups], - } + data = {"groups": [group.pk for group in self.kfet_groups]} form = UserGroupForm(data, instance=user) form.is_valid() @@ -71,7 +66,6 @@ class UserGroupFormTests(TestCase): class KFetGenericUserTests(TestCase): - def test_exists(self): """ The account is set up when app is ready, so it should exist. @@ -86,44 +80,39 @@ class KFetGenericUserTests(TestCase): # Backends ## -class AccountBackendTests(TestCase): +class AccountBackendTests(TestCase): def setUp(self): - self.request = RequestFactory().get('/') + self.request = RequestFactory().get("/") def test_valid(self): - acc = Account(trigramme='000') - acc.change_pwd('valid') - acc.save({'username': 'user'}) + acc = Account(trigramme="000") + acc.change_pwd("valid") + acc.save({"username": "user"}) - auth = AccountBackend().authenticate( - self.request, kfet_password='valid') + auth = AccountBackend().authenticate(self.request, kfet_password="valid") self.assertEqual(auth, acc.user) def test_invalid(self): - auth = AccountBackend().authenticate( - self.request, kfet_password='invalid') + auth = AccountBackend().authenticate(self.request, kfet_password="invalid") self.assertIsNone(auth) class GenericBackendTests(TestCase): - def setUp(self): - self.request = RequestFactory().get('/') + self.request = RequestFactory().get("/") def test_valid(self): token = GenericTeamToken.objects.create_token() - auth = GenericBackend().authenticate( - self.request, kfet_token=token.token) + auth = GenericBackend().authenticate(self.request, kfet_token=token.token) self.assertEqual(auth, get_kfet_generic_user()) self.assertEqual(GenericTeamToken.objects.all().count(), 0) def test_invalid(self): - auth = GenericBackend().authenticate( - self.request, kfet_token='invalid') + auth = GenericBackend().authenticate(self.request, kfet_token="invalid") self.assertIsNone(auth) @@ -131,78 +120,74 @@ class GenericBackendTests(TestCase): # Views ## -class GenericLoginViewTests(TestCase): +class GenericLoginViewTests(TestCase): def setUp(self): - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) - user_acc = Account(trigramme='000') - user_acc.save({'username': 'user'}) + user_acc = Account(trigramme="000") + user_acc.save({"username": "user"}) self.user = user_acc.user - self.user.set_password('user') + self.user.set_password("user") self.user.save() - team_acc = Account(trigramme='100') - team_acc.save({'username': 'team'}) + team_acc = Account(trigramme="100") + team_acc.save({"username": "team"}) self.team = team_acc.user - self.team.set_password('team') + self.team.set_password("team") self.team.save() self.team.user_permissions.add( - Permission.objects.get( - content_type__app_label='kfet', codename='is_team'), + Permission.objects.get(content_type__app_label="kfet", codename="is_team") ) - self.url = reverse('kfet.login.generic') + self.url = reverse("kfet.login.generic") self.generic_user = get_kfet_generic_user() def test_url(self): - self.assertEqual(self.url, '/k-fet/login/generic') + self.assertEqual(self.url, "/k-fet/login/generic") def test_notoken_get(self): """ Send confirmation for user to emit POST request, instead of GET. """ - self.client.login(username='team', password='team') + self.client.login(username="team", password="team") r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertTemplateUsed(r, 'kfet/confirm_form.html') + self.assertTemplateUsed(r, "kfet/confirm_form.html") def test_notoken_post(self): """ POST request without token in COOKIES sets a token and redirects to logout url. """ - self.client.login(username='team', password='team') + self.client.login(username="team", password="team") r = self.client.post(self.url) self.assertRedirects( - r, '/logout?next={}'.format(self.url), - fetch_redirect_response=False, + r, "/logout?next={}".format(self.url), fetch_redirect_response=False ) def test_notoken_not_team(self): """ Logged in user must be a team user to initiate login as generic user. """ - self.client.login(username='user', password='user') + self.client.login(username="user", password="user") # With GET. r = self.client.get(self.url) self.assertRedirects( - r, '/login?next={}'.format(self.url), - fetch_redirect_response=False, + r, "/login?next={}".format(self.url), fetch_redirect_response=False ) # Also with POST. r = self.client.post(self.url) self.assertRedirects( - r, '/login?next={}'.format(self.url), - fetch_redirect_response=False, + r, "/login?next={}".format(self.url), fetch_redirect_response=False ) def _set_signed_cookie(self, client, key, value): @@ -216,10 +201,9 @@ class GenericLoginViewTests(TestCase): try: cookie = client.cookies[key] # It also can be emptied. - self.assertEqual(cookie.value, '') - self.assertEqual( - cookie['expires'], 'Thu, 01-Jan-1970 00:00:00 GMT') - self.assertEqual(cookie['max-age'], 0) + self.assertEqual(cookie.value, "") + self.assertEqual(cookie["expires"], "Thu, 01-Jan-1970 00:00:00 GMT") + self.assertEqual(cookie["max-age"], 0) except AssertionError: raise AssertionError("The cookie '%s' still exists." % key) @@ -227,16 +211,16 @@ class GenericLoginViewTests(TestCase): """ The kfet generic user is logged in. """ - token = GenericTeamToken.objects.create(token='valid') + token = GenericTeamToken.objects.create(token="valid") self._set_signed_cookie( - self.client, GenericLoginView.TOKEN_COOKIE_NAME, 'valid') + self.client, GenericLoginView.TOKEN_COOKIE_NAME, "valid" + ) r = self.client.get(self.url) - self.assertRedirects(r, reverse('kfet.kpsul')) + self.assertRedirects(r, reverse("kfet.kpsul")) self.assertEqual(r.wsgi_request.user, self.generic_user) - self._is_cookie_deleted( - self.client, GenericLoginView.TOKEN_COOKIE_NAME) + self._is_cookie_deleted(self.client, GenericLoginView.TOKEN_COOKIE_NAME) with self.assertRaises(GenericTeamToken.DoesNotExist): token.refresh_from_db() @@ -245,27 +229,26 @@ class GenericLoginViewTests(TestCase): If token is invalid, delete it and try again. """ self._set_signed_cookie( - self.client, GenericLoginView.TOKEN_COOKIE_NAME, 'invalid') + self.client, GenericLoginView.TOKEN_COOKIE_NAME, "invalid" + ) r = self.client.get(self.url) self.assertRedirects(r, self.url, fetch_redirect_response=False) self.assertEqual(r.wsgi_request.user, AnonymousUser()) - self._is_cookie_deleted( - self.client, GenericLoginView.TOKEN_COOKIE_NAME) + self._is_cookie_deleted(self.client, GenericLoginView.TOKEN_COOKIE_NAME) def test_flow_ok(self): """ A team user is logged in as the kfet generic user. """ - self.client.login(username='team', password='team') - next_url = '/k-fet/' + self.client.login(username="team", password="team") + next_url = "/k-fet/" - r = self.client.post( - '{}?next={}'.format(self.url, next_url), follow=True) + r = self.client.post("{}?next={}".format(self.url, next_url), follow=True) self.assertEqual(r.wsgi_request.user, self.generic_user) - self.assertEqual(r.wsgi_request.path, '/k-fet/') + self.assertEqual(r.wsgi_request.path, "/k-fet/") ## @@ -276,10 +259,10 @@ class GenericLoginViewTests(TestCase): # - temporary_auth context processor ## -class TemporaryAuthTests(TestCase): +class TemporaryAuthTests(TestCase): def setUp(self): - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) @@ -287,22 +270,23 @@ class TemporaryAuthTests(TestCase): self.middleware = TemporaryAuthMiddleware(mock.Mock()) - user1_acc = Account(trigramme='000') - user1_acc.change_pwd('kfet_user1') - user1_acc.save({'username': 'user1'}) + user1_acc = Account(trigramme="000") + user1_acc.change_pwd("kfet_user1") + user1_acc.save({"username": "user1"}) self.user1 = user1_acc.user - self.user1.set_password('user1') + self.user1.set_password("user1") self.user1.save() - user2_acc = Account(trigramme='100') - user2_acc.change_pwd('kfet_user2') - user2_acc.save({'username': 'user2'}) + user2_acc = Account(trigramme="100") + user2_acc.change_pwd("kfet_user2") + user2_acc.save({"username": "user2"}) self.user2 = user2_acc.user - self.user2.set_password('user2') + self.user2.set_password("user2") self.user2.save() self.perm = Permission.objects.get( - content_type__app_label='kfet', codename='is_team') + content_type__app_label="kfet", codename="is_team" + ) self.user2.user_permissions.add(self.perm) def test_middleware_header(self): @@ -310,7 +294,7 @@ class TemporaryAuthTests(TestCase): A user can be authenticated if ``HTTP_KFETPASSWORD`` header of a request contains a valid kfet password. """ - request = self.factory.get('/', HTTP_KFETPASSWORD='kfet_user2') + request = self.factory.get("/", HTTP_KFETPASSWORD="kfet_user2") request.user = self.user1 self.middleware(request) @@ -323,7 +307,7 @@ class TemporaryAuthTests(TestCase): A user can be authenticated if ``KFETPASSWORD`` of POST data contains a valid kfet password. """ - request = self.factory.post('/', {'KFETPASSWORD': 'kfet_user2'}) + request = self.factory.post("/", {"KFETPASSWORD": "kfet_user2"}) request.user = self.user1 self.middleware(request) @@ -335,34 +319,33 @@ class TemporaryAuthTests(TestCase): """ The given password must be a password of an Account. """ - request = self.factory.post('/', {'KFETPASSWORD': 'invalid'}) + request = self.factory.post("/", {"KFETPASSWORD": "invalid"}) request.user = self.user1 self.middleware(request) self.assertEqual(request.user, self.user1) - self.assertFalse(hasattr(request, 'real_user')) + self.assertFalse(hasattr(request, "real_user")) def test_context_processor(self): """ Context variables give the real authenticated user and his permissions. """ - self.client.login(username='user1', password='user1') + self.client.login(username="user1", password="user1") - r = self.client.get('/k-fet/accounts/', HTTP_KFETPASSWORD='kfet_user2') + r = self.client.get("/k-fet/accounts/", HTTP_KFETPASSWORD="kfet_user2") - self.assertEqual(r.context['user'], self.user1) - self.assertNotIn('kfet.is_team', r.context['perms']) + self.assertEqual(r.context["user"], self.user1) + self.assertNotIn("kfet.is_team", r.context["perms"]) def test_auth_not_persistent(self): """ The authentication is temporary, i.e. for one request. """ - self.client.login(username='user1', password='user1') + self.client.login(username="user1", password="user1") - r1 = self.client.get( - '/k-fet/accounts/', HTTP_KFETPASSWORD='kfet_user2') + r1 = self.client.get("/k-fet/accounts/", HTTP_KFETPASSWORD="kfet_user2") self.assertEqual(r1.wsgi_request.user, self.user2) - r2 = self.client.get('/k-fet/accounts/') + r2 = self.client.get("/k-fet/accounts/") self.assertEqual(r2.wsgi_request.user, self.user1) diff --git a/kfet/auth/utils.py b/kfet/auth/utils.py index 0edc555d..ed63bb67 100644 --- a/kfet/auth/utils.py +++ b/kfet/auth/utils.py @@ -23,12 +23,9 @@ def setup_kfet_generic_user(**kwargs): """ generic = get_kfet_generic_user() generic.user_permissions.add( - Permission.objects.get( - content_type__app_label='kfet', - codename='is_team', - ) + Permission.objects.get(content_type__app_label="kfet", codename="is_team") ) def hash_password(password): - return hashlib.sha256(password.encode('utf-8')).hexdigest() + return hashlib.sha256(password.encode("utf-8")).hexdigest() diff --git a/kfet/auth/views.py b/kfet/auth/views.py index 7b9f4099..35a6eedf 100644 --- a/kfet/auth/views.py +++ b/kfet/auth/views.py @@ -1,17 +1,17 @@ from django.contrib import messages -from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import permission_required from django.contrib.auth.models import Group, User from django.contrib.auth.views import redirect_to_login +from django.contrib.messages.views import SuccessMessageMixin from django.core.urlresolvers import reverse, reverse_lazy from django.db.models import Prefetch from django.http import QueryDict from django.shortcuts import redirect, render from django.utils.decorators import method_decorator from django.utils.translation import ugettext_lazy as _ -from django.views.generic import View from django.views.decorators.http import require_http_methods +from django.views.generic import View from django.views.generic.edit import CreateView, UpdateView from .forms import GroupForm @@ -30,28 +30,33 @@ class GenericLoginView(View): provider, which can be external. Session is unusable as it will be cleared on logout. """ - TOKEN_COOKIE_NAME = 'kfettoken' - @method_decorator(require_http_methods(['GET', 'POST'])) + TOKEN_COOKIE_NAME = "kfettoken" + + @method_decorator(require_http_methods(["GET", "POST"])) def dispatch(self, request, *args, **kwargs): token = request.get_signed_cookie(self.TOKEN_COOKIE_NAME, None) if not token: - if not request.user.has_perm('kfet.is_team'): + if not request.user.has_perm("kfet.is_team"): return redirect_to_login(request.get_full_path()) - if request.method == 'POST': + if request.method == "POST": # Step 1: set token and logout user. return self.prepare_auth() else: # GET request should not change server/client states. Send a # confirmation template to emit a POST request. - return render(request, 'kfet/confirm_form.html', { - 'title': _("Ouvrir une session partagée"), - 'text': _( - "Êtes-vous sûr·e de vouloir ouvrir une session " - "partagée ?" - ), - }) + return render( + request, + "kfet/confirm_form.html", + { + "title": _("Ouvrir une session partagée"), + "text": _( + "Êtes-vous sûr·e de vouloir ouvrir une session " + "partagée ?" + ), + }, + ) else: # Step 2: validate token. return self.validate_auth(token) @@ -62,20 +67,19 @@ class GenericLoginView(View): # Prepare callback of logout. here_url = reverse(login_generic) - if 'next' in self.request.GET: + if "next" in self.request.GET: # Keep given next page. here_qd = QueryDict(mutable=True) - here_qd['next'] = self.request.GET['next'] - here_url += '?{}'.format(here_qd.urlencode()) + here_qd["next"] = self.request.GET["next"] + here_url += "?{}".format(here_qd.urlencode()) - logout_url = reverse('cof-logout') + logout_url = reverse("cof-logout") logout_qd = QueryDict(mutable=True) - logout_qd['next'] = here_url - logout_url += '?{}'.format(logout_qd.urlencode(safe='/')) + logout_qd["next"] = here_url + logout_url += "?{}".format(logout_qd.urlencode(safe="/")) resp = redirect(logout_url) - resp.set_signed_cookie( - self.TOKEN_COOKIE_NAME, token.token, httponly=True) + resp.set_signed_cookie(self.TOKEN_COOKIE_NAME, token.token, httponly=True) return resp def validate_auth(self, token): @@ -85,9 +89,9 @@ class GenericLoginView(View): if user: # Log in generic user. login(self.request, user) - messages.success(self.request, _( - "K-Fêt — Ouverture d'une session partagée." - )) + messages.success( + self.request, _("K-Fêt — Ouverture d'une session partagée.") + ) resp = redirect(self.get_next_url()) else: # Try again. @@ -98,39 +102,34 @@ class GenericLoginView(View): return resp def get_next_url(self): - return self.request.GET.get('next', reverse('kfet.kpsul')) + return self.request.GET.get("next", reverse("kfet.kpsul")) login_generic = GenericLoginView.as_view() -@permission_required('kfet.manage_perms') +@permission_required("kfet.manage_perms") def account_group(request): user_pre = Prefetch( - 'user_set', - queryset=User.objects.select_related('profile__account_kfet'), + "user_set", queryset=User.objects.select_related("profile__account_kfet") ) - groups = ( - Group.objects - .filter(name__icontains='K-Fêt') - .prefetch_related('permissions', user_pre) + groups = Group.objects.filter(name__icontains="K-Fêt").prefetch_related( + "permissions", user_pre ) - return render(request, 'kfet/account_group.html', { - 'groups': groups, - }) + return render(request, "kfet/account_group.html", {"groups": groups}) class AccountGroupCreate(SuccessMessageMixin, CreateView): model = Group - template_name = 'kfet/account_group_form.html' + template_name = "kfet/account_group_form.html" form_class = GroupForm - success_message = 'Nouveau groupe : %(name)s' - success_url = reverse_lazy('kfet.account.group') + success_message = "Nouveau groupe : %(name)s" + success_url = reverse_lazy("kfet.account.group") class AccountGroupUpdate(SuccessMessageMixin, UpdateView): - queryset = Group.objects.filter(name__icontains='K-Fêt') - template_name = 'kfet/account_group_form.html' + queryset = Group.objects.filter(name__icontains="K-Fêt") + template_name = "kfet/account_group_form.html" form_class = GroupForm - success_message = 'Groupe modifié : %(name)s' - success_url = reverse_lazy('kfet.account.group') + success_message = "Groupe modifié : %(name)s" + success_url = reverse_lazy("kfet.account.group") diff --git a/kfet/autocomplete.py b/kfet/autocomplete.py index 0d1904d6..d7448194 100644 --- a/kfet/autocomplete.py +++ b/kfet/autocomplete.py @@ -1,8 +1,8 @@ -from ldap3 import Connection -from django.shortcuts import render -from django.http import Http404 -from django.db.models import Q from django.conf import settings +from django.db.models import Q +from django.http import Http404 +from django.shortcuts import render +from ldap3 import Connection from gestioncof.models import User from kfet.decorators import teamkfet_required @@ -25,81 +25,80 @@ def account_create(request): raise Http404 q = request.GET.get("q") - if (len(q) == 0): + if len(q) == 0: return render(request, "kfet/account_create_autocomplete.html") - data = {'q': q} + data = {"q": q} queries = {} search_words = q.split() # Fetching data from User, CofProfile and Account tables - queries['kfet'] = Account.objects - queries['users_cof'] = User.objects.filter(profile__is_cof=True) - queries['users_notcof'] = User.objects.filter(profile__is_cof=False) + queries["kfet"] = Account.objects + queries["users_cof"] = User.objects.filter(profile__is_cof=True) + queries["users_notcof"] = User.objects.filter(profile__is_cof=False) for word in search_words: - queries['kfet'] = queries['kfet'].filter( + queries["kfet"] = queries["kfet"].filter( Q(cofprofile__user__username__icontains=word) | Q(cofprofile__user__first_name__icontains=word) | Q(cofprofile__user__last_name__icontains=word) ) - queries['users_cof'] = queries['users_cof'].filter( + queries["users_cof"] = queries["users_cof"].filter( Q(username__icontains=word) | Q(first_name__icontains=word) | Q(last_name__icontains=word) ) - queries['users_notcof'] = queries['users_notcof'].filter( + queries["users_notcof"] = queries["users_notcof"].filter( Q(username__icontains=word) | Q(first_name__icontains=word) | Q(last_name__icontains=word) ) # Clearing redundancies - queries['kfet'] = queries['kfet'].distinct() + queries["kfet"] = queries["kfet"].distinct() usernames = set( - queries['kfet'].values_list('cofprofile__user__username', flat=True)) - queries['kfet'] = [ - (account, account.cofprofile.user) - for account in queries['kfet'] + queries["kfet"].values_list("cofprofile__user__username", flat=True) + ) + queries["kfet"] = [ + (account, account.cofprofile.user) for account in queries["kfet"] ] - queries['users_cof'] = \ - queries['users_cof'].exclude(username__in=usernames).distinct() - queries['users_notcof'] = \ - queries['users_notcof'].exclude(username__in=usernames).distinct() - usernames |= set( - queries['users_cof'].values_list('username', flat=True)) - usernames |= set( - queries['users_notcof'].values_list('username', flat=True)) + queries["users_cof"] = ( + queries["users_cof"].exclude(username__in=usernames).distinct() + ) + queries["users_notcof"] = ( + queries["users_notcof"].exclude(username__in=usernames).distinct() + ) + usernames |= set(queries["users_cof"].values_list("username", flat=True)) + usernames |= set(queries["users_notcof"].values_list("username", flat=True)) # Fetching data from the SPI - if getattr(settings, 'LDAP_SERVER_URL', None): + if getattr(settings, "LDAP_SERVER_URL", None): # Fetching - ldap_query = '(&{:s})'.format(''.join( - '(|(cn=*{bit:s}*)(uid=*{bit:s}*))'.format(bit=word) - for word in search_words if word.isalnum() - )) + ldap_query = "(&{:s})".format( + "".join( + "(|(cn=*{bit:s}*)(uid=*{bit:s}*))".format(bit=word) + for word in search_words + if word.isalnum() + ) + ) if ldap_query != "(&)": # If none of the bits were legal, we do not perform the query entries = None with Connection(settings.LDAP_SERVER_URL) as conn: - conn.search( - 'dc=spi,dc=ens,dc=fr', ldap_query, - attributes=['uid', 'cn'] - ) + conn.search("dc=spi,dc=ens,dc=fr", ldap_query, attributes=["uid", "cn"]) entries = conn.entries # Clearing redundancies - queries['clippers'] = [ + queries["clippers"] = [ Clipper(entry.uid.value, entry.cn.value) for entry in entries - if entry.uid.value - and entry.uid.value not in usernames + if entry.uid.value and entry.uid.value not in usernames ] # Resulting data data.update(queries) - data['options'] = sum([len(query) for query in queries]) + data["options"] = sum([len(query) for query in queries]) return render(request, "kfet/account_create_autocomplete.html", data) @@ -111,17 +110,19 @@ def account_search(request): q = request.GET.get("q") words = q.split() - data = {'q': q} + data = {"q": q} for word in words: query = Account.objects.filter( - Q(cofprofile__user__username__icontains=word) | - Q(cofprofile__user__first_name__icontains=word) | - Q(cofprofile__user__last_name__icontains=word) - ).distinct() + Q(cofprofile__user__username__icontains=word) + | Q(cofprofile__user__first_name__icontains=word) + | Q(cofprofile__user__last_name__icontains=word) + ).distinct() - query = [(account.trigramme, account.cofprofile.user.get_full_name()) - for account in query] + query = [ + (account.trigramme, account.cofprofile.user.get_full_name()) + for account in query + ] - data['accounts'] = query - return render(request, 'kfet/account_search_autocomplete.html', data) + data["accounts"] = query + return render(request, "kfet/account_search_autocomplete.html", data) diff --git a/kfet/cms/__init__.py b/kfet/cms/__init__.py index 0f6cab45..f6aabddc 100644 --- a/kfet/cms/__init__.py +++ b/kfet/cms/__init__.py @@ -1 +1 @@ -default_app_config = 'kfet.cms.apps.KFetCMSAppConfig' +default_app_config = "kfet.cms.apps.KFetCMSAppConfig" diff --git a/kfet/cms/apps.py b/kfet/cms/apps.py index 1db0e043..d4928ac6 100644 --- a/kfet/cms/apps.py +++ b/kfet/cms/apps.py @@ -2,9 +2,9 @@ from django.apps import AppConfig class KFetCMSAppConfig(AppConfig): - name = 'kfet.cms' - label = 'kfetcms' - verbose_name = 'CMS K-Fêt' + name = "kfet.cms" + label = "kfetcms" + verbose_name = "CMS K-Fêt" def ready(self): from . import hooks diff --git a/kfet/cms/context_processors.py b/kfet/cms/context_processors.py index 34f175d1..700170ee 100644 --- a/kfet/cms/context_processors.py +++ b/kfet/cms/context_processors.py @@ -3,18 +3,14 @@ from kfet.models import Article def get_articles(request=None): articles = ( - Article.objects - .filter(is_sold=True, hidden=False) - .select_related('category') - .order_by('category__name', 'name') + Article.objects.filter(is_sold=True, hidden=False) + .select_related("category") + .order_by("category__name", "name") ) pressions, others = [], [] for article in articles: - if article.category.name == 'Pression': + if article.category.name == "Pression": pressions.append(article) else: others.append(article) - return { - 'pressions': pressions, - 'articles': others, - } + return {"pressions": pressions, "articles": others} diff --git a/kfet/cms/hooks.py b/kfet/cms/hooks.py index e58aeef5..a3070cfb 100644 --- a/kfet/cms/hooks.py +++ b/kfet/cms/hooks.py @@ -1,12 +1,10 @@ from django.contrib.staticfiles.templatetags.staticfiles import static from django.utils.html import format_html - from wagtail.wagtailcore import hooks -@hooks.register('insert_editor_css') +@hooks.register("insert_editor_css") def editor_css(): return format_html( - '', - static('kfetcms/css/editor.css'), + '', static("kfetcms/css/editor.css") ) diff --git a/kfet/cms/management/commands/kfet_loadwagtail.py b/kfet/cms/management/commands/kfet_loadwagtail.py index 86b94d3e..566cca43 100644 --- a/kfet/cms/management/commands/kfet_loadwagtail.py +++ b/kfet/cms/management/commands/kfet_loadwagtail.py @@ -1,7 +1,6 @@ from django.contrib.auth.models import Group from django.core.management import call_command from django.core.management.base import BaseCommand - from wagtail.wagtailcore.models import Page, Site @@ -9,7 +8,7 @@ class Command(BaseCommand): help = "Importe des données pour Wagtail" def add_arguments(self, parser): - parser.add_argument('--file', default='kfet_wagtail_17_05') + parser.add_argument("--file", default="kfet_wagtail_17_05") def handle(self, *args, **options): @@ -18,12 +17,10 @@ class Command(BaseCommand): # Nettoyage des données initiales posées par Wagtail dans la migration # wagtailcore/0002 - Group.objects.filter(name__in=('Moderators', 'Editors')).delete() + Group.objects.filter(name__in=("Moderators", "Editors")).delete() try: - homepage = Page.objects.get( - title="Welcome to your new Wagtail site!" - ) + homepage = Page.objects.get(title="Welcome to your new Wagtail site!") homepage.delete() Site.objects.filter(root_page=homepage).delete() except Page.DoesNotExist: @@ -32,4 +29,4 @@ class Command(BaseCommand): # Import des données # Par défaut, il s'agit d'une copie du site K-Fêt (17-05) - call_command('loaddata', options['file']) + call_command("loaddata", options["file"]) diff --git a/kfet/cms/migrations/0001_initial.py b/kfet/cms/migrations/0001_initial.py index ed0b0948..8c075903 100644 --- a/kfet/cms/migrations/0001_initial.py +++ b/kfet/cms/migrations/0001_initial.py @@ -1,49 +1,214 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models -import wagtail.wagtailsnippets.blocks +import django.db.models.deletion import wagtail.wagtailcore.blocks import wagtail.wagtailcore.fields -import django.db.models.deletion +import wagtail.wagtailsnippets.blocks +from django.db import migrations, models + import kfet.cms.models class Migration(migrations.Migration): dependencies = [ - ('wagtailcore', '0033_remove_golive_expiry_help_text'), - ('wagtailimages', '0019_delete_filter'), + ("wagtailcore", "0033_remove_golive_expiry_help_text"), + ("wagtailimages", "0019_delete_filter"), ] operations = [ migrations.CreateModel( - name='KFetPage', + name="KFetPage", fields=[ - ('page_ptr', models.OneToOneField(serialize=False, primary_key=True, parent_link=True, auto_created=True, to='wagtailcore.Page', on_delete=models.CASCADE)), - ('no_header', models.BooleanField(verbose_name='Sans en-tête', help_text="Coché, l'en-tête (avec le titre) de la page n'est pas affiché.", default=False)), - ('content', wagtail.wagtailcore.fields.StreamField((('rich', wagtail.wagtailcore.blocks.RichTextBlock(label='Éditeur')), ('carte', kfet.cms.models.MenuBlock()), ('group_team', wagtail.wagtailcore.blocks.StructBlock((('show_only', wagtail.wagtailcore.blocks.IntegerBlock(help_text='Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.', required=False, label='Montrer seulement')), ('members', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailsnippets.blocks.SnippetChooserBlock(kfet.cms.models.MemberTeam), classname='team-group', label='K-Fêt-eux-ses'))))), ('group', wagtail.wagtailcore.blocks.StreamBlock((('rich', wagtail.wagtailcore.blocks.RichTextBlock(label='Éditeur')), ('carte', kfet.cms.models.MenuBlock()), ('group_team', wagtail.wagtailcore.blocks.StructBlock((('show_only', wagtail.wagtailcore.blocks.IntegerBlock(help_text='Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.', required=False, label='Montrer seulement')), ('members', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailsnippets.blocks.SnippetChooserBlock(kfet.cms.models.MemberTeam), classname='team-group', label='K-Fêt-eux-ses')))))), label='Contenu groupé'))), verbose_name='Contenu')), - ('layout', models.CharField(max_length=255, choices=[('kfet/base_col_1.html', 'Une colonne : centrée sur la page'), ('kfet/base_col_2.html', 'Deux colonnes : fixe à gauche, contenu à droite'), ('kfet/base_col_mult.html', 'Contenu scindé sur plusieurs colonnes')], help_text='Comment cette page devrait être affichée ?', verbose_name='Template', default='kfet/base_col_mult.html')), - ('main_size', models.CharField(max_length=255, blank=True, verbose_name='Taille de la colonne de contenu')), - ('col_count', models.CharField(max_length=255, blank=True, verbose_name='Nombre de colonnes', help_text="S'applique au page dont le contenu est scindé sur plusieurs colonnes")), + ( + "page_ptr", + models.OneToOneField( + serialize=False, + primary_key=True, + parent_link=True, + auto_created=True, + to="wagtailcore.Page", + on_delete=models.CASCADE, + ), + ), + ( + "no_header", + models.BooleanField( + verbose_name="Sans en-tête", + help_text="Coché, l'en-tête (avec le titre) de la page n'est pas affiché.", + default=False, + ), + ), + ( + "content", + wagtail.wagtailcore.fields.StreamField( + ( + ( + "rich", + wagtail.wagtailcore.blocks.RichTextBlock( + label="Éditeur" + ), + ), + ("carte", kfet.cms.models.MenuBlock()), + ( + "group_team", + wagtail.wagtailcore.blocks.StructBlock( + ( + ( + "show_only", + wagtail.wagtailcore.blocks.IntegerBlock( + help_text="Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.", + required=False, + label="Montrer seulement", + ), + ), + ( + "members", + wagtail.wagtailcore.blocks.ListBlock( + wagtail.wagtailsnippets.blocks.SnippetChooserBlock( + kfet.cms.models.MemberTeam + ), + classname="team-group", + label="K-Fêt-eux-ses", + ), + ), + ) + ), + ), + ( + "group", + wagtail.wagtailcore.blocks.StreamBlock( + ( + ( + "rich", + wagtail.wagtailcore.blocks.RichTextBlock( + label="Éditeur" + ), + ), + ("carte", kfet.cms.models.MenuBlock()), + ( + "group_team", + wagtail.wagtailcore.blocks.StructBlock( + ( + ( + "show_only", + wagtail.wagtailcore.blocks.IntegerBlock( + help_text="Nombre initial de membres affichés. Laisser vide pour tou-te-s les afficher.", + required=False, + label="Montrer seulement", + ), + ), + ( + "members", + wagtail.wagtailcore.blocks.ListBlock( + wagtail.wagtailsnippets.blocks.SnippetChooserBlock( + kfet.cms.models.MemberTeam + ), + classname="team-group", + label="K-Fêt-eux-ses", + ), + ), + ) + ), + ), + ), + label="Contenu groupé", + ), + ), + ), + verbose_name="Contenu", + ), + ), + ( + "layout", + models.CharField( + max_length=255, + choices=[ + ( + "kfet/base_col_1.html", + "Une colonne : centrée sur la page", + ), + ( + "kfet/base_col_2.html", + "Deux colonnes : fixe à gauche, contenu à droite", + ), + ( + "kfet/base_col_mult.html", + "Contenu scindé sur plusieurs colonnes", + ), + ], + help_text="Comment cette page devrait être affichée ?", + verbose_name="Template", + default="kfet/base_col_mult.html", + ), + ), + ( + "main_size", + models.CharField( + max_length=255, + blank=True, + verbose_name="Taille de la colonne de contenu", + ), + ), + ( + "col_count", + models.CharField( + max_length=255, + blank=True, + verbose_name="Nombre de colonnes", + help_text="S'applique au page dont le contenu est scindé sur plusieurs colonnes", + ), + ), ], options={ - 'verbose_name': 'page K-Fêt', - 'verbose_name_plural': 'pages K-Fêt', + "verbose_name": "page K-Fêt", + "verbose_name_plural": "pages K-Fêt", }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='MemberTeam', + name="MemberTeam", fields=[ - ('id', models.AutoField(verbose_name='ID', auto_created=True, serialize=False, primary_key=True)), - ('first_name', models.CharField(blank=True, max_length=255, verbose_name='Prénom', default='')), - ('last_name', models.CharField(blank=True, max_length=255, verbose_name='Nom', default='')), - ('nick_name', models.CharField(verbose_name='Alias', blank=True, default='', max_length=255)), - ('photo', models.ForeignKey(null=True, related_name='+', on_delete=django.db.models.deletion.SET_NULL, verbose_name='Photo', blank=True, to='wagtailimages.Image')), + ( + "id", + models.AutoField( + verbose_name="ID", + auto_created=True, + serialize=False, + primary_key=True, + ), + ), + ( + "first_name", + models.CharField( + blank=True, max_length=255, verbose_name="Prénom", default="" + ), + ), + ( + "last_name", + models.CharField( + blank=True, max_length=255, verbose_name="Nom", default="" + ), + ), + ( + "nick_name", + models.CharField( + verbose_name="Alias", blank=True, default="", max_length=255 + ), + ), + ( + "photo", + models.ForeignKey( + null=True, + related_name="+", + on_delete=django.db.models.deletion.SET_NULL, + verbose_name="Photo", + blank=True, + to="wagtailimages.Image", + ), + ), ], - options={ - 'verbose_name': 'K-Fêt-eux-se', - }, + options={"verbose_name": "K-Fêt-eux-se"}, ), ] diff --git a/kfet/cms/migrations/0002_alter_kfetpage_colcount.py b/kfet/cms/migrations/0002_alter_kfetpage_colcount.py index fe91d3e6..4eace262 100644 --- a/kfet/cms/migrations/0002_alter_kfetpage_colcount.py +++ b/kfet/cms/migrations/0002_alter_kfetpage_colcount.py @@ -6,14 +6,17 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfetcms', '0001_initial'), - ] + dependencies = [("kfetcms", "0001_initial")] operations = [ migrations.AlterField( - model_name='kfetpage', - name='col_count', - field=models.CharField(blank=True, max_length=255, verbose_name='Nombre de colonnes', help_text="S'applique au page dont le contenu est scindé sur plusieurs colonnes."), - ), + model_name="kfetpage", + name="col_count", + field=models.CharField( + blank=True, + max_length=255, + verbose_name="Nombre de colonnes", + help_text="S'applique au page dont le contenu est scindé sur plusieurs colonnes.", + ), + ) ] diff --git a/kfet/cms/models.py b/kfet/cms/models.py index 0dff183f..6d108f31 100644 --- a/kfet/cms/models.py +++ b/kfet/cms/models.py @@ -1,8 +1,10 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ - from wagtail.wagtailadmin.edit_handlers import ( - FieldPanel, FieldRowPanel, MultiFieldPanel, StreamFieldPanel + FieldPanel, + FieldRowPanel, + MultiFieldPanel, + StreamFieldPanel, ) from wagtail.wagtailcore import blocks from wagtail.wagtailcore.fields import StreamField @@ -17,47 +19,45 @@ from kfet.cms.context_processors import get_articles @register_snippet class MemberTeam(models.Model): first_name = models.CharField( - verbose_name=_('Prénom'), - blank=True, default='', max_length=255, + verbose_name=_("Prénom"), blank=True, default="", max_length=255 ) last_name = models.CharField( - verbose_name=_('Nom'), - blank=True, default='', max_length=255, + verbose_name=_("Nom"), blank=True, default="", max_length=255 ) nick_name = models.CharField( - verbose_name=_('Alias'), - blank=True, default='', max_length=255, + verbose_name=_("Alias"), blank=True, default="", max_length=255 ) photo = models.ForeignKey( - 'wagtailimages.Image', - verbose_name=_('Photo'), + "wagtailimages.Image", + verbose_name=_("Photo"), on_delete=models.SET_NULL, - null=True, blank=True, - related_name='+', + null=True, + blank=True, + related_name="+", ) class Meta: - verbose_name = _('K-Fêt-eux-se') + verbose_name = _("K-Fêt-eux-se") panels = [ - FieldPanel('first_name'), - FieldPanel('last_name'), - FieldPanel('nick_name'), - ImageChooserPanel('photo'), + FieldPanel("first_name"), + FieldPanel("last_name"), + FieldPanel("nick_name"), + ImageChooserPanel("photo"), ] def __str__(self): return self.get_full_name() def get_full_name(self): - return '{} {}'.format(self.first_name, self.last_name).strip() + return "{} {}".format(self.first_name, self.last_name).strip() class MenuBlock(blocks.StaticBlock): class Meta: - icon = 'list-ul' - label = _('Carte') - template = 'kfetcms/block_menu.html' + icon = "list-ul" + label = _("Carte") + template = "kfetcms/block_menu.html" def get_context(self, *args, **kwargs): context = super().get_context(*args, **kwargs) @@ -67,71 +67,68 @@ class MenuBlock(blocks.StaticBlock): class GroupTeamBlock(blocks.StructBlock): show_only = blocks.IntegerBlock( - label=_('Montrer seulement'), + label=_("Montrer seulement"), required=False, help_text=_( - 'Nombre initial de membres affichés. Laisser vide pour tou-te-s ' - 'les afficher.' + "Nombre initial de membres affichés. Laisser vide pour tou-te-s " + "les afficher." ), ) members = blocks.ListBlock( SnippetChooserBlock(MemberTeam), - label=_('K-Fêt-eux-ses'), - classname='team-group', + label=_("K-Fêt-eux-ses"), + classname="team-group", ) class Meta: - icon = 'group' - label = _('Groupe de K-Fêt-eux-ses') - template = 'kfetcms/block_teamgroup.html' + icon = "group" + label = _("Groupe de K-Fêt-eux-ses") + template = "kfetcms/block_teamgroup.html" class ChoicesStreamBlock(blocks.StreamBlock): - rich = blocks.RichTextBlock(label=_('Éditeur')) + rich = blocks.RichTextBlock(label=_("Éditeur")) carte = MenuBlock() group_team = GroupTeamBlock() class KFetStreamBlock(ChoicesStreamBlock): - group = ChoicesStreamBlock(label=_('Contenu groupé')) + group = ChoicesStreamBlock(label=_("Contenu groupé")) class KFetPage(Page): - content = StreamField(KFetStreamBlock, verbose_name=_('Contenu')) + content = StreamField(KFetStreamBlock, verbose_name=_("Contenu")) # Layout fields - TEMPLATE_COL_1 = 'kfet/base_col_1.html' - TEMPLATE_COL_2 = 'kfet/base_col_2.html' - TEMPLATE_COL_MULT = 'kfet/base_col_mult.html' + TEMPLATE_COL_1 = "kfet/base_col_1.html" + TEMPLATE_COL_2 = "kfet/base_col_2.html" + TEMPLATE_COL_MULT = "kfet/base_col_mult.html" no_header = models.BooleanField( - verbose_name=_('Sans en-tête'), + verbose_name=_("Sans en-tête"), default=False, - help_text=_( - "Coché, l'en-tête (avec le titre) de la page n'est pas affiché." - ), + help_text=_("Coché, l'en-tête (avec le titre) de la page n'est pas affiché."), ) layout = models.CharField( - verbose_name=_('Template'), + verbose_name=_("Template"), choices=[ - (TEMPLATE_COL_1, _('Une colonne : centrée sur la page')), - (TEMPLATE_COL_2, _('Deux colonnes : fixe à gauche, contenu à droite')), - (TEMPLATE_COL_MULT, _('Contenu scindé sur plusieurs colonnes')), + (TEMPLATE_COL_1, _("Une colonne : centrée sur la page")), + (TEMPLATE_COL_2, _("Deux colonnes : fixe à gauche, contenu à droite")), + (TEMPLATE_COL_MULT, _("Contenu scindé sur plusieurs colonnes")), ], - default=TEMPLATE_COL_MULT, max_length=255, - help_text=_( - "Comment cette page devrait être affichée ?" - ), + default=TEMPLATE_COL_MULT, + max_length=255, + help_text=_("Comment cette page devrait être affichée ?"), ) main_size = models.CharField( - verbose_name=_('Taille de la colonne de contenu'), - blank=True, max_length=255, + verbose_name=_("Taille de la colonne de contenu"), blank=True, max_length=255 ) col_count = models.CharField( - verbose_name=_('Nombre de colonnes'), - blank=True, max_length=255, + verbose_name=_("Nombre de colonnes"), + blank=True, + max_length=255, help_text=_( "S'applique au page dont le contenu est scindé sur plusieurs colonnes." ), @@ -139,34 +136,29 @@ class KFetPage(Page): # Panels - content_panels = Page.content_panels + [ - StreamFieldPanel('content'), - ] + content_panels = Page.content_panels + [StreamFieldPanel("content")] layout_panel = [ - FieldPanel('no_header'), - FieldPanel('layout'), - FieldRowPanel([ - FieldPanel('main_size'), - FieldPanel('col_count'), - ]), + FieldPanel("no_header"), + FieldPanel("layout"), + FieldRowPanel([FieldPanel("main_size"), FieldPanel("col_count")]), ] settings_panels = [ - MultiFieldPanel(layout_panel, _('Affichage')) + MultiFieldPanel(layout_panel, _("Affichage")) ] + Page.settings_panels # Base template template = "kfetcms/base.html" class Meta: - verbose_name = _('page K-Fêt') - verbose_name_plural = _('pages K-Fêt') + verbose_name = _("page K-Fêt") + verbose_name_plural = _("pages K-Fêt") def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) - page = context['page'] + page = context["page"] if not page.seo_title: page.seo_title = page.title diff --git a/kfet/config.py b/kfet/config.py index 452c2a09..a8f7f0eb 100644 --- a/kfet/config.py +++ b/kfet/config.py @@ -1,8 +1,7 @@ +import djconfig from django.core.exceptions import ValidationError from django.db import models -import djconfig - class KFetConfig(object): """kfet app configuration. @@ -10,7 +9,8 @@ class KFetConfig(object): Enhance isolation with backend used to store config. """ - prefix = 'kfet_' + + prefix = "kfet_" def __init__(self): # Set this to False again to reload the config, e.g for testing @@ -28,11 +28,11 @@ class KFetConfig(object): def __getattr__(self, key): self._check_init() - if key == 'subvention_cof': + if key == "subvention_cof": # Allows accessing to the reduction as a subvention # Other reason: backward compatibility - reduction_mult = 1 - self.reduction_cof/100 - return (1/reduction_mult - 1) * 100 + reduction_mult = 1 - self.reduction_cof / 100 + return (1 / reduction_mult - 1) * 100 return getattr(djconfig.config, self._get_dj_key(key)) def list(self): @@ -44,12 +44,15 @@ class KFetConfig(object): """ # prevent circular imports from kfet.forms import KFetConfigForm + self._check_init() - return [(field.label, getattr(djconfig.config, name), ) - for name, field in KFetConfigForm.base_fields.items()] + return [ + (field.label, getattr(djconfig.config, name)) + for name, field in KFetConfigForm.base_fields.items() + ] def _get_dj_key(self, key): - return '{}{}'.format(self.prefix, key) + return "{}{}".format(self.prefix, key) def set(self, **kwargs): """Update configuration value(s). @@ -78,8 +81,9 @@ class KFetConfig(object): cfg_form.save() else: raise ValidationError( - 'Invalid values in kfet_config.set: %(fields)s', - params={'fields': list(cfg_form.errors)}) + "Invalid values in kfet_config.set: %(fields)s", + params={"fields": list(cfg_form.errors)}, + ) kfet_config = KFetConfig() diff --git a/kfet/consumers.py b/kfet/consumers.py index a53bbb72..2655c86b 100644 --- a/kfet/consumers.py +++ b/kfet/consumers.py @@ -2,5 +2,5 @@ from .utils import DjangoJsonWebsocketConsumer, PermConsumerMixin class KPsul(PermConsumerMixin, DjangoJsonWebsocketConsumer): - groups = ['kfet.kpsul'] - perms_connect = ['kfet.is_team'] + groups = ["kfet.kpsul"] + perms_connect = ["kfet.is_team"] diff --git a/kfet/context_processors.py b/kfet/context_processors.py index 89678f62..6668eebf 100644 --- a/kfet/context_processors.py +++ b/kfet/context_processors.py @@ -2,4 +2,4 @@ from kfet.config import kfet_config def config(request): - return {'kfet_config': kfet_config} + return {"kfet_config": kfet_config} diff --git a/kfet/decorators.py b/kfet/decorators.py index 66c9d71c..70848820 100644 --- a/kfet/decorators.py +++ b/kfet/decorators.py @@ -2,6 +2,7 @@ from django.contrib.auth.decorators import user_passes_test def kfet_is_team(user): - return user.has_perm('kfet.is_team') + return user.has_perm("kfet.is_team") + teamkfet_required = user_passes_test(kfet_is_team) diff --git a/kfet/forms.py b/kfet/forms.py index 9d0fadd8..b253fd67 100644 --- a/kfet/forms.py +++ b/kfet/forms.py @@ -2,79 +2,87 @@ from datetime import timedelta from decimal import Decimal from django import forms -from django.core.exceptions import ValidationError -from django.core import validators from django.contrib.auth.models import User +from django.core import validators +from django.core.exceptions import ValidationError from django.forms import modelformset_factory from django.utils import timezone - from djconfig.forms import ConfigForm -from kfet.models import ( - Account, Checkout, Article, OperationGroup, Operation, - CheckoutStatement, ArticleCategory, AccountNegative, Transfer, - TransferGroup, Supplier) from gestioncof.models import CofProfile +from kfet.models import ( + Account, + AccountNegative, + Article, + ArticleCategory, + Checkout, + CheckoutStatement, + Operation, + OperationGroup, + Supplier, + Transfer, + TransferGroup, +) from .auth.forms import UserGroupForm # noqa - # ----- # Widgets # ----- + class DateTimeWidget(forms.DateTimeInput): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.attrs['format'] = '%Y-%m-%d %H:%M' + self.attrs["format"] = "%Y-%m-%d %H:%M" class Media: - css = { - 'all': ('kfet/css/bootstrap-datetimepicker.min.css',) - } - js = ('kfet/js/bootstrap-datetimepicker.min.js',) + css = {"all": ("kfet/css/bootstrap-datetimepicker.min.css",)} + js = ("kfet/js/bootstrap-datetimepicker.min.js",) # ----- # Account forms # ----- + class AccountForm(forms.ModelForm): # Surcharge pour passer data à Account.save() - def save(self, data = {}, *args, **kwargs): - obj = super().save(commit = False, *args, **kwargs) - obj.save(data = data) + def save(self, data={}, *args, **kwargs): + obj = super().save(commit=False, *args, **kwargs) + obj.save(data=data) return obj class Meta: - model = Account - fields = ['trigramme', 'promo', 'nickname', 'is_frozen'] - widgets = { - 'trigramme': forms.TextInput(attrs={'autocomplete': 'off'}), - } + model = Account + fields = ["trigramme", "promo", "nickname", "is_frozen"] + widgets = {"trigramme": forms.TextInput(attrs={"autocomplete": "off"})} + class AccountBalanceForm(forms.ModelForm): class Meta: model = Account - fields = ['balance'] + fields = ["balance"] + class AccountTriForm(AccountForm): - def clean_trigramme(self): - trigramme = self.cleaned_data['trigramme'] + trigramme = self.cleaned_data["trigramme"] return trigramme.upper() class Meta(AccountForm.Meta): - fields = ['trigramme'] + fields = ["trigramme"] + class AccountNoTriForm(AccountForm): class Meta(AccountForm.Meta): - exclude = ['trigramme'] + exclude = ["trigramme"] + class AccountRestrictForm(AccountForm): class Meta(AccountForm.Meta): - fields = ['is_frozen'] + fields = ["is_frozen"] class AccountPwdForm(forms.Form): @@ -85,14 +93,12 @@ class AccountPwdForm(forms.Form): widget=forms.PasswordInput, ) pwd2 = forms.CharField( - label="Confirmer le mot de passe", - required=False, - widget=forms.PasswordInput, + label="Confirmer le mot de passe", required=False, widget=forms.PasswordInput ) def clean(self): - pwd1 = self.cleaned_data.get('pwd1', '') - pwd2 = self.cleaned_data.get('pwd2', '') + pwd1 = self.cleaned_data.get("pwd1", "") + pwd2 = self.cleaned_data.get("pwd2", "") if len(pwd1) < 8: raise ValidationError("Mot de passe trop court") if pwd1 != pwd2: @@ -102,63 +108,66 @@ class AccountPwdForm(forms.Form): class CofForm(forms.ModelForm): def clean_is_cof(self): - instance = getattr(self, 'instance', None) + instance = getattr(self, "instance", None) if instance and instance.pk: return instance.is_cof else: return False + class Meta: - model = CofProfile - fields = ['login_clipper', 'is_cof', 'departement'] + model = CofProfile + fields = ["login_clipper", "is_cof", "departement"] + class CofRestrictForm(CofForm): class Meta(CofForm.Meta): - fields = ['departement'] + fields = ["departement"] class UserForm(forms.ModelForm): class Meta: model = User - fields = ['username', 'first_name', 'last_name', 'email'] - help_texts = { - 'username': '' - } + fields = ["username", "first_name", "last_name", "email"] + help_texts = {"username": ""} class UserRestrictForm(UserForm): class Meta(UserForm.Meta): - fields = ['first_name', 'last_name'] + fields = ["first_name", "last_name"] + class UserRestrictTeamForm(UserForm): class Meta(UserForm.Meta): - fields = ['first_name', 'last_name', 'email'] + fields = ["first_name", "last_name", "email"] class AccountNegativeForm(forms.ModelForm): class Meta: - model = AccountNegative - fields = ['authz_overdraft_amount', 'authz_overdraft_until', - 'balance_offset', 'comment'] - widgets = { - 'authz_overdraft_until': DateTimeWidget(), - } + model = AccountNegative + fields = [ + "authz_overdraft_amount", + "authz_overdraft_until", + "balance_offset", + "comment", + ] + widgets = {"authz_overdraft_until": DateTimeWidget()} + # ----- # Checkout forms # ----- + class CheckoutForm(forms.ModelForm): class Meta: - model = Checkout - fields = ['name', 'valid_from', 'valid_to', 'balance', 'is_protected'] - widgets = { - 'valid_from': DateTimeWidget(), - 'valid_to' : DateTimeWidget(), - } + model = Checkout + fields = ["name", "valid_from", "valid_to", "balance", "is_protected"] + widgets = {"valid_from": DateTimeWidget(), "valid_to": DateTimeWidget()} + class CheckoutRestrictForm(CheckoutForm): class Meta(CheckoutForm.Meta): - fields = ['name', 'valid_from', 'valid_to'] + fields = ["name", "valid_from", "valid_to"] class CheckoutStatementCreateForm(forms.ModelForm): @@ -180,173 +189,206 @@ class CheckoutStatementCreateForm(forms.ModelForm): class Meta: model = CheckoutStatement - exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken', - 'balance_old', 'balance_new'] + exclude = [ + "by", + "at", + "checkout", + "amount_error", + "amount_taken", + "balance_old", + "balance_new", + ] def clean(self): - not_count = self.cleaned_data['not_count'] + not_count = self.cleaned_data["not_count"] if not not_count and ( - self.cleaned_data['balance_001'] is None - or self.cleaned_data['balance_002'] is None - or self.cleaned_data['balance_005'] is None - or self.cleaned_data['balance_01'] is None - or self.cleaned_data['balance_02'] is None - or self.cleaned_data['balance_05'] is None - or self.cleaned_data['balance_1'] is None - or self.cleaned_data['balance_2'] is None - or self.cleaned_data['balance_5'] is None - or self.cleaned_data['balance_10'] is None - or self.cleaned_data['balance_20'] is None - or self.cleaned_data['balance_50'] is None - or self.cleaned_data['balance_100'] is None - or self.cleaned_data['balance_200'] is None - or self.cleaned_data['balance_500'] is None): - raise ValidationError("Y'a un problème. Si tu comptes la caisse, mets au moins des 0 stp (et t'as pas idée de comment c'est long de vérifier que t'as mis des valeurs de partout...)") + self.cleaned_data["balance_001"] is None + or self.cleaned_data["balance_002"] is None + or self.cleaned_data["balance_005"] is None + or self.cleaned_data["balance_01"] is None + or self.cleaned_data["balance_02"] is None + or self.cleaned_data["balance_05"] is None + or self.cleaned_data["balance_1"] is None + or self.cleaned_data["balance_2"] is None + or self.cleaned_data["balance_5"] is None + or self.cleaned_data["balance_10"] is None + or self.cleaned_data["balance_20"] is None + or self.cleaned_data["balance_50"] is None + or self.cleaned_data["balance_100"] is None + or self.cleaned_data["balance_200"] is None + or self.cleaned_data["balance_500"] is None + ): + raise ValidationError( + "Y'a un problème. Si tu comptes la caisse, mets au moins des 0 stp (et t'as pas idée de comment c'est long de vérifier que t'as mis des valeurs de partout...)" + ) super().clean() + class CheckoutStatementUpdateForm(forms.ModelForm): class Meta: model = CheckoutStatement - exclude = ['by', 'at', 'checkout', 'amount_error', 'amount_taken'] + exclude = ["by", "at", "checkout", "amount_error", "amount_taken"] # ----- # Category # ----- + class CategoryForm(forms.ModelForm): class Meta: model = ArticleCategory - fields = ['name', 'has_addcost'] + fields = ["name", "has_addcost"] + # ----- # Article forms # ----- + class ArticleForm(forms.ModelForm): category_new = forms.CharField( - label="Créer une catégorie", - max_length=45, - required = False) + label="Créer une catégorie", max_length=45, required=False + ) category = forms.ModelChoiceField( - label="Catégorie", - queryset = ArticleCategory.objects.all(), - required = False) + label="Catégorie", queryset=ArticleCategory.objects.all(), required=False + ) suppliers = forms.ModelMultipleChoiceField( - label="Fournisseurs", - queryset = Supplier.objects.all(), - required = False) + label="Fournisseurs", queryset=Supplier.objects.all(), required=False + ) supplier_new = forms.CharField( - label="Créer un fournisseur", - max_length = 45, - required = False) + label="Créer un fournisseur", max_length=45, required=False + ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.instance.pk: - self.initial['suppliers'] = self.instance.suppliers.values_list('pk', flat=True) + self.initial["suppliers"] = self.instance.suppliers.values_list( + "pk", flat=True + ) def clean(self): - category = self.cleaned_data.get('category') - category_new = self.cleaned_data.get('category_new') + category = self.cleaned_data.get("category") + category_new = self.cleaned_data.get("category_new") if not category and not category_new: - raise ValidationError('Sélectionnez une catégorie ou créez en une') + raise ValidationError("Sélectionnez une catégorie ou créez en une") elif not category: category, _ = ArticleCategory.objects.get_or_create(name=category_new) - self.cleaned_data['category'] = category + self.cleaned_data["category"] = category super().clean() class Meta: - model = Article - fields = ['name', 'is_sold', 'hidden', 'price', 'stock', 'category', 'box_type', - 'box_capacity'] + model = Article + fields = [ + "name", + "is_sold", + "hidden", + "price", + "stock", + "category", + "box_type", + "box_capacity", + ] + class ArticleRestrictForm(ArticleForm): class Meta(ArticleForm.Meta): - fields = ['name', 'is_sold', 'hidden', 'price', 'category', 'box_type', - 'box_capacity'] + fields = [ + "name", + "is_sold", + "hidden", + "price", + "category", + "box_type", + "box_capacity", + ] + # ----- # K-Psul forms # ----- + class KPsulOperationGroupForm(forms.ModelForm): # FIXME(AD): Use timezone.now instead of timezone.now() to avoid using a # fixed datetime (application boot here). # One may even use: Checkout.objects.is_valid() if changing # to now = timezone.now is ok in 'is_valid' definition. checkout = forms.ModelChoiceField( - queryset = Checkout.objects.filter( - is_protected=False, valid_from__lte=timezone.now(), - valid_to__gte=timezone.now()), - widget = forms.HiddenInput()) + queryset=Checkout.objects.filter( + is_protected=False, + valid_from__lte=timezone.now(), + valid_to__gte=timezone.now(), + ), + widget=forms.HiddenInput(), + ) on_acc = forms.ModelChoiceField( - queryset = Account.objects.exclude(trigramme='GNR'), - widget = forms.HiddenInput()) + queryset=Account.objects.exclude(trigramme="GNR"), widget=forms.HiddenInput() + ) + class Meta: - model = OperationGroup - fields = ['on_acc', 'checkout', 'comment'] + model = OperationGroup + fields = ["on_acc", "checkout", "comment"] + class KPsulAccountForm(forms.ModelForm): class Meta: - model = Account - fields = ['trigramme'] + model = Account + fields = ["trigramme"] widgets = { - 'trigramme': forms.TextInput( - attrs={ - 'autocomplete': 'off', - 'spellcheck': 'false', - }), + "trigramme": forms.TextInput( + attrs={"autocomplete": "off", "spellcheck": "false"} + ) } class KPsulCheckoutForm(forms.Form): checkout = forms.ModelChoiceField( - queryset=None, - widget=forms.Select(attrs={'id': 'id_checkout_select'}), + queryset=None, widget=forms.Select(attrs={"id": "id_checkout_select"}) ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Create the queryset on form instanciation to use the current time. - self.fields['checkout'].queryset = ( - Checkout.objects.is_valid().filter(is_protected=False)) + self.fields["checkout"].queryset = Checkout.objects.is_valid().filter( + is_protected=False + ) class KPsulOperationForm(forms.ModelForm): article = forms.ModelChoiceField( - queryset=Article.objects.select_related('category').all(), + queryset=Article.objects.select_related("category").all(), required=False, - widget = forms.HiddenInput()) + widget=forms.HiddenInput(), + ) article_nb = forms.IntegerField( required=False, initial=None, validators=[validators.MinValueValidator(1)], widget=forms.HiddenInput(), ) + class Meta: - model = Operation - fields = ['type', 'amount', 'article', 'article_nb'] - widgets = { - 'type': forms.HiddenInput(), - 'amount': forms.HiddenInput(), - } + model = Operation + fields = ["type", "amount", "article", "article_nb"] + widgets = {"type": forms.HiddenInput(), "amount": forms.HiddenInput()} def clean(self): super().clean() - type_ope = self.cleaned_data.get('type') - amount = self.cleaned_data.get('amount') - article = self.cleaned_data.get('article') - article_nb = self.cleaned_data.get('article_nb') + type_ope = self.cleaned_data.get("type") + amount = self.cleaned_data.get("amount") + article = self.cleaned_data.get("article") + article_nb = self.cleaned_data.get("article_nb") errors = [] if type_ope and type_ope == Operation.PURCHASE: if not article or article_nb is None or article_nb < 1: - errors.append(ValidationError( - "Un achat nécessite un article et une quantité")) + errors.append( + ValidationError("Un achat nécessite un article et une quantité") + ) elif type_ope and type_ope in [Operation.DEPOSIT, Operation.WITHDRAW]: if not amount or article or article_nb: errors.append(ValidationError("Bad request")) @@ -355,8 +397,8 @@ class KPsulOperationForm(forms.ModelForm): errors.append(ValidationError("Charge non positive")) elif type_ope == Operation.WITHDRAW and amount >= 0: errors.append(ValidationError("Retrait non négatif")) - self.cleaned_data['article'] = None - self.cleaned_data['article_nb'] = None + self.cleaned_data["article"] = None + self.cleaned_data["article_nb"] = None if errors: raise ValidationError(errors) @@ -364,26 +406,29 @@ class KPsulOperationForm(forms.ModelForm): KPsulOperationFormSet = modelformset_factory( Operation, - form = KPsulOperationForm, - can_delete = True, - extra = 0, - min_num = 1, validate_min = True) + form=KPsulOperationForm, + can_delete=True, + extra=0, + min_num=1, + validate_min=True, +) + class AddcostForm(forms.Form): - trigramme = forms.CharField(required = False) + trigramme = forms.CharField(required=False) amount = forms.DecimalField( - required = False, - max_digits=6,decimal_places=2,min_value=Decimal(0)) + required=False, max_digits=6, decimal_places=2, min_value=Decimal(0) + ) def clean(self): - trigramme = self.cleaned_data.get('trigramme') + trigramme = self.cleaned_data.get("trigramme") if trigramme: try: Account.objects.get(trigramme=trigramme) except Account.DoesNotExist: - raise ValidationError('Compte invalide') + raise ValidationError("Compte invalide") else: - self.cleaned_data['amount'] = 0 + self.cleaned_data["amount"] = 0 super().clean() @@ -395,34 +440,40 @@ class AddcostForm(forms.Form): class KFetConfigForm(ConfigForm): kfet_reduction_cof = forms.DecimalField( - label='Réduction COF', initial=Decimal('20'), - max_digits=6, decimal_places=2, + label="Réduction COF", + initial=Decimal("20"), + max_digits=6, + decimal_places=2, help_text="Réduction, à donner en pourcentage, appliquée lors d'un " - "achat par un-e membre du COF sur le montant en euros.", + "achat par un-e membre du COF sur le montant en euros.", ) kfet_addcost_amount = forms.DecimalField( - label='Montant de la majoration (en €)', initial=Decimal('0'), + label="Montant de la majoration (en €)", + initial=Decimal("0"), required=False, - max_digits=6, decimal_places=2, + max_digits=6, + decimal_places=2, ) kfet_addcost_for = forms.ModelChoiceField( - label='Destinataire de la majoration', initial=None, required=False, - help_text='Laissez vide pour désactiver la majoration.', - queryset=(Account.objects - .select_related('cofprofile', 'cofprofile__user') - .all()), + label="Destinataire de la majoration", + initial=None, + required=False, + help_text="Laissez vide pour désactiver la majoration.", + queryset=( + Account.objects.select_related("cofprofile", "cofprofile__user").all() + ), ) kfet_overdraft_duration = forms.DurationField( - label='Durée du découvert autorisé par défaut', - initial=timedelta(days=1), + label="Durée du découvert autorisé par défaut", initial=timedelta(days=1) ) kfet_overdraft_amount = forms.DecimalField( - label='Montant du découvert autorisé par défaut (en €)', - initial=Decimal('20'), - max_digits=6, decimal_places=2, + label="Montant du découvert autorisé par défaut (en €)", + initial=Decimal("20"), + max_digits=6, + decimal_places=2, ) kfet_cancel_duration = forms.DurationField( - label='Durée pour annuler une commande sans mot de passe', + label="Durée pour annuler une commande sans mot de passe", initial=timedelta(minutes=5), ) @@ -438,105 +489,98 @@ class FilterHistoryForm(forms.Form): # Transfer forms # ----- + class TransferGroupForm(forms.ModelForm): class Meta: - model = TransferGroup - fields = ['comment'] + model = TransferGroup + fields = ["comment"] + class TransferForm(forms.ModelForm): from_acc = forms.ModelChoiceField( - queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13', 'GNR']), - widget = forms.HiddenInput() + queryset=Account.objects.exclude(trigramme__in=["LIQ", "#13", "GNR"]), + widget=forms.HiddenInput(), ) to_acc = forms.ModelChoiceField( - queryset = Account.objects.exclude(trigramme__in=['LIQ', '#13', 'GNR']), - widget = forms.HiddenInput() + queryset=Account.objects.exclude(trigramme__in=["LIQ", "#13", "GNR"]), + widget=forms.HiddenInput(), ) def clean_amount(self): - amount = self.cleaned_data['amount'] + amount = self.cleaned_data["amount"] if amount <= 0: raise forms.ValidationError("Montant invalide") return amount class Meta: - model = Transfer - fields = ['from_acc', 'to_acc', 'amount'] + model = Transfer + fields = ["from_acc", "to_acc", "amount"] + TransferFormSet = modelformset_factory( - Transfer, - form = TransferForm, - min_num = 1, validate_min = True, - extra = 9, + Transfer, form=TransferForm, min_num=1, validate_min=True, extra=9 ) # ----- # Inventory forms # ----- + class InventoryArticleForm(forms.Form): article = forms.ModelChoiceField( - queryset = Article.objects.all(), - widget = forms.HiddenInput(), - ) + queryset=Article.objects.all(), widget=forms.HiddenInput() + ) stock_new = forms.IntegerField(required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if 'initial' in kwargs: - self.name = kwargs['initial']['name'] - self.stock_old = kwargs['initial']['stock_old'] - self.category = kwargs['initial']['category'] - self.category_name = kwargs['initial']['category__name'] - self.box_capacity = kwargs['initial']['box_capacity'] + if "initial" in kwargs: + self.name = kwargs["initial"]["name"] + self.stock_old = kwargs["initial"]["stock_old"] + self.category = kwargs["initial"]["category"] + self.category_name = kwargs["initial"]["category__name"] + self.box_capacity = kwargs["initial"]["box_capacity"] + # ----- # Order forms # ----- - class OrderArticleForm(forms.Form): article = forms.ModelChoiceField( - queryset=Article.objects.all(), - widget=forms.HiddenInput(), - ) + queryset=Article.objects.all(), widget=forms.HiddenInput() + ) quantity_ordered = forms.IntegerField(required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if 'initial' in kwargs: - self.name = kwargs['initial']['name'] - self.stock = kwargs['initial']['stock'] - self.category = kwargs['initial']['category'] - self.category_name = kwargs['initial']['category__name'] - self.box_capacity = kwargs['initial']['box_capacity'] - self.v_all = kwargs['initial']['v_all'] - self.v_moy = kwargs['initial']['v_moy'] - self.v_et = kwargs['initial']['v_et'] - self.v_prev = kwargs['initial']['v_prev'] - self.c_rec = kwargs['initial']['c_rec'] + if "initial" in kwargs: + self.name = kwargs["initial"]["name"] + self.stock = kwargs["initial"]["stock"] + self.category = kwargs["initial"]["category"] + self.category_name = kwargs["initial"]["category__name"] + self.box_capacity = kwargs["initial"]["box_capacity"] + self.v_all = kwargs["initial"]["v_all"] + self.v_moy = kwargs["initial"]["v_moy"] + self.v_et = kwargs["initial"]["v_et"] + self.v_prev = kwargs["initial"]["v_prev"] + self.c_rec = kwargs["initial"]["c_rec"] + class OrderArticleToInventoryForm(forms.Form): article = forms.ModelChoiceField( - queryset = Article.objects.all(), - widget = forms.HiddenInput(), - ) - price_HT = forms.DecimalField( - max_digits = 7, decimal_places = 4, - required = False) - TVA = forms.DecimalField( - max_digits = 7, decimal_places = 2, - required = False) - rights = forms.DecimalField( - max_digits = 7, decimal_places = 4, - required = False) + queryset=Article.objects.all(), widget=forms.HiddenInput() + ) + price_HT = forms.DecimalField(max_digits=7, decimal_places=4, required=False) + TVA = forms.DecimalField(max_digits=7, decimal_places=2, required=False) + rights = forms.DecimalField(max_digits=7, decimal_places=4, required=False) quantity_received = forms.IntegerField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if 'initial' in kwargs: - self.name = kwargs['initial']['name'] - self.category = kwargs['initial']['category'] - self.category_name = kwargs['initial']['category__name'] - self.quantity_ordered = kwargs['initial']['quantity_ordered'] + if "initial" in kwargs: + self.name = kwargs["initial"]["name"] + self.category = kwargs["initial"]["category"] + self.category_name = kwargs["initial"]["category__name"] + self.quantity_ordered = kwargs["initial"]["quantity_ordered"] diff --git a/kfet/management/commands/createopes.py b/kfet/management/commands/createopes.py index 5a7699ae..03458ea0 100644 --- a/kfet/management/commands/createopes.py +++ b/kfet/management/commands/createopes.py @@ -1,4 +1,3 @@ - """ Crée des opérations aléatoires réparties sur une période de temps spécifiée """ @@ -6,29 +5,40 @@ Crée des opérations aléatoires réparties sur une période de temps spécifi import random from datetime import timedelta from decimal import Decimal -from django.utils import timezone -from django.core.management.base import BaseCommand -from kfet.models import (Account, Article, OperationGroup, Operation, - Checkout, Transfer, TransferGroup) +from django.core.management.base import BaseCommand +from django.utils import timezone + +from kfet.models import ( + Account, + Article, + Checkout, + Operation, + OperationGroup, + Transfer, + TransferGroup, +) class Command(BaseCommand): - help = ("Crée des opérations réparties uniformément " - "sur une période de temps") + help = "Crée des opérations réparties uniformément " "sur une période de temps" def add_arguments(self, parser): # Nombre d'opérations à créer - parser.add_argument('opes', type=int, - help='Number of opegroups to create') + parser.add_argument("opes", type=int, help="Number of opegroups to create") # Période sur laquelle créer (depuis num_days avant maintenant) - parser.add_argument('days', type=int, - help='Period in which to create opegroups') + parser.add_argument( + "days", type=int, help="Period in which to create opegroups" + ) # Optionnel : nombre de transfert à créer (défaut 0) - parser.add_argument('--transfers', type=int, default=0, - help='Number of transfers to create (default 0)') + parser.add_argument( + "--transfers", + type=int, + default=0, + help="Number of transfers to create (default 0)", + ) def handle(self, *args, **options): @@ -39,19 +49,19 @@ class Command(BaseCommand): purchases = 0 transfers = 0 - num_ops = options['opes'] - num_transfers = options['transfers'] + num_ops = options["opes"] + num_transfers = options["transfers"] # Convert to seconds - time = options['days'] * 24 * 3600 + time = options["days"] * 24 * 3600 now = timezone.now() checkout = Checkout.objects.first() articles = Article.objects.all() - accounts = Account.objects.exclude(trigramme='LIQ') - liq_account = Account.objects.get(trigramme='LIQ') + accounts = Account.objects.exclude(trigramme="LIQ") + liq_account = Account.objects.get(trigramme="LIQ") try: con_account = Account.objects.get( - cofprofile__user__first_name='Assurancetourix' + cofprofile__user__first_name="Assurancetourix" ) except Account.DoesNotExist: con_account = random.choice(accounts) @@ -78,12 +88,12 @@ class Command(BaseCommand): if random.random() < 0.2: addcost = True addcost_for = con_account - addcost_amount = Decimal('0.5') + addcost_amount = Decimal("0.5") else: addcost = False # Initialize opegroup amount - amount = Decimal('0') + amount = Decimal("0") # Generating operations ope_list = [] @@ -95,19 +105,18 @@ class Command(BaseCommand): if typevar > 0.9 and account != liq_account: ope = Operation( type=Operation.DEPOSIT, - amount=Decimal(random.randint(1, 99)/10) + amount=Decimal(random.randint(1, 99) / 10), ) # 0.05 probability to have a withdrawal elif typevar > 0.85 and account != liq_account: ope = Operation( type=Operation.WITHDRAW, - amount=-Decimal(random.randint(1, 99)/10) + amount=-Decimal(random.randint(1, 99) / 10), ) # 0.05 probability to have an edition elif typevar > 0.8 and account != liq_account: ope = Operation( - type=Operation.EDIT, - amount=Decimal(random.randint(1, 99)/10) + type=Operation.EDIT, amount=Decimal(random.randint(1, 99) / 10) ) else: article = random.choice(articles) @@ -115,9 +124,9 @@ class Command(BaseCommand): ope = Operation( type=Operation.PURCHASE, - amount=-article.price*nb, + amount=-article.price * nb, article=article, - article_nb=nb + article_nb=nb, ) purchases += 1 @@ -130,23 +139,23 @@ class Command(BaseCommand): ope_list.append(ope) amount += ope.amount - opegroup_list.append(OperationGroup( - on_acc=account, - checkout=checkout, - at=at, - is_cof=account.cofprofile.is_cof, - amount=amount, - )) + opegroup_list.append( + OperationGroup( + on_acc=account, + checkout=checkout, + at=at, + is_cof=account.cofprofile.is_cof, + amount=amount, + ) + ) at_list.append(at) - ope_by_grp.append((at, ope_list, )) + ope_by_grp.append((at, ope_list)) OperationGroup.objects.bulk_create(opegroup_list) # Fetch created OperationGroup objects pk by at - opegroups = (OperationGroup.objects - .filter(at__in=at_list) - .values('id', 'at')) - opegroups_by = {grp['at']: grp['id'] for grp in opegroups} + opegroups = OperationGroup.objects.filter(at__in=at_list).values("id", "at") + opegroups_by = {grp["at"]: grp["id"] for grp in opegroups} all_ope = [] for _ in range(num_ops): @@ -175,30 +184,28 @@ class Command(BaseCommand): else: comment = "" - transfergroup_list.append(TransferGroup( - at=at, - comment=comment, - valid_by=random.choice(accounts), - )) + transfergroup_list.append( + TransferGroup(at=at, comment=comment, valid_by=random.choice(accounts)) + ) at_list.append(at) # Randomly generate transfer transfer_list = [] for i in range(random.randint(1, 4)): - transfer_list.append(Transfer( - from_acc=random.choice(accounts), - to_acc=random.choice(accounts), - amount=Decimal(random.randint(1, 99)/10) - )) + transfer_list.append( + Transfer( + from_acc=random.choice(accounts), + to_acc=random.choice(accounts), + amount=Decimal(random.randint(1, 99) / 10), + ) + ) - transfer_by_grp.append((at, transfer_list, )) + transfer_by_grp.append((at, transfer_list)) TransferGroup.objects.bulk_create(transfergroup_list) - transfergroups = (TransferGroup.objects - .filter(at__in=at_list) - .values('id', 'at')) - transfergroups_by = {grp['at']: grp['id'] for grp in transfergroups} + transfergroups = TransferGroup.objects.filter(at__in=at_list).values("id", "at") + transfergroups_by = {grp["at"]: grp["id"] for grp in transfergroups} all_transfer = [] for _ in range(num_transfers): @@ -211,9 +218,10 @@ class Command(BaseCommand): transfers += len(all_transfer) self.stdout.write( - "- {:d} opérations créées dont {:d} commandes d'articles" - .format(opes_created, purchases)) + "- {:d} opérations créées dont {:d} commandes d'articles".format( + opes_created, purchases + ) + ) if transfers: - self.stdout.write("- {:d} transferts créés" - .format(transfers)) + self.stdout.write("- {:d} transferts créés".format(transfers)) diff --git a/kfet/management/commands/loadkfetdevdata.py b/kfet/management/commands/loadkfetdevdata.py index 6dd25f29..0543be80 100644 --- a/kfet/management/commands/loadkfetdevdata.py +++ b/kfet/management/commands/loadkfetdevdata.py @@ -6,18 +6,23 @@ import os import random from datetime import timedelta -from django.utils import timezone -from django.contrib.auth.models import User, Group, Permission, ContentType +from django.contrib.auth.models import ContentType, Group, Permission, User from django.core.management import call_command +from django.utils import timezone from gestioncof.management.base import MyBaseCommand from gestioncof.models import CofProfile -from kfet.models import (Account, Checkout, CheckoutStatement, Supplier, - SupplierArticle, Article) +from kfet.models import ( + Account, + Article, + Checkout, + CheckoutStatement, + Supplier, + SupplierArticle, +) # Où sont stockés les fichiers json -DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), - 'data') +DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data") class Command(MyBaseCommand): @@ -28,7 +33,7 @@ class Command(MyBaseCommand): # Groupes # --- - Group.objects.filter(name__icontains='K-Fêt').delete() + Group.objects.filter(name__icontains="K-Fêt").delete() group_chef = Group(name="K-Fêt César") group_boy = Group(name="K-Fêt Légionnaire") @@ -37,10 +42,11 @@ class Command(MyBaseCommand): group_boy.save() permissions_chef = Permission.objects.filter( - content_type__in=ContentType.objects.filter( - app_label='kfet')) + content_type__in=ContentType.objects.filter(app_label="kfet") + ) permissions_boy = Permission.objects.filter( - codename__in=['is_team', 'perform_deposit']) + codename__in=["is_team", "perform_deposit"] + ) group_chef.permissions.add(*permissions_chef) group_boy.permissions.add(*permissions_boy) @@ -51,11 +57,11 @@ class Command(MyBaseCommand): self.stdout.write("Création des comptes K-Fêt") - gaulois = CofProfile.objects.filter(user__last_name='Gaulois') - gaulois_trigramme = map('{:03d}'.format, range(50)) + gaulois = CofProfile.objects.filter(user__last_name="Gaulois") + gaulois_trigramme = map("{:03d}".format, range(50)) - romains = CofProfile.objects.filter(user__last_name='Romain') - romains_trigramme = map(lambda x: str(100+x), range(99)) + romains = CofProfile.objects.filter(user__last_name="Romain") + romains_trigramme = map(lambda x: str(100 + x), range(99)) created_accounts = 0 team_accounts = 0 @@ -64,18 +70,18 @@ class Command(MyBaseCommand): account, created = Account.objects.get_or_create( trigramme=trigramme, cofprofile=profile, - defaults={'balance': random.randint(1, 999)/10} + defaults={"balance": random.randint(1, 999) / 10}, ) created_accounts += int(created) - if profile.user.first_name == 'Abraracourcix': + if profile.user.first_name == "Abraracourcix": profile.user.groups.add(group_chef) for (profile, trigramme) in zip(romains, romains_trigramme): account, created = Account.objects.get_or_create( trigramme=trigramme, cofprofile=profile, - defaults={'balance': random.randint(1, 999)/10} + defaults={"balance": random.randint(1, 999) / 10}, ) created_accounts += int(created) @@ -83,47 +89,50 @@ class Command(MyBaseCommand): profile.user.groups.add(group_boy) team_accounts += 1 - self.stdout.write("- {:d} comptes créés, {:d} dans l'équipe K-Fêt" - .format(created_accounts, team_accounts)) + self.stdout.write( + "- {:d} comptes créés, {:d} dans l'équipe K-Fêt".format( + created_accounts, team_accounts + ) + ) # Compte liquide self.stdout.write("Création du compte liquide") - liq_user, _ = User.objects.get_or_create(username='liquide') + liq_user, _ = User.objects.get_or_create(username="liquide") liq_profile, _ = CofProfile.objects.get_or_create(user=liq_user) - liq_account, _ = Account.objects.get_or_create(cofprofile=liq_profile, - trigramme='LIQ') + liq_account, _ = Account.objects.get_or_create( + cofprofile=liq_profile, trigramme="LIQ" + ) # Root account if existing - root_profile = CofProfile.objects.filter(user__username='root') + root_profile = CofProfile.objects.filter(user__username="root") if root_profile.exists(): self.stdout.write("Création du compte K-Fêt root") root_profile = root_profile.get() - Account.objects.get_or_create(cofprofile=root_profile, - trigramme='AAA') + Account.objects.get_or_create(cofprofile=root_profile, trigramme="AAA") # --- # Caisse # --- checkout, created = Checkout.objects.get_or_create( - created_by=Account.objects.get(trigramme='000'), - name='Chaudron', + created_by=Account.objects.get(trigramme="000"), + name="Chaudron", defaults={ - 'valid_from': timezone.now(), - 'valid_to': timezone.now() + timedelta(days=730) + "valid_from": timezone.now(), + "valid_to": timezone.now() + timedelta(days=730), }, ) if created: CheckoutStatement.objects.create( - by=Account.objects.get(trigramme='000'), + by=Account.objects.get(trigramme="000"), checkout=checkout, balance_old=0, balance_new=0, amount_taken=0, - amount_error=0 + amount_error=0, ) # --- @@ -135,10 +144,7 @@ class Command(MyBaseCommand): articles = random.sample(list(Article.objects.all()), 40) to_create = [] for article in articles: - to_create.append(SupplierArticle( - supplier=supplier, - article=article - )) + to_create.append(SupplierArticle(supplier=supplier, article=article)) SupplierArticle.objects.bulk_create(to_create) @@ -146,10 +152,10 @@ class Command(MyBaseCommand): # Opérations # --- - call_command('createopes', '100', '7', '--transfers=20') + call_command("createopes", "100", "7", "--transfers=20") # --- # Wagtail CMS # --- - call_command('kfet_loadwagtail') + call_command("kfet_loadwagtail") diff --git a/kfet/migrations/0001_initial.py b/kfet/migrations/0001_initial.py index 8f9b14fa..8dbad4a8 100644 --- a/kfet/migrations/0001_initial.py +++ b/kfet/migrations/0001_initial.py @@ -1,257 +1,713 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations -import django.db.models.deletion -import django.core.validators import datetime +import django.core.validators +import django.db.models.deletion +from django.db import migrations, models + class Migration(migrations.Migration): - dependencies = [ - ('gestioncof', '0007_alter_club'), - ] + dependencies = [("gestioncof", "0007_alter_club")] operations = [ migrations.CreateModel( - name='Account', + name="Account", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('trigramme', models.CharField(max_length=3, validators=[django.core.validators.RegexValidator(regex='^[^a-z]{3}$')], unique=True)), - ('balance', models.DecimalField(decimal_places=2, default=0, max_digits=6)), - ('frozen', models.BooleanField(default=False)), - ('promo', models.IntegerField(null=True, blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016)], default=2015)), - ('nickname', models.CharField(max_length=255, blank=True, default='')), - ('password', models.CharField(max_length=255, blank=True, null=True, unique=True, default=None)), - ('cofprofile', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='account_kfet', to='gestioncof.CofProfile')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "trigramme", + models.CharField( + max_length=3, + validators=[ + django.core.validators.RegexValidator(regex="^[^a-z]{3}$") + ], + unique=True, + ), + ), + ( + "balance", + models.DecimalField(decimal_places=2, default=0, max_digits=6), + ), + ("frozen", models.BooleanField(default=False)), + ( + "promo", + models.IntegerField( + null=True, + blank=True, + choices=[ + (1980, 1980), + (1981, 1981), + (1982, 1982), + (1983, 1983), + (1984, 1984), + (1985, 1985), + (1986, 1986), + (1987, 1987), + (1988, 1988), + (1989, 1989), + (1990, 1990), + (1991, 1991), + (1992, 1992), + (1993, 1993), + (1994, 1994), + (1995, 1995), + (1996, 1996), + (1997, 1997), + (1998, 1998), + (1999, 1999), + (2000, 2000), + (2001, 2001), + (2002, 2002), + (2003, 2003), + (2004, 2004), + (2005, 2005), + (2006, 2006), + (2007, 2007), + (2008, 2008), + (2009, 2009), + (2010, 2010), + (2011, 2011), + (2012, 2012), + (2013, 2013), + (2014, 2014), + (2015, 2015), + (2016, 2016), + ], + default=2015, + ), + ), + ("nickname", models.CharField(max_length=255, blank=True, default="")), + ( + "password", + models.CharField( + max_length=255, blank=True, null=True, unique=True, default=None + ), + ), + ( + "cofprofile", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="account_kfet", + to="gestioncof.CofProfile", + ), + ), ], ), migrations.CreateModel( - name='AccountNegative', + name="AccountNegative", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start', models.DateTimeField(default=datetime.datetime(2016, 8, 2, 10, 22, 1, 569492))), - ('balance_offset', models.DecimalField(decimal_places=2, max_digits=6)), - ('authorized_overdraft', models.DecimalField(decimal_places=2, default=0, max_digits=6)), - ('comment', models.CharField(max_length=255, blank=True)), - ('account', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='negative', to='kfet.Account')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "start", + models.DateTimeField( + default=datetime.datetime(2016, 8, 2, 10, 22, 1, 569492) + ), + ), + ("balance_offset", models.DecimalField(decimal_places=2, max_digits=6)), + ( + "authorized_overdraft", + models.DecimalField(decimal_places=2, default=0, max_digits=6), + ), + ("comment", models.CharField(max_length=255, blank=True)), + ( + "account", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="negative", + to="kfet.Account", + ), + ), ], ), migrations.CreateModel( - name='Article', + name="Article", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=45)), - ('is_sold', models.BooleanField(default=True)), - ('price', models.DecimalField(decimal_places=2, max_digits=6)), - ('stock', models.IntegerField(default=0)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=45)), + ("is_sold", models.BooleanField(default=True)), + ("price", models.DecimalField(decimal_places=2, max_digits=6)), + ("stock", models.IntegerField(default=0)), ], ), migrations.CreateModel( - name='ArticleCategory', + name="ArticleCategory", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=45)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=45)), ], ), migrations.CreateModel( - name='ArticleRule', + name="ArticleRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('ratio', models.PositiveSmallIntegerField()), - ('article_on', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='rule_on', to='kfet.Article')), - ('article_to', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='rule_to', to='kfet.Article')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("ratio", models.PositiveSmallIntegerField()), + ( + "article_on", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="rule_on", + to="kfet.Article", + ), + ), + ( + "article_to", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="rule_to", + to="kfet.Article", + ), + ), ], ), migrations.CreateModel( - name='Checkout', + name="Checkout", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=45)), - ('valid_from', models.DateTimeField()), - ('valid_to', models.DateTimeField()), - ('balance', models.DecimalField(decimal_places=2, max_digits=6)), - ('is_protected', models.BooleanField(default=False)), - ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.Account')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=45)), + ("valid_from", models.DateTimeField()), + ("valid_to", models.DateTimeField()), + ("balance", models.DecimalField(decimal_places=2, max_digits=6)), + ("is_protected", models.BooleanField(default=False)), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="kfet.Account", + ), + ), ], ), migrations.CreateModel( - name='CheckoutTransfer', + name="CheckoutTransfer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount', models.DecimalField(decimal_places=2, max_digits=6)), - ('from_checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_from', to='kfet.Checkout')), - ('to_checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_to', to='kfet.Checkout')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("amount", models.DecimalField(decimal_places=2, max_digits=6)), + ( + "from_checkout", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="transfers_from", + to="kfet.Checkout", + ), + ), + ( + "to_checkout", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="transfers_to", + to="kfet.Checkout", + ), + ), ], ), migrations.CreateModel( - name='Inventory', + name="Inventory", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('at', models.DateTimeField(auto_now_add=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("at", models.DateTimeField(auto_now_add=True)), ], ), migrations.CreateModel( - name='InventoryArticle', + name="InventoryArticle", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('stock_old', models.IntegerField()), - ('stock_new', models.IntegerField()), - ('stock_error', models.IntegerField(default=0)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Article')), - ('inventory', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Inventory')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("stock_old", models.IntegerField()), + ("stock_new", models.IntegerField()), + ("stock_error", models.IntegerField(default=0)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="kfet.Article" + ), + ), + ( + "inventory", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="kfet.Inventory" + ), + ), ], ), migrations.CreateModel( - name='Operation', + name="Operation", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(max_length=8, choices=[('purchase', 'Achat'), ('deposit', 'Charge'), ('withdraw', 'Retrait')])), - ('amount', models.DecimalField(decimal_places=2, max_digits=6)), - ('on_checkout', models.BooleanField(default=True)), - ('canceled_at', models.DateTimeField(blank=True, null=True, default=None)), - ('addcost_amount', models.DecimalField(decimal_places=2, max_digits=6)), - ('addcost_for', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='addcosts', to='kfet.Account', null=True, default=None)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='operations', to='kfet.Article', null=True, default=None)), - ('canceled_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=None)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.CharField( + max_length=8, + choices=[ + ("purchase", "Achat"), + ("deposit", "Charge"), + ("withdraw", "Retrait"), + ], + ), + ), + ("amount", models.DecimalField(decimal_places=2, max_digits=6)), + ("on_checkout", models.BooleanField(default=True)), + ( + "canceled_at", + models.DateTimeField(blank=True, null=True, default=None), + ), + ("addcost_amount", models.DecimalField(decimal_places=2, max_digits=6)), + ( + "addcost_for", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="addcosts", + to="kfet.Account", + null=True, + default=None, + ), + ), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="operations", + to="kfet.Article", + null=True, + default=None, + ), + ), + ( + "canceled_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="+", + to="kfet.Account", + null=True, + default=None, + ), + ), ], ), migrations.CreateModel( - name='OperationGroup', + name="OperationGroup", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('at', models.DateTimeField(auto_now_add=True)), - ('amount', models.IntegerField()), - ('is_cof', models.BooleanField(default=False)), - ('comment', models.CharField(max_length=255, blank=True, default='')), - ('checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='operations', to='kfet.Checkout')), - ('on_acc', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='operations', to='kfet.Account')), - ('valid_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("at", models.DateTimeField(auto_now_add=True)), + ("amount", models.IntegerField()), + ("is_cof", models.BooleanField(default=False)), + ("comment", models.CharField(max_length=255, blank=True, default="")), + ( + "checkout", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="operations", + to="kfet.Checkout", + ), + ), + ( + "on_acc", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="operations", + to="kfet.Account", + ), + ), + ( + "valid_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="+", + to="kfet.Account", + null=True, + default=True, + ), + ), ], ), migrations.CreateModel( - name='Order', + name="Order", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('at', models.DateTimeField(auto_now_add=True)), - ('amount', models.DecimalField(decimal_places=2, max_digits=6)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("at", models.DateTimeField(auto_now_add=True)), + ("amount", models.DecimalField(decimal_places=2, max_digits=6)), ], ), migrations.CreateModel( - name='OrderArticle', + name="OrderArticle", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity_ordered', models.IntegerField()), - ('quantity_received', models.IntegerField()), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Article')), - ('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Order')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("quantity_ordered", models.IntegerField()), + ("quantity_received", models.IntegerField()), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="kfet.Article" + ), + ), + ( + "order", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="kfet.Order" + ), + ), ], ), migrations.CreateModel( - name='Statement', + name="Statement", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('balance_old', models.DecimalField(decimal_places=2, max_digits=6)), - ('balance_new', models.DecimalField(decimal_places=2, max_digits=6)), - ('amount_taken', models.DecimalField(decimal_places=2, max_digits=6)), - ('amount_error', models.DecimalField(decimal_places=2, max_digits=6)), - ('at', models.DateTimeField(auto_now_add=True)), - ('by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.Account')), - ('checkout', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='statements', to='kfet.Checkout')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("balance_old", models.DecimalField(decimal_places=2, max_digits=6)), + ("balance_new", models.DecimalField(decimal_places=2, max_digits=6)), + ("amount_taken", models.DecimalField(decimal_places=2, max_digits=6)), + ("amount_error", models.DecimalField(decimal_places=2, max_digits=6)), + ("at", models.DateTimeField(auto_now_add=True)), + ( + "by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="kfet.Account", + ), + ), + ( + "checkout", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="statements", + to="kfet.Checkout", + ), + ), ], ), migrations.CreateModel( - name='Supplier', + name="Supplier", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=45)), - ('address', models.TextField()), - ('email', models.EmailField(max_length=254)), - ('phone', models.CharField(max_length=10)), - ('comment', models.TextField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=45)), + ("address", models.TextField()), + ("email", models.EmailField(max_length=254)), + ("phone", models.CharField(max_length=10)), + ("comment", models.TextField()), ], ), migrations.CreateModel( - name='SupplierArticle', + name="SupplierArticle", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('box_type', models.CharField(max_length=7, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')])), - ('box_capacity', models.PositiveSmallIntegerField()), - ('price_HT', models.DecimalField(decimal_places=4, max_digits=7)), - ('TVA', models.DecimalField(decimal_places=2, max_digits=4)), - ('rights', models.DecimalField(decimal_places=4, max_digits=7)), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Article')), - ('supplier', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='kfet.Supplier')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "box_type", + models.CharField( + max_length=7, + choices=[ + ("caisse", "Caisse"), + ("carton", "Carton"), + ("palette", "Palette"), + ("fût", "Fût"), + ], + ), + ), + ("box_capacity", models.PositiveSmallIntegerField()), + ("price_HT", models.DecimalField(decimal_places=4, max_digits=7)), + ("TVA", models.DecimalField(decimal_places=2, max_digits=4)), + ("rights", models.DecimalField(decimal_places=4, max_digits=7)), + ( + "article", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="kfet.Article" + ), + ), + ( + "supplier", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="kfet.Supplier" + ), + ), ], ), migrations.CreateModel( - name='Transfer', + name="Transfer", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('amount', models.DecimalField(decimal_places=2, max_digits=6)), - ('canceled_at', models.DateTimeField(blank=True, null=True, default=None)), - ('canceled_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=None)), - ('from_acc', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_from', to='kfet.Account')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("amount", models.DecimalField(decimal_places=2, max_digits=6)), + ( + "canceled_at", + models.DateTimeField(blank=True, null=True, default=None), + ), + ( + "canceled_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="+", + to="kfet.Account", + null=True, + default=None, + ), + ), + ( + "from_acc", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="transfers_from", + to="kfet.Account", + ), + ), ], ), migrations.CreateModel( - name='TransferGroup', + name="TransferGroup", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('at', models.DateTimeField(auto_now_add=True)), - ('comment', models.CharField(max_length=255, blank=True, default='')), - ('valid_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='+', to='kfet.Account', null=True, default=None)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("at", models.DateTimeField(auto_now_add=True)), + ("comment", models.CharField(max_length=255, blank=True, default="")), + ( + "valid_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="+", + to="kfet.Account", + null=True, + default=None, + ), + ), ], ), migrations.AddField( - model_name='transfer', - name='group', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers', to='kfet.TransferGroup'), + model_name="transfer", + name="group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="transfers", + to="kfet.TransferGroup", + ), ), migrations.AddField( - model_name='transfer', - name='to_acc', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='transfers_to', to='kfet.Account'), + model_name="transfer", + name="to_acc", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="transfers_to", + to="kfet.Account", + ), ), migrations.AddField( - model_name='supplier', - name='articles', - field=models.ManyToManyField(related_name='suppliers', through='kfet.SupplierArticle', to='kfet.Article'), + model_name="supplier", + name="articles", + field=models.ManyToManyField( + related_name="suppliers", + through="kfet.SupplierArticle", + to="kfet.Article", + ), ), migrations.AddField( - model_name='order', - name='articles', - field=models.ManyToManyField(related_name='orders', through='kfet.OrderArticle', to='kfet.Article'), + model_name="order", + name="articles", + field=models.ManyToManyField( + related_name="orders", through="kfet.OrderArticle", to="kfet.Article" + ), ), migrations.AddField( - model_name='order', - name='supplier', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='orders', to='kfet.Supplier'), + model_name="order", + name="supplier", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="orders", + to="kfet.Supplier", + ), ), migrations.AddField( - model_name='operation', - name='group', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.OperationGroup'), + model_name="operation", + name="group", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="kfet.OperationGroup", + ), ), migrations.AddField( - model_name='inventory', - name='articles', - field=models.ManyToManyField(related_name='inventories', through='kfet.InventoryArticle', to='kfet.Article'), + model_name="inventory", + name="articles", + field=models.ManyToManyField( + related_name="inventories", + through="kfet.InventoryArticle", + to="kfet.Article", + ), ), migrations.AddField( - model_name='inventory', - name='by', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='kfet.Account'), + model_name="inventory", + name="by", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="kfet.Account", + ), ), migrations.AddField( - model_name='inventory', - name='order', - field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, blank=True, related_name='inventory', to='kfet.Order', null=True, default=None), + model_name="inventory", + name="order", + field=models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + blank=True, + related_name="inventory", + to="kfet.Order", + null=True, + default=None, + ), ), migrations.AddField( - model_name='article', - name='category', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='articles', to='kfet.ArticleCategory'), + model_name="article", + name="category", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="articles", + to="kfet.ArticleCategory", + ), ), ] diff --git a/kfet/migrations/0002_auto_20160802_2139.py b/kfet/migrations/0002_auto_20160802_2139.py index 0a59de44..39ccbbe5 100644 --- a/kfet/migrations/0002_auto_20160802_2139.py +++ b/kfet/migrations/0002_auto_20160802_2139.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import datetime +from django.db import migrations, models + class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0001_initial'), - ] + dependencies = [("kfet", "0001_initial")] operations = [ migrations.AlterModelOptions( - name='account', - options={'permissions': (('is_team', 'Is part of the team'),)}, + name="account", + options={"permissions": (("is_team", "Is part of the team"),)}, ), migrations.AlterField( - model_name='accountnegative', - name='start', - field=models.DateTimeField(default=datetime.datetime(2016, 8, 2, 21, 39, 30, 52279)), + model_name="accountnegative", + name="start", + field=models.DateTimeField( + default=datetime.datetime(2016, 8, 2, 21, 39, 30, 52279) + ), ), ] diff --git a/kfet/migrations/0003_auto_20160802_2142.py b/kfet/migrations/0003_auto_20160802_2142.py index 586146de..c3bfda52 100644 --- a/kfet/migrations/0003_auto_20160802_2142.py +++ b/kfet/migrations/0003_auto_20160802_2142.py @@ -1,20 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import datetime +from django.db import migrations, models + class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0002_auto_20160802_2139'), - ] + dependencies = [("kfet", "0002_auto_20160802_2139")] operations = [ migrations.AlterField( - model_name='accountnegative', - name='start', + model_name="accountnegative", + name="start", field=models.DateTimeField(default=datetime.datetime.now), - ), + ) ] diff --git a/kfet/migrations/0004_auto_20160802_2144.py b/kfet/migrations/0004_auto_20160802_2144.py index b9e9d0d3..48646cef 100644 --- a/kfet/migrations/0004_auto_20160802_2144.py +++ b/kfet/migrations/0004_auto_20160802_2144.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0003_auto_20160802_2142'), - ] + dependencies = [("kfet", "0003_auto_20160802_2142")] operations = [ migrations.AlterField( - model_name='accountnegative', - name='balance_offset', + model_name="accountnegative", + name="balance_offset", field=models.DecimalField(decimal_places=2, max_digits=6, default=0), - ), + ) ] diff --git a/kfet/migrations/0005_auto_20160802_2154.py b/kfet/migrations/0005_auto_20160802_2154.py index a01419fc..49c96b1c 100644 --- a/kfet/migrations/0005_auto_20160802_2154.py +++ b/kfet/migrations/0005_auto_20160802_2154.py @@ -1,28 +1,31 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0004_auto_20160802_2144'), - ] + dependencies = [("kfet", "0004_auto_20160802_2144")] operations = [ migrations.CreateModel( - name='GlobalPermissions', + name="GlobalPermissions", fields=[ - ('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)), + ( + "id", + models.AutoField( + serialize=False, + primary_key=True, + verbose_name="ID", + auto_created=True, + ), + ) ], options={ - 'permissions': (('is_team', 'Is part of the team'),), - 'managed': False, + "permissions": (("is_team", "Is part of the team"),), + "managed": False, }, ), - migrations.AlterModelOptions( - name='account', - options={}, - ), + migrations.AlterModelOptions(name="account", options={}), ] diff --git a/kfet/migrations/0006_auto_20160804_0600.py b/kfet/migrations/0006_auto_20160804_0600.py index 063504b9..524e1352 100644 --- a/kfet/migrations/0006_auto_20160804_0600.py +++ b/kfet/migrations/0006_auto_20160804_0600.py @@ -1,28 +1,23 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0005_auto_20160802_2154'), - ] + dependencies = [("kfet", "0005_auto_20160802_2154")] operations = [ migrations.AlterModelOptions( - name='checkout', - options={'ordering': ['-valid_to']}, + name="checkout", options={"ordering": ["-valid_to"]} ), migrations.RenameField( - model_name='account', - old_name='frozen', - new_name='is_frozen', + model_name="account", old_name="frozen", new_name="is_frozen" ), migrations.AlterField( - model_name='checkout', - name='balance', + model_name="checkout", + name="balance", field=models.DecimalField(max_digits=6, default=0, decimal_places=2), ), ] diff --git a/kfet/migrations/0007_auto_20160804_0641.py b/kfet/migrations/0007_auto_20160804_0641.py index 70bff402..fb870a71 100644 --- a/kfet/migrations/0007_auto_20160804_0641.py +++ b/kfet/migrations/0007_auto_20160804_0641.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0006_auto_20160804_0600'), - ] + dependencies = [("kfet", "0006_auto_20160804_0600")] operations = [ migrations.AlterField( - model_name='article', - name='price', + model_name="article", + name="price", field=models.DecimalField(default=0, max_digits=6, decimal_places=2), - ), + ) ] diff --git a/kfet/migrations/0008_auto_20160804_1736.py b/kfet/migrations/0008_auto_20160804_1736.py index 1abbb76a..930562b0 100644 --- a/kfet/migrations/0008_auto_20160804_1736.py +++ b/kfet/migrations/0008_auto_20160804_1736.py @@ -1,20 +1,23 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import django.core.validators +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0007_auto_20160804_0641'), - ] + dependencies = [("kfet", "0007_auto_20160804_0641")] operations = [ migrations.AlterField( - model_name='account', - name='trigramme', - field=models.CharField(unique=True, validators=[django.core.validators.RegexValidator(regex='^[^a-z]{3}$')], db_index=True, max_length=3), - ), + model_name="account", + name="trigramme", + field=models.CharField( + unique=True, + validators=[django.core.validators.RegexValidator(regex="^[^a-z]{3}$")], + db_index=True, + max_length=3, + ), + ) ] diff --git a/kfet/migrations/0009_auto_20160805_0720.py b/kfet/migrations/0009_auto_20160805_0720.py index 8e9a4db9..90a19749 100644 --- a/kfet/migrations/0009_auto_20160805_0720.py +++ b/kfet/migrations/0009_auto_20160805_0720.py @@ -1,24 +1,20 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0008_auto_20160804_1736'), - ] + dependencies = [("kfet", "0008_auto_20160804_1736")] operations = [ migrations.RenameField( - model_name='operation', - old_name='on_checkout', - new_name='is_checkout', + model_name="operation", old_name="on_checkout", new_name="is_checkout" ), migrations.AddField( - model_name='operation', - name='article_nb', + model_name="operation", + name="article_nb", field=models.PositiveSmallIntegerField(default=None, null=True, blank=True), ), ] diff --git a/kfet/migrations/0010_auto_20160806_2343.py b/kfet/migrations/0010_auto_20160806_2343.py index 60c8cc93..84267a6d 100644 --- a/kfet/migrations/0010_auto_20160806_2343.py +++ b/kfet/migrations/0010_auto_20160806_2343.py @@ -1,30 +1,35 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0009_auto_20160805_0720'), - ] + dependencies = [("kfet", "0009_auto_20160805_0720")] operations = [ migrations.AlterField( - model_name='operation', - name='addcost_amount', + model_name="operation", + name="addcost_amount", field=models.DecimalField(max_digits=6, default=0, decimal_places=2), ), migrations.AlterField( - model_name='operationgroup', - name='amount', + model_name="operationgroup", + name="amount", field=models.DecimalField(max_digits=6, default=0, decimal_places=2), ), migrations.AlterField( - model_name='operationgroup', - name='valid_by', - field=models.ForeignKey(default=None, related_name='+', to='kfet.Account', blank=True, null=True, on_delete=django.db.models.deletion.PROTECT), + model_name="operationgroup", + name="valid_by", + field=models.ForeignKey( + default=None, + related_name="+", + to="kfet.Account", + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + ), ), ] diff --git a/kfet/migrations/0011_auto_20160807_1720.py b/kfet/migrations/0011_auto_20160807_1720.py index 97525676..53064235 100644 --- a/kfet/migrations/0011_auto_20160807_1720.py +++ b/kfet/migrations/0011_auto_20160807_1720.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0010_auto_20160806_2343'), - ] + dependencies = [("kfet", "0010_auto_20160806_2343")] operations = [ migrations.AlterField( - model_name='operation', - name='amount', - field=models.DecimalField(decimal_places=2, max_digits=6, default=0, blank=True), - ), + model_name="operation", + name="amount", + field=models.DecimalField( + decimal_places=2, max_digits=6, default=0, blank=True + ), + ) ] diff --git a/kfet/migrations/0012_settings.py b/kfet/migrations/0012_settings.py index 8f0f6247..1bae911d 100644 --- a/kfet/migrations/0012_settings.py +++ b/kfet/migrations/0012_settings.py @@ -1,22 +1,37 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0011_auto_20160807_1720'), - ] + dependencies = [("kfet", "0011_auto_20160807_1720")] operations = [ migrations.CreateModel( - name='Settings', + name="Settings", fields=[ - ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), - ('name', models.CharField(max_length=45)), - ('value_decimal', models.DecimalField(null=True, max_digits=6, decimal_places=2, blank=True, default=None)), + ( + "id", + models.AutoField( + serialize=False, + auto_created=True, + primary_key=True, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=45)), + ( + "value_decimal", + models.DecimalField( + null=True, + max_digits=6, + decimal_places=2, + blank=True, + default=None, + ), + ), ], - ), + ) ] diff --git a/kfet/migrations/0013_auto_20160807_1840.py b/kfet/migrations/0013_auto_20160807_1840.py index d7ce2c75..9a9ac3b1 100644 --- a/kfet/migrations/0013_auto_20160807_1840.py +++ b/kfet/migrations/0013_auto_20160807_1840.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0012_settings'), - ] + dependencies = [("kfet", "0012_settings")] operations = [ migrations.AlterField( - model_name='settings', - name='name', + model_name="settings", + name="name", field=models.CharField(unique=True, max_length=45), - ), + ) ] diff --git a/kfet/migrations/0014_auto_20160807_2314.py b/kfet/migrations/0014_auto_20160807_2314.py index 50417091..ecaee428 100644 --- a/kfet/migrations/0014_auto_20160807_2314.py +++ b/kfet/migrations/0014_auto_20160807_2314.py @@ -1,18 +1,22 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0013_auto_20160807_1840'), - ] + dependencies = [("kfet", "0013_auto_20160807_1840")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('can_perform_deposit', 'Peut effectuer une charge')), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("can_perform_deposit", "Peut effectuer une charge"), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0015_auto_20160807_2324.py b/kfet/migrations/0015_auto_20160807_2324.py index a1789fc2..fa2af882 100644 --- a/kfet/migrations/0015_auto_20160807_2324.py +++ b/kfet/migrations/0015_auto_20160807_2324.py @@ -1,18 +1,26 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0014_auto_20160807_2314'), - ] + dependencies = [("kfet", "0014_auto_20160807_2314")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('can_perform_deposit', 'Peut effectuer une charge'), ('can_perform_negative_operations', 'Peut enregistrer des commandes en négatif')), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("can_perform_deposit", "Peut effectuer une charge"), + ( + "can_perform_negative_operations", + "Peut enregistrer des commandes en négatif", + ), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0016_settings_value_account.py b/kfet/migrations/0016_settings_value_account.py index 53793938..e10eb682 100644 --- a/kfet/migrations/0016_settings_value_account.py +++ b/kfet/migrations/0016_settings_value_account.py @@ -1,20 +1,24 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0015_auto_20160807_2324'), - ] + dependencies = [("kfet", "0015_auto_20160807_2324")] operations = [ migrations.AddField( - model_name='settings', - name='value_account', - field=models.ForeignKey(to='kfet.Account', on_delete=django.db.models.deletion.PROTECT, default=None, null=True, blank=True), - ), + model_name="settings", + name="value_account", + field=models.ForeignKey( + to="kfet.Account", + on_delete=django.db.models.deletion.PROTECT, + default=None, + null=True, + blank=True, + ), + ) ] diff --git a/kfet/migrations/0017_auto_20160808_0234.py b/kfet/migrations/0017_auto_20160808_0234.py index e8aa8ec0..795078a9 100644 --- a/kfet/migrations/0017_auto_20160808_0234.py +++ b/kfet/migrations/0017_auto_20160808_0234.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0016_settings_value_account'), - ] + dependencies = [("kfet", "0016_settings_value_account")] operations = [ migrations.AlterField( - model_name='operation', - name='addcost_amount', - field=models.DecimalField(blank=True, null=True, decimal_places=2, default=None, max_digits=6), - ), + model_name="operation", + name="addcost_amount", + field=models.DecimalField( + blank=True, null=True, decimal_places=2, default=None, max_digits=6 + ), + ) ] diff --git a/kfet/migrations/0018_auto_20160808_0341.py b/kfet/migrations/0018_auto_20160808_0341.py index 384e82b2..3b29b716 100644 --- a/kfet/migrations/0018_auto_20160808_0341.py +++ b/kfet/migrations/0018_auto_20160808_0341.py @@ -1,18 +1,27 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0017_auto_20160808_0234'), - ] + dependencies = [("kfet", "0017_auto_20160808_0234")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('can_perform_deposit', 'Effectuer une charge'), ('can_perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte")), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("can_perform_deposit", "Effectuer une charge"), + ( + "can_perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0019_auto_20160808_0343.py b/kfet/migrations/0019_auto_20160808_0343.py index 6512b7ea..f500032a 100644 --- a/kfet/migrations/0019_auto_20160808_0343.py +++ b/kfet/migrations/0019_auto_20160808_0343.py @@ -1,18 +1,27 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0018_auto_20160808_0341'), - ] + dependencies = [("kfet", "0018_auto_20160808_0341")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"))}, - ), + name="globalpermissions", + options={ + "managed": False, + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ), + }, + ) ] diff --git a/kfet/migrations/0020_auto_20160808_0450.py b/kfet/migrations/0020_auto_20160808_0450.py index 2ecc18ee..d3424bac 100644 --- a/kfet/migrations/0020_auto_20160808_0450.py +++ b/kfet/migrations/0020_auto_20160808_0450.py @@ -1,20 +1,21 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import datetime +from django.db import migrations, models + class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0019_auto_20160808_0343'), - ] + dependencies = [("kfet", "0019_auto_20160808_0343")] operations = [ migrations.AlterField( - model_name='accountnegative', - name='start', - field=models.DateTimeField(default=datetime.datetime.now, blank=True, null=True), - ), + model_name="accountnegative", + name="start", + field=models.DateTimeField( + default=datetime.datetime.now, blank=True, null=True + ), + ) ] diff --git a/kfet/migrations/0021_auto_20160808_0506.py b/kfet/migrations/0021_auto_20160808_0506.py index 61a7ef65..2ef48232 100644 --- a/kfet/migrations/0021_auto_20160808_0506.py +++ b/kfet/migrations/0021_auto_20160808_0506.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0020_auto_20160808_0450'), - ] + dependencies = [("kfet", "0020_auto_20160808_0450")] operations = [ migrations.AlterField( - model_name='accountnegative', - name='start', + model_name="accountnegative", + name="start", field=models.DateTimeField(default=None, blank=True, null=True), - ), + ) ] diff --git a/kfet/migrations/0022_auto_20160808_0512.py b/kfet/migrations/0022_auto_20160808_0512.py index ba5de03e..3701e856 100644 --- a/kfet/migrations/0022_auto_20160808_0512.py +++ b/kfet/migrations/0022_auto_20160808_0512.py @@ -1,24 +1,26 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0021_auto_20160808_0506'), - ] + dependencies = [("kfet", "0021_auto_20160808_0506")] operations = [ migrations.AlterField( - model_name='accountnegative', - name='authorized_overdraft', - field=models.DecimalField(blank=True, decimal_places=2, null=True, default=None, max_digits=6), + model_name="accountnegative", + name="authorized_overdraft", + field=models.DecimalField( + blank=True, decimal_places=2, null=True, default=None, max_digits=6 + ), ), migrations.AlterField( - model_name='accountnegative', - name='balance_offset', - field=models.DecimalField(blank=True, decimal_places=2, null=True, default=None, max_digits=6), + model_name="accountnegative", + name="balance_offset", + field=models.DecimalField( + blank=True, decimal_places=2, null=True, default=None, max_digits=6 + ), ), ] diff --git a/kfet/migrations/0023_auto_20160808_0535.py b/kfet/migrations/0023_auto_20160808_0535.py index 7e4d7051..03a8d1c3 100644 --- a/kfet/migrations/0023_auto_20160808_0535.py +++ b/kfet/migrations/0023_auto_20160808_0535.py @@ -1,24 +1,22 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0022_auto_20160808_0512'), - ] + dependencies = [("kfet", "0022_auto_20160808_0512")] operations = [ migrations.RenameField( - model_name='accountnegative', - old_name='authorized_overdraft', - new_name='authz_overdraft_amount', + model_name="accountnegative", + old_name="authorized_overdraft", + new_name="authz_overdraft_amount", ), migrations.AddField( - model_name='accountnegative', - name='authz_overdraft_until', + model_name="accountnegative", + name="authz_overdraft_until", field=models.DateTimeField(null=True, default=None, blank=True), ), ] diff --git a/kfet/migrations/0024_settings_value_duration.py b/kfet/migrations/0024_settings_value_duration.py index 1f90b1c9..56b22812 100644 --- a/kfet/migrations/0024_settings_value_duration.py +++ b/kfet/migrations/0024_settings_value_duration.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0023_auto_20160808_0535'), - ] + dependencies = [("kfet", "0023_auto_20160808_0535")] operations = [ migrations.AddField( - model_name='settings', - name='value_duration', + model_name="settings", + name="value_duration", field=models.DurationField(null=True, default=None, blank=True), - ), + ) ] diff --git a/kfet/migrations/0025_auto_20160809_0750.py b/kfet/migrations/0025_auto_20160809_0750.py index 8ba90e2d..51f3b5a3 100644 --- a/kfet/migrations/0025_auto_20160809_0750.py +++ b/kfet/migrations/0025_auto_20160809_0750.py @@ -1,18 +1,28 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0024_settings_value_duration'), - ] + dependencies = [("kfet", "0024_settings_value_duration")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes')), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0026_auto_20160809_0810.py b/kfet/migrations/0026_auto_20160809_0810.py index 7e96c937..942eaf26 100644 --- a/kfet/migrations/0026_auto_20160809_0810.py +++ b/kfet/migrations/0026_auto_20160809_0810.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0025_auto_20160809_0750'), - ] + dependencies = [("kfet", "0025_auto_20160809_0750")] operations = [ migrations.AlterField( - model_name='settings', - name='name', + model_name="settings", + name="name", field=models.CharField(db_index=True, max_length=45, unique=True), - ), + ) ] diff --git a/kfet/migrations/0027_auto_20160811_0648.py b/kfet/migrations/0027_auto_20160811_0648.py index 25bd826b..a0084e8a 100644 --- a/kfet/migrations/0027_auto_20160811_0648.py +++ b/kfet/migrations/0027_auto_20160811_0648.py @@ -1,39 +1,51 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0026_auto_20160809_0810'), - ] + dependencies = [("kfet", "0026_auto_20160809_0810")] operations = [ migrations.CreateModel( - name='CheckoutStatement', + name="CheckoutStatement", fields=[ - ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), - ('balance_old', models.DecimalField(decimal_places=2, max_digits=6)), - ('balance_new', models.DecimalField(decimal_places=2, max_digits=6)), - ('amount_taken', models.DecimalField(decimal_places=2, max_digits=6)), - ('amount_error', models.DecimalField(decimal_places=2, max_digits=6)), - ('at', models.DateTimeField(auto_now_add=True)), - ('by', models.ForeignKey(to='kfet.Account', on_delete=django.db.models.deletion.PROTECT, related_name='+')), - ('checkout', models.ForeignKey(to='kfet.Checkout', on_delete=django.db.models.deletion.PROTECT, related_name='statements')), + ( + "id", + models.AutoField( + verbose_name="ID", + primary_key=True, + serialize=False, + auto_created=True, + ), + ), + ("balance_old", models.DecimalField(decimal_places=2, max_digits=6)), + ("balance_new", models.DecimalField(decimal_places=2, max_digits=6)), + ("amount_taken", models.DecimalField(decimal_places=2, max_digits=6)), + ("amount_error", models.DecimalField(decimal_places=2, max_digits=6)), + ("at", models.DateTimeField(auto_now_add=True)), + ( + "by", + models.ForeignKey( + to="kfet.Account", + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + ), + ), + ( + "checkout", + models.ForeignKey( + to="kfet.Checkout", + on_delete=django.db.models.deletion.PROTECT, + related_name="statements", + ), + ), ], ), - migrations.RemoveField( - model_name='statement', - name='by', - ), - migrations.RemoveField( - model_name='statement', - name='checkout', - ), - migrations.DeleteModel( - name='Statement', - ), + migrations.RemoveField(model_name="statement", name="by"), + migrations.RemoveField(model_name="statement", name="checkout"), + migrations.DeleteModel(name="Statement"), ] diff --git a/kfet/migrations/0028_auto_20160820_0146.py b/kfet/migrations/0028_auto_20160820_0146.py index 5f8fa377..a1b046cf 100644 --- a/kfet/migrations/0028_auto_20160820_0146.py +++ b/kfet/migrations/0028_auto_20160820_0146.py @@ -1,30 +1,40 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0027_auto_20160811_0648'), - ] + dependencies = [("kfet", "0027_auto_20160811_0648")] operations = [ migrations.AlterField( - model_name='operation', - name='group', - field=models.ForeignKey(to='kfet.OperationGroup', on_delete=django.db.models.deletion.PROTECT, related_name='opes'), + model_name="operation", + name="group", + field=models.ForeignKey( + to="kfet.OperationGroup", + on_delete=django.db.models.deletion.PROTECT, + related_name="opes", + ), ), migrations.AlterField( - model_name='operationgroup', - name='checkout', - field=models.ForeignKey(to='kfet.Checkout', on_delete=django.db.models.deletion.PROTECT, related_name='opesgroup'), + model_name="operationgroup", + name="checkout", + field=models.ForeignKey( + to="kfet.Checkout", + on_delete=django.db.models.deletion.PROTECT, + related_name="opesgroup", + ), ), migrations.AlterField( - model_name='operationgroup', - name='on_acc', - field=models.ForeignKey(to='kfet.Account', on_delete=django.db.models.deletion.PROTECT, related_name='opesgroup'), + model_name="operationgroup", + name="on_acc", + field=models.ForeignKey( + to="kfet.Account", + on_delete=django.db.models.deletion.PROTECT, + related_name="opesgroup", + ), ), ] diff --git a/kfet/migrations/0029_genericteamtoken.py b/kfet/migrations/0029_genericteamtoken.py index ba13674c..c5a81f05 100644 --- a/kfet/migrations/0029_genericteamtoken.py +++ b/kfet/migrations/0029_genericteamtoken.py @@ -1,21 +1,27 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0028_auto_20160820_0146'), - ] + dependencies = [("kfet", "0028_auto_20160820_0146")] operations = [ migrations.CreateModel( - name='GenericTeamToken', + name="GenericTeamToken", fields=[ - ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), - ('token', models.CharField(unique=True, max_length=50)), + ( + "id", + models.AutoField( + serialize=False, + primary_key=True, + auto_created=True, + verbose_name="ID", + ), + ), + ("token", models.CharField(unique=True, max_length=50)), ], - ), + ) ] diff --git a/kfet/migrations/0030_auto_20160821_0029.py b/kfet/migrations/0030_auto_20160821_0029.py index ed54efa9..1858522d 100644 --- a/kfet/migrations/0030_auto_20160821_0029.py +++ b/kfet/migrations/0030_auto_20160821_0029.py @@ -1,18 +1,29 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0029_genericteamtoken'), - ] + dependencies = [("kfet", "0029_genericteamtoken")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt')), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0031_auto_20160822_0523.py b/kfet/migrations/0031_auto_20160822_0523.py index e7ca4d6f..23aec1be 100644 --- a/kfet/migrations/0031_auto_20160822_0523.py +++ b/kfet/migrations/0031_auto_20160822_0523.py @@ -1,18 +1,30 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0030_auto_20160821_0029'), - ] + dependencies = [("kfet", "0030_auto_20160821_0029")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations')), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0032_auto_20160822_2350.py b/kfet/migrations/0032_auto_20160822_2350.py index 142fb29d..7cba0da3 100644 --- a/kfet/migrations/0032_auto_20160822_2350.py +++ b/kfet/migrations/0032_auto_20160822_2350.py @@ -1,94 +1,92 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0031_auto_20160822_0523'), - ] + dependencies = [("kfet", "0031_auto_20160822_0523")] operations = [ migrations.AddField( - model_name='checkoutstatement', - name='taken_001', + model_name="checkoutstatement", + name="taken_001", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_002', + model_name="checkoutstatement", + name="taken_002", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_005', + model_name="checkoutstatement", + name="taken_005", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_01', + model_name="checkoutstatement", + name="taken_01", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_02', + model_name="checkoutstatement", + name="taken_02", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_05', + model_name="checkoutstatement", + name="taken_05", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_1', + model_name="checkoutstatement", + name="taken_1", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_10', + model_name="checkoutstatement", + name="taken_10", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_100', + model_name="checkoutstatement", + name="taken_100", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_2', + model_name="checkoutstatement", + name="taken_2", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_20', + model_name="checkoutstatement", + name="taken_20", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_200', + model_name="checkoutstatement", + name="taken_200", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_5', + model_name="checkoutstatement", + name="taken_5", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_50', + model_name="checkoutstatement", + name="taken_50", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_500', + model_name="checkoutstatement", + name="taken_500", field=models.PositiveSmallIntegerField(default=0), ), migrations.AddField( - model_name='checkoutstatement', - name='taken_cheque', + model_name="checkoutstatement", + name="taken_cheque", field=models.PositiveSmallIntegerField(default=0), ), ] diff --git a/kfet/migrations/0033_checkoutstatement_not_count.py b/kfet/migrations/0033_checkoutstatement_not_count.py index 50c58256..dd445406 100644 --- a/kfet/migrations/0033_checkoutstatement_not_count.py +++ b/kfet/migrations/0033_checkoutstatement_not_count.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0032_auto_20160822_2350'), - ] + dependencies = [("kfet", "0032_auto_20160822_2350")] operations = [ migrations.AddField( - model_name='checkoutstatement', - name='not_count', + model_name="checkoutstatement", + name="not_count", field=models.BooleanField(default=False), - ), + ) ] diff --git a/kfet/migrations/0034_auto_20160823_0206.py b/kfet/migrations/0034_auto_20160823_0206.py index 90d0965c..1b28e289 100644 --- a/kfet/migrations/0034_auto_20160823_0206.py +++ b/kfet/migrations/0034_auto_20160823_0206.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0033_checkoutstatement_not_count'), - ] + dependencies = [("kfet", "0033_checkoutstatement_not_count")] operations = [ migrations.AlterField( - model_name='checkoutstatement', - name='taken_cheque', + model_name="checkoutstatement", + name="taken_cheque", field=models.DecimalField(max_digits=6, decimal_places=2, default=0), - ), + ) ] diff --git a/kfet/migrations/0035_auto_20160823_1505.py b/kfet/migrations/0035_auto_20160823_1505.py index 5fd73ae8..e2a98ca7 100644 --- a/kfet/migrations/0035_auto_20160823_1505.py +++ b/kfet/migrations/0035_auto_20160823_1505.py @@ -1,18 +1,34 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0034_auto_20160823_0206'), - ] + dependencies = [("kfet", "0034_auto_20160823_0206")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'))}, - ), + name="globalpermissions", + options={ + "managed": False, + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ), + }, + ) ] diff --git a/kfet/migrations/0036_auto_20160823_1910.py b/kfet/migrations/0036_auto_20160823_1910.py index 2d29fd7a..09726e37 100644 --- a/kfet/migrations/0036_auto_20160823_1910.py +++ b/kfet/migrations/0036_auto_20160823_1910.py @@ -1,18 +1,35 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0035_auto_20160823_1505'), - ] + dependencies = [("kfet", "0035_auto_20160823_1505")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'))}, - ), + name="globalpermissions", + options={ + "managed": False, + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ("view_negs", "Voir la liste des négatifs"), + ), + }, + ) ] diff --git a/kfet/migrations/0037_auto_20160826_2333.py b/kfet/migrations/0037_auto_20160826_2333.py index 9f937b60..6ebc921d 100644 --- a/kfet/migrations/0037_auto_20160826_2333.py +++ b/kfet/migrations/0037_auto_20160826_2333.py @@ -1,39 +1,54 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0036_auto_20160823_1910'), - ] + dependencies = [("kfet", "0036_auto_20160823_1910")] operations = [ migrations.AlterField( - model_name='supplierarticle', - name='TVA', - field=models.DecimalField(null=True, max_digits=4, decimal_places=2, default=None, blank=True), + model_name="supplierarticle", + name="TVA", + field=models.DecimalField( + null=True, max_digits=4, decimal_places=2, default=None, blank=True + ), ), migrations.AlterField( - model_name='supplierarticle', - name='box_capacity', + model_name="supplierarticle", + name="box_capacity", field=models.PositiveSmallIntegerField(null=True, default=None, blank=True), ), migrations.AlterField( - model_name='supplierarticle', - name='box_type', - field=models.CharField(null=True, max_length=7, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')], default=None, blank=True), + model_name="supplierarticle", + name="box_type", + field=models.CharField( + null=True, + max_length=7, + choices=[ + ("caisse", "Caisse"), + ("carton", "Carton"), + ("palette", "Palette"), + ("fût", "Fût"), + ], + default=None, + blank=True, + ), ), migrations.AlterField( - model_name='supplierarticle', - name='price_HT', - field=models.DecimalField(null=True, max_digits=7, decimal_places=4, default=None, blank=True), + model_name="supplierarticle", + name="price_HT", + field=models.DecimalField( + null=True, max_digits=7, decimal_places=4, default=None, blank=True + ), ), migrations.AlterField( - model_name='supplierarticle', - name='rights', - field=models.DecimalField(null=True, max_digits=7, decimal_places=4, default=None, blank=True), + model_name="supplierarticle", + name="rights", + field=models.DecimalField( + null=True, max_digits=7, decimal_places=4, default=None, blank=True + ), ), ] diff --git a/kfet/migrations/0038_auto_20160828_0402.py b/kfet/migrations/0038_auto_20160828_0402.py index 215591b7..be8d847e 100644 --- a/kfet/migrations/0038_auto_20160828_0402.py +++ b/kfet/migrations/0038_auto_20160828_0402.py @@ -1,36 +1,36 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0037_auto_20160826_2333'), - ] + dependencies = [("kfet", "0037_auto_20160826_2333")] operations = [ - migrations.AlterModelOptions( - name='inventory', - options={'ordering': ['-at']}, - ), - migrations.RemoveField( - model_name='supplierarticle', - name='box_capacity', - ), - migrations.RemoveField( - model_name='supplierarticle', - name='box_type', - ), + migrations.AlterModelOptions(name="inventory", options={"ordering": ["-at"]}), + migrations.RemoveField(model_name="supplierarticle", name="box_capacity"), + migrations.RemoveField(model_name="supplierarticle", name="box_type"), migrations.AddField( - model_name='article', - name='box_capacity', + model_name="article", + name="box_capacity", field=models.PositiveSmallIntegerField(blank=True, null=True, default=None), ), migrations.AddField( - model_name='article', - name='box_type', - field=models.CharField(max_length=7, blank=True, null=True, default=None, choices=[('caisse', 'Caisse'), ('carton', 'Carton'), ('palette', 'Palette'), ('fût', 'Fût')]), + model_name="article", + name="box_type", + field=models.CharField( + max_length=7, + blank=True, + null=True, + default=None, + choices=[ + ("caisse", "Caisse"), + ("carton", "Carton"), + ("palette", "Palette"), + ("fût", "Fût"), + ], + ), ), ] diff --git a/kfet/migrations/0039_auto_20160828_0430.py b/kfet/migrations/0039_auto_20160828_0430.py index 271fda68..2611d578 100644 --- a/kfet/migrations/0039_auto_20160828_0430.py +++ b/kfet/migrations/0039_auto_20160828_0430.py @@ -1,24 +1,22 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0038_auto_20160828_0402'), - ] + dependencies = [("kfet", "0038_auto_20160828_0402")] operations = [ migrations.AlterField( - model_name='order', - name='amount', + model_name="order", + name="amount", field=models.DecimalField(default=0, decimal_places=2, max_digits=6), ), migrations.AlterField( - model_name='orderarticle', - name='quantity_received', + model_name="orderarticle", + name="quantity_received", field=models.IntegerField(default=0), ), ] diff --git a/kfet/migrations/0040_auto_20160829_2035.py b/kfet/migrations/0040_auto_20160829_2035.py index 78b577a8..16bd2b36 100644 --- a/kfet/migrations/0040_auto_20160829_2035.py +++ b/kfet/migrations/0040_auto_20160829_2035.py @@ -1,31 +1,41 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations import datetime + +from django.db import migrations, models from django.utils.timezone import utc class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0039_auto_20160828_0430'), - ] + dependencies = [("kfet", "0039_auto_20160828_0430")] operations = [ - migrations.AlterModelOptions( - name='order', - options={'ordering': ['-at']}, - ), + migrations.AlterModelOptions(name="order", options={"ordering": ["-at"]}), migrations.AddField( - model_name='supplierarticle', - name='at', - field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 8, 29, 18, 35, 3, 419033, tzinfo=utc)), + model_name="supplierarticle", + name="at", + field=models.DateTimeField( + auto_now_add=True, + default=datetime.datetime(2016, 8, 29, 18, 35, 3, 419033, tzinfo=utc), + ), preserve_default=False, ), migrations.AlterField( - model_name='article', - name='box_type', - field=models.CharField(choices=[('caisse', 'caisse'), ('carton', 'carton'), ('palette', 'palette'), ('fût', 'fût')], null=True, max_length=7, blank=True, default=None), + model_name="article", + name="box_type", + field=models.CharField( + choices=[ + ("caisse", "caisse"), + ("carton", "carton"), + ("palette", "palette"), + ("fût", "fût"), + ], + null=True, + max_length=7, + blank=True, + default=None, + ), ), ] diff --git a/kfet/migrations/0041_auto_20160830_1502.py b/kfet/migrations/0041_auto_20160830_1502.py index 40c83907..488d33ff 100644 --- a/kfet/migrations/0041_auto_20160830_1502.py +++ b/kfet/migrations/0041_auto_20160830_1502.py @@ -1,18 +1,39 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0040_auto_20160829_2035'), - ] + dependencies = [("kfet", "0040_auto_20160829_2035")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'), ('order_to_inventory', "Générer un inventaire à partir d'une commande")), 'managed': False}, - ), + name="globalpermissions", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ("view_negs", "Voir la liste des négatifs"), + ( + "order_to_inventory", + "Générer un inventaire à partir d'une commande", + ), + ), + "managed": False, + }, + ) ] diff --git a/kfet/migrations/0042_auto_20160831_0126.py b/kfet/migrations/0042_auto_20160831_0126.py index 3306c401..70adbad5 100644 --- a/kfet/migrations/0042_auto_20160831_0126.py +++ b/kfet/migrations/0042_auto_20160831_0126.py @@ -1,18 +1,40 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0041_auto_20160830_1502'), - ] + dependencies = [("kfet", "0041_auto_20160830_1502")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des négatifs'), ('order_to_inventory', "Générer un inventaire à partir d'une commande"), ('edit_balance_account', "Modifier la balance d'un compte"))}, - ), + name="globalpermissions", + options={ + "managed": False, + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ("view_negs", "Voir la liste des négatifs"), + ( + "order_to_inventory", + "Générer un inventaire à partir d'une commande", + ), + ("edit_balance_account", "Modifier la balance d'un compte"), + ), + }, + ) ] diff --git a/kfet/migrations/0043_auto_20160901_0046.py b/kfet/migrations/0043_auto_20160901_0046.py index 2d9bf12a..b5132335 100644 --- a/kfet/migrations/0043_auto_20160901_0046.py +++ b/kfet/migrations/0043_auto_20160901_0046.py @@ -1,19 +1,60 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0042_auto_20160831_0126'), - ] + dependencies = [("kfet", "0042_auto_20160831_0126")] operations = [ migrations.AlterField( - model_name='account', - name='promo', - field=models.IntegerField(blank=True, default=2016, null=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016)]), - ), + model_name="account", + name="promo", + field=models.IntegerField( + blank=True, + default=2016, + null=True, + choices=[ + (1980, 1980), + (1981, 1981), + (1982, 1982), + (1983, 1983), + (1984, 1984), + (1985, 1985), + (1986, 1986), + (1987, 1987), + (1988, 1988), + (1989, 1989), + (1990, 1990), + (1991, 1991), + (1992, 1992), + (1993, 1993), + (1994, 1994), + (1995, 1995), + (1996, 1996), + (1997, 1997), + (1998, 1998), + (1999, 1999), + (2000, 2000), + (2001, 2001), + (2002, 2002), + (2003, 2003), + (2004, 2004), + (2005, 2005), + (2006, 2006), + (2007, 2007), + (2008, 2008), + (2009, 2009), + (2010, 2010), + (2011, 2011), + (2012, 2012), + (2013, 2013), + (2014, 2014), + (2015, 2015), + (2016, 2016), + ], + ), + ) ] diff --git a/kfet/migrations/0044_auto_20160901_1614.py b/kfet/migrations/0044_auto_20160901_1614.py index 2a91206a..f81ae828 100644 --- a/kfet/migrations/0044_auto_20160901_1614.py +++ b/kfet/migrations/0044_auto_20160901_1614.py @@ -1,18 +1,44 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0043_auto_20160901_0046'), - ] + dependencies = [("kfet", "0043_auto_20160901_0046")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en n\xe9gatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non r\xe9centes'), ('manage_perms', 'G\xe9rer les permissions K-F\xeat'), ('manage_addcosts', 'G\xe9rer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des n\xe9gatifs'), ('order_to_inventory', "G\xe9n\xe9rer un inventaire \xe0 partir d'une commande"), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'\xe9quipe"))}, - ), + name="globalpermissions", + options={ + "managed": False, + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en n\xe9gatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non r\xe9centes"), + ("manage_perms", "G\xe9rer les permissions K-F\xeat"), + ("manage_addcosts", "G\xe9rer les majorations"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ("view_negs", "Voir la liste des n\xe9gatifs"), + ( + "order_to_inventory", + "G\xe9n\xe9rer un inventaire \xe0 partir d'une commande", + ), + ("edit_balance_account", "Modifier la balance d'un compte"), + ( + "change_account_password", + "Modifier le mot de passe d'une personne de l'\xe9quipe", + ), + ), + }, + ) ] diff --git a/kfet/migrations/0045_auto_20160905_0705.py b/kfet/migrations/0045_auto_20160905_0705.py index 0673fdca..0f98c56a 100644 --- a/kfet/migrations/0045_auto_20160905_0705.py +++ b/kfet/migrations/0045_auto_20160905_0705.py @@ -1,23 +1,61 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0044_auto_20160901_1614'), - ] + dependencies = [("kfet", "0044_auto_20160901_1614")] operations = [ migrations.AlterModelOptions( - name='globalpermissions', - options={'managed': False, 'permissions': (('is_team', 'Is part of the team'), ('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en n\xe9gatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non r\xe9centes'), ('manage_perms', 'G\xe9rer les permissions K-F\xeat'), ('manage_addcosts', 'G\xe9rer les majorations'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'), ('view_negs', 'Voir la liste des n\xe9gatifs'), ('order_to_inventory', "G\xe9n\xe9rer un inventaire \xe0 partir d'une commande"), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'\xe9quipe"), ('special_add_account', 'Cr\xe9er un compte avec une balance initiale'))}, + name="globalpermissions", + options={ + "managed": False, + "permissions": ( + ("is_team", "Is part of the team"), + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en n\xe9gatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non r\xe9centes"), + ("manage_perms", "G\xe9rer les permissions K-F\xeat"), + ("manage_addcosts", "G\xe9rer les majorations"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ("view_negs", "Voir la liste des n\xe9gatifs"), + ( + "order_to_inventory", + "G\xe9n\xe9rer un inventaire \xe0 partir d'une commande", + ), + ("edit_balance_account", "Modifier la balance d'un compte"), + ( + "change_account_password", + "Modifier le mot de passe d'une personne de l'\xe9quipe", + ), + ( + "special_add_account", + "Cr\xe9er un compte avec une balance initiale", + ), + ), + }, ), migrations.AlterField( - model_name='operation', - name='type', - field=models.CharField(max_length=8, choices=[('purchase', 'Achat'), ('deposit', 'Charge'), ('withdraw', 'Retrait'), ('initial', 'Initial')]), + model_name="operation", + name="type", + field=models.CharField( + max_length=8, + choices=[ + ("purchase", "Achat"), + ("deposit", "Charge"), + ("withdraw", "Retrait"), + ("initial", "Initial"), + ], + ), ), ] diff --git a/kfet/migrations/0046_account_created_at.py b/kfet/migrations/0046_account_created_at.py index a624c0fb..a0274432 100644 --- a/kfet/migrations/0046_account_created_at.py +++ b/kfet/migrations/0046_account_created_at.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0045_auto_20160905_0705'), - ] + dependencies = [("kfet", "0045_auto_20160905_0705")] operations = [ migrations.AddField( - model_name='account', - name='created_at', + model_name="account", + name="created_at", field=models.DateTimeField(auto_now_add=True, null=True), - ), + ) ] diff --git a/kfet/migrations/0047_auto_20170104_1528.py b/kfet/migrations/0047_auto_20170104_1528.py index d59447af..d391e1f4 100644 --- a/kfet/migrations/0047_auto_20170104_1528.py +++ b/kfet/migrations/0047_auto_20170104_1528.py @@ -6,14 +6,56 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0046_account_created_at'), - ] + dependencies = [("kfet", "0046_account_created_at")] operations = [ migrations.AlterField( - model_name='account', - name='promo', - field=models.IntegerField(blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017)], default=2016, null=True), - ), + model_name="account", + name="promo", + field=models.IntegerField( + blank=True, + choices=[ + (1980, 1980), + (1981, 1981), + (1982, 1982), + (1983, 1983), + (1984, 1984), + (1985, 1985), + (1986, 1986), + (1987, 1987), + (1988, 1988), + (1989, 1989), + (1990, 1990), + (1991, 1991), + (1992, 1992), + (1993, 1993), + (1994, 1994), + (1995, 1995), + (1996, 1996), + (1997, 1997), + (1998, 1998), + (1999, 1999), + (2000, 2000), + (2001, 2001), + (2002, 2002), + (2003, 2003), + (2004, 2004), + (2005, 2005), + (2006, 2006), + (2007, 2007), + (2008, 2008), + (2009, 2009), + (2010, 2010), + (2011, 2011), + (2012, 2012), + (2013, 2013), + (2014, 2014), + (2015, 2015), + (2016, 2016), + (2017, 2017), + ], + default=2016, + null=True, + ), + ) ] diff --git a/kfet/migrations/0048_article_hidden.py b/kfet/migrations/0048_article_hidden.py index 63869f77..d4d89022 100644 --- a/kfet/migrations/0048_article_hidden.py +++ b/kfet/migrations/0048_article_hidden.py @@ -6,14 +6,15 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0047_auto_20170104_1528'), - ] + dependencies = [("kfet", "0047_auto_20170104_1528")] operations = [ migrations.AddField( - model_name='article', - name='hidden', - field=models.BooleanField(help_text='Si oui, ne sera pas affiché au public ; par exemple sur la carte.', default=False), - ), + model_name="article", + name="hidden", + field=models.BooleanField( + help_text="Si oui, ne sera pas affiché au public ; par exemple sur la carte.", + default=False, + ), + ) ] diff --git a/kfet/migrations/0048_default_datetime.py b/kfet/migrations/0048_default_datetime.py index c9bacf1e..d5408c59 100644 --- a/kfet/migrations/0048_default_datetime.py +++ b/kfet/migrations/0048_default_datetime.py @@ -1,25 +1,23 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models import django.utils.timezone +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0047_auto_20170104_1528'), - ] + dependencies = [("kfet", "0047_auto_20170104_1528")] operations = [ migrations.AlterField( - model_name='operationgroup', - name='at', + model_name="operationgroup", + name="at", field=models.DateTimeField(default=django.utils.timezone.now), ), migrations.AlterField( - model_name='transfergroup', - name='at', + model_name="transfergroup", + name="at", field=models.DateTimeField(default=django.utils.timezone.now), ), ] diff --git a/kfet/migrations/0049_merge.py b/kfet/migrations/0049_merge.py index 0ce9a525..e9bcb47a 100644 --- a/kfet/migrations/0049_merge.py +++ b/kfet/migrations/0049_merge.py @@ -6,10 +6,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0048_article_hidden'), - ('kfet', '0048_default_datetime'), - ] + dependencies = [("kfet", "0048_article_hidden"), ("kfet", "0048_default_datetime")] - operations = [ - ] + operations = [] diff --git a/kfet/migrations/0050_remove_checkout.py b/kfet/migrations/0050_remove_checkout.py index b712c2d8..8cfd370a 100644 --- a/kfet/migrations/0050_remove_checkout.py +++ b/kfet/migrations/0050_remove_checkout.py @@ -7,32 +7,36 @@ from django.db import migrations, models def adapt_operation_types(apps, schema_editor): Operation = apps.get_model("kfet", "Operation") Operation.objects.filter( - is_checkout=False, - type__in=['withdraw', 'deposit']).update(type='edit') + is_checkout=False, type__in=["withdraw", "deposit"] + ).update(type="edit") def revert_operation_types(apps, schema_editor): Operation = apps.get_model("kfet", "Operation") - edits = Operation.objects.filter(type='edit') - edits.filter(amount__gt=0).update(type='deposit') - edits.filter(amount__lte=0).update(type='withdraw') + edits = Operation.objects.filter(type="edit") + edits.filter(amount__gt=0).update(type="deposit") + edits.filter(amount__lte=0).update(type="withdraw") class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0049_merge'), - ] + dependencies = [("kfet", "0049_merge")] operations = [ migrations.AlterField( - model_name='operation', - name='type', - field=models.CharField(choices=[('purchase', 'Achat'), ('deposit', 'Charge'), ('withdraw', 'Retrait'), ('initial', 'Initial'), ('edit', 'Édition')], max_length=8), + model_name="operation", + name="type", + field=models.CharField( + choices=[ + ("purchase", "Achat"), + ("deposit", "Charge"), + ("withdraw", "Retrait"), + ("initial", "Initial"), + ("edit", "Édition"), + ], + max_length=8, + ), ), migrations.RunPython(adapt_operation_types, revert_operation_types), - migrations.RemoveField( - model_name='operation', - name='is_checkout', - ), + migrations.RemoveField(model_name="operation", name="is_checkout"), ] diff --git a/kfet/migrations/0051_verbose_names.py b/kfet/migrations/0051_verbose_names.py index ae407fac..9892af71 100644 --- a/kfet/migrations/0051_verbose_names.py +++ b/kfet/migrations/0051_verbose_names.py @@ -1,210 +1,303 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0050_remove_checkout'), - ] + dependencies = [("kfet", "0050_remove_checkout")] operations = [ migrations.AlterField( - model_name='account', - name='is_frozen', - field=models.BooleanField(default=False, verbose_name='est gelé'), + model_name="account", + name="is_frozen", + field=models.BooleanField(default=False, verbose_name="est gelé"), ), migrations.AlterField( - model_name='account', - name='nickname', - field=models.CharField(default='', max_length=255, verbose_name='surnom(s)', blank=True), + model_name="account", + name="nickname", + field=models.CharField( + default="", max_length=255, verbose_name="surnom(s)", blank=True + ), ), migrations.AlterField( - model_name='accountnegative', - name='authz_overdraft_amount', - field=models.DecimalField(max_digits=6, blank=True, default=None, null=True, verbose_name='négatif autorisé', decimal_places=2), + model_name="accountnegative", + name="authz_overdraft_amount", + field=models.DecimalField( + max_digits=6, + blank=True, + default=None, + null=True, + verbose_name="négatif autorisé", + decimal_places=2, + ), ), migrations.AlterField( - model_name='accountnegative', - name='authz_overdraft_until', - field=models.DateTimeField(default=None, null=True, verbose_name='expiration du négatif', blank=True), + model_name="accountnegative", + name="authz_overdraft_until", + field=models.DateTimeField( + default=None, + null=True, + verbose_name="expiration du négatif", + blank=True, + ), ), migrations.AlterField( - model_name='accountnegative', - name='balance_offset', - field=models.DecimalField(blank=True, max_digits=6, help_text="Montant non compris dans l'autorisation de négatif", default=None, null=True, verbose_name='décalage de balance', decimal_places=2), + model_name="accountnegative", + name="balance_offset", + field=models.DecimalField( + blank=True, + max_digits=6, + help_text="Montant non compris dans l'autorisation de négatif", + default=None, + null=True, + verbose_name="décalage de balance", + decimal_places=2, + ), ), migrations.AlterField( - model_name='accountnegative', - name='comment', - field=models.CharField(blank=True, max_length=255, verbose_name='commentaire'), + model_name="accountnegative", + name="comment", + field=models.CharField( + blank=True, max_length=255, verbose_name="commentaire" + ), ), migrations.AlterField( - model_name='article', - name='box_capacity', - field=models.PositiveSmallIntegerField(default=None, null=True, verbose_name='capacité du contenant', blank=True), + model_name="article", + name="box_capacity", + field=models.PositiveSmallIntegerField( + default=None, + null=True, + verbose_name="capacité du contenant", + blank=True, + ), ), migrations.AlterField( - model_name='article', - name='box_type', - field=models.CharField(blank=True, max_length=7, choices=[('caisse', 'caisse'), ('carton', 'carton'), ('palette', 'palette'), ('fût', 'fût')], default=None, null=True, verbose_name='type de contenant'), + model_name="article", + name="box_type", + field=models.CharField( + blank=True, + max_length=7, + choices=[ + ("caisse", "caisse"), + ("carton", "carton"), + ("palette", "palette"), + ("fût", "fût"), + ], + default=None, + null=True, + verbose_name="type de contenant", + ), ), migrations.AlterField( - model_name='article', - name='category', - field=models.ForeignKey(related_name='articles', to='kfet.ArticleCategory', on_delete=django.db.models.deletion.PROTECT, verbose_name='catégorie'), + model_name="article", + name="category", + field=models.ForeignKey( + related_name="articles", + to="kfet.ArticleCategory", + on_delete=django.db.models.deletion.PROTECT, + verbose_name="catégorie", + ), ), migrations.AlterField( - model_name='article', - name='hidden', - field=models.BooleanField(default=False, verbose_name='caché', help_text='Si oui, ne sera pas affiché au public ; par exemple sur la carte.'), + model_name="article", + name="hidden", + field=models.BooleanField( + default=False, + verbose_name="caché", + help_text="Si oui, ne sera pas affiché au public ; par exemple sur la carte.", + ), ), migrations.AlterField( - model_name='article', - name='is_sold', - field=models.BooleanField(default=True, verbose_name='en vente'), + model_name="article", + name="is_sold", + field=models.BooleanField(default=True, verbose_name="en vente"), ), migrations.AlterField( - model_name='article', - name='name', - field=models.CharField(max_length=45, verbose_name='nom'), + model_name="article", + name="name", + field=models.CharField(max_length=45, verbose_name="nom"), ), migrations.AlterField( - model_name='article', - name='price', - field=models.DecimalField(default=0, verbose_name='prix', decimal_places=2, max_digits=6), + model_name="article", + name="price", + field=models.DecimalField( + default=0, verbose_name="prix", decimal_places=2, max_digits=6 + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='amount_error', - field=models.DecimalField(max_digits=6, verbose_name="montant de l'erreur", decimal_places=2), + model_name="checkoutstatement", + name="amount_error", + field=models.DecimalField( + max_digits=6, verbose_name="montant de l'erreur", decimal_places=2 + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='amount_taken', - field=models.DecimalField(max_digits=6, verbose_name='montant pris', decimal_places=2), + model_name="checkoutstatement", + name="amount_taken", + field=models.DecimalField( + max_digits=6, verbose_name="montant pris", decimal_places=2 + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='balance_new', - field=models.DecimalField(max_digits=6, verbose_name='nouvelle balance', decimal_places=2), + model_name="checkoutstatement", + name="balance_new", + field=models.DecimalField( + max_digits=6, verbose_name="nouvelle balance", decimal_places=2 + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='balance_old', - field=models.DecimalField(max_digits=6, verbose_name='ancienne balance', decimal_places=2), + model_name="checkoutstatement", + name="balance_old", + field=models.DecimalField( + max_digits=6, verbose_name="ancienne balance", decimal_places=2 + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='not_count', - field=models.BooleanField(default=False, verbose_name='caisse non comptée'), + model_name="checkoutstatement", + name="not_count", + field=models.BooleanField(default=False, verbose_name="caisse non comptée"), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_001', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 1¢'), + model_name="checkoutstatement", + name="taken_001", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 1¢" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_002', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 2¢'), + model_name="checkoutstatement", + name="taken_002", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 2¢" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_005', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 5¢'), + model_name="checkoutstatement", + name="taken_005", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 5¢" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_01', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 10¢'), + model_name="checkoutstatement", + name="taken_01", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 10¢" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_02', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 20¢'), + model_name="checkoutstatement", + name="taken_02", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 20¢" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_05', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 50¢'), + model_name="checkoutstatement", + name="taken_05", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 50¢" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_1', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 1€'), + model_name="checkoutstatement", + name="taken_1", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 1€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_10', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 10€'), + model_name="checkoutstatement", + name="taken_10", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 10€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_100', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 100€'), + model_name="checkoutstatement", + name="taken_100", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 100€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_2', - field=models.PositiveSmallIntegerField(default=0, verbose_name='pièces de 2€'), + model_name="checkoutstatement", + name="taken_2", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="pièces de 2€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_20', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 20€'), + model_name="checkoutstatement", + name="taken_20", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 20€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_200', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 200€'), + model_name="checkoutstatement", + name="taken_200", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 200€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_5', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 5€'), + model_name="checkoutstatement", + name="taken_5", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 5€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_50', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 50€'), + model_name="checkoutstatement", + name="taken_50", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 50€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_500', - field=models.PositiveSmallIntegerField(default=0, verbose_name='billets de 500€'), + model_name="checkoutstatement", + name="taken_500", + field=models.PositiveSmallIntegerField( + default=0, verbose_name="billets de 500€" + ), ), migrations.AlterField( - model_name='checkoutstatement', - name='taken_cheque', - field=models.DecimalField(default=0, verbose_name='montant des chèques', decimal_places=2, max_digits=6), + model_name="checkoutstatement", + name="taken_cheque", + field=models.DecimalField( + default=0, + verbose_name="montant des chèques", + decimal_places=2, + max_digits=6, + ), ), migrations.AlterField( - model_name='supplier', - name='address', - field=models.TextField(verbose_name='adresse'), + model_name="supplier", + name="address", + field=models.TextField(verbose_name="adresse"), ), migrations.AlterField( - model_name='supplier', - name='comment', - field=models.TextField(verbose_name='commentaire'), + model_name="supplier", + name="comment", + field=models.TextField(verbose_name="commentaire"), ), migrations.AlterField( - model_name='supplier', - name='email', - field=models.EmailField(max_length=254, verbose_name='adresse mail'), + model_name="supplier", + name="email", + field=models.EmailField(max_length=254, verbose_name="adresse mail"), ), migrations.AlterField( - model_name='supplier', - name='name', - field=models.CharField(max_length=45, verbose_name='nom'), + model_name="supplier", + name="name", + field=models.CharField(max_length=45, verbose_name="nom"), ), migrations.AlterField( - model_name='supplier', - name='phone', - field=models.CharField(max_length=10, verbose_name='téléphone'), + model_name="supplier", + name="phone", + field=models.CharField(max_length=10, verbose_name="téléphone"), ), ] diff --git a/kfet/migrations/0052_category_addcost.py b/kfet/migrations/0052_category_addcost.py index 83346a1a..8a8da85f 100644 --- a/kfet/migrations/0052_category_addcost.py +++ b/kfet/migrations/0052_category_addcost.py @@ -6,19 +6,21 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0051_verbose_names'), - ] + dependencies = [("kfet", "0051_verbose_names")] operations = [ migrations.AddField( - model_name='articlecategory', - name='has_addcost', - field=models.BooleanField(default=True, help_text="Si oui et qu'une majoration est active, celle-ci sera appliquée aux articles de cette catégorie.", verbose_name='majorée'), + model_name="articlecategory", + name="has_addcost", + field=models.BooleanField( + default=True, + help_text="Si oui et qu'une majoration est active, celle-ci sera appliquée aux articles de cette catégorie.", + verbose_name="majorée", + ), ), migrations.AlterField( - model_name='articlecategory', - name='name', - field=models.CharField(max_length=45, verbose_name='nom'), + model_name="articlecategory", + name="name", + field=models.CharField(max_length=45, verbose_name="nom"), ), ] diff --git a/kfet/migrations/0053_created_at.py b/kfet/migrations/0053_created_at.py index a868de33..6da14568 100644 --- a/kfet/migrations/0053_created_at.py +++ b/kfet/migrations/0053_created_at.py @@ -1,20 +1,18 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models import django.utils.timezone +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0052_category_addcost'), - ] + dependencies = [("kfet", "0052_category_addcost")] operations = [ migrations.AlterField( - model_name='account', - name='created_at', + model_name="account", + name="created_at", field=models.DateTimeField(default=django.utils.timezone.now), - ), + ) ] diff --git a/kfet/migrations/0054_delete_settings.py b/kfet/migrations/0054_delete_settings.py index 80ee1d24..7294c8bf 100644 --- a/kfet/migrations/0054_delete_settings.py +++ b/kfet/migrations/0054_delete_settings.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models from kfet.forms import KFetConfigForm def adapt_settings(apps, schema_editor): - Settings = apps.get_model('kfet', 'Settings') + Settings = apps.get_model("kfet", "Settings") db_alias = schema_editor.connection.alias obj = Settings.objects.using(db_alias) @@ -22,17 +22,17 @@ def adapt_settings(apps, schema_editor): pass try: - subvention = obj.get(name='SUBVENTION_COF').value_decimal - subvention_mult = 1 + subvention/100 - reduction = (1 - 1/subvention_mult) * 100 - cfg['kfet_reduction_cof'] = reduction + subvention = obj.get(name="SUBVENTION_COF").value_decimal + subvention_mult = 1 + subvention / 100 + reduction = (1 - 1 / subvention_mult) * 100 + cfg["kfet_reduction_cof"] = reduction except Settings.DoesNotExist: pass - try_get('kfet_addcost_amount', 'ADDCOST_AMOUNT', 'value_decimal') - try_get('kfet_addcost_for', 'ADDCOST_FOR', 'value_account') - try_get('kfet_overdraft_duration', 'OVERDRAFT_DURATION', 'value_duration') - try_get('kfet_overdraft_amount', 'OVERDRAFT_AMOUNT', 'value_decimal') - try_get('kfet_cancel_duration', 'CANCEL_DURATION', 'value_duration') + try_get("kfet_addcost_amount", "ADDCOST_AMOUNT", "value_decimal") + try_get("kfet_addcost_for", "ADDCOST_FOR", "value_account") + try_get("kfet_overdraft_duration", "OVERDRAFT_DURATION", "value_duration") + try_get("kfet_overdraft_amount", "OVERDRAFT_AMOUNT", "value_decimal") + try_get("kfet_cancel_duration", "CANCEL_DURATION", "value_duration") cfg_form = KFetConfigForm(initial=cfg) if cfg_form.is_valid(): @@ -41,18 +41,10 @@ def adapt_settings(apps, schema_editor): class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0053_created_at'), - ('djconfig', '0001_initial'), - ] + dependencies = [("kfet", "0053_created_at"), ("djconfig", "0001_initial")] operations = [ migrations.RunPython(adapt_settings), - migrations.RemoveField( - model_name='settings', - name='value_account', - ), - migrations.DeleteModel( - name='Settings', - ), + migrations.RemoveField(model_name="settings", name="value_account"), + migrations.DeleteModel(name="Settings"), ] diff --git a/kfet/migrations/0054_update_promos.py b/kfet/migrations/0054_update_promos.py index 2691e903..0f86779b 100644 --- a/kfet/migrations/0054_update_promos.py +++ b/kfet/migrations/0054_update_promos.py @@ -6,14 +6,56 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0053_created_at'), - ] + dependencies = [("kfet", "0053_created_at")] operations = [ migrations.AlterField( - model_name='account', - name='promo', - field=models.IntegerField(blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017)], default=2017, null=True), - ), + model_name="account", + name="promo", + field=models.IntegerField( + blank=True, + choices=[ + (1980, 1980), + (1981, 1981), + (1982, 1982), + (1983, 1983), + (1984, 1984), + (1985, 1985), + (1986, 1986), + (1987, 1987), + (1988, 1988), + (1989, 1989), + (1990, 1990), + (1991, 1991), + (1992, 1992), + (1993, 1993), + (1994, 1994), + (1995, 1995), + (1996, 1996), + (1997, 1997), + (1998, 1998), + (1999, 1999), + (2000, 2000), + (2001, 2001), + (2002, 2002), + (2003, 2003), + (2004, 2004), + (2005, 2005), + (2006, 2006), + (2007, 2007), + (2008, 2008), + (2009, 2009), + (2010, 2010), + (2011, 2011), + (2012, 2012), + (2013, 2013), + (2014, 2014), + (2015, 2015), + (2016, 2016), + (2017, 2017), + ], + default=2017, + null=True, + ), + ) ] diff --git a/kfet/migrations/0055_move_permissions.py b/kfet/migrations/0055_move_permissions.py index a418124c..9db3f793 100644 --- a/kfet/migrations/0055_move_permissions.py +++ b/kfet/migrations/0055_move_permissions.py @@ -13,41 +13,42 @@ def forwards_perms(apps, schema_editor): permissions which are assumed unused. """ - ContentType = apps.get_model('contenttypes', 'contenttype') + ContentType = apps.get_model("contenttypes", "contenttype") try: ctype_global = ContentType.objects.get( - app_label="kfet", model="globalpermissions", + app_label="kfet", model="globalpermissions" ) except ContentType.DoesNotExist: # We are not migrating from existing data, nothing to do. return perms = { - 'account': ( - 'is_team', 'manage_perms', 'manage_addcosts', - 'edit_balance_account', 'change_account_password', - 'special_add_account', + "account": ( + "is_team", + "manage_perms", + "manage_addcosts", + "edit_balance_account", + "change_account_password", + "special_add_account", ), - 'accountnegative': ('view_negs',), - 'inventory': ('order_to_inventory',), - 'operation': ( - 'perform_deposit', 'perform_negative_operations', - 'override_frozen_protection', 'cancel_old_operations', - 'perform_commented_operations', + "accountnegative": ("view_negs",), + "inventory": ("order_to_inventory",), + "operation": ( + "perform_deposit", + "perform_negative_operations", + "override_frozen_protection", + "cancel_old_operations", + "perform_commented_operations", ), } - Permission = apps.get_model('auth', 'permission') + Permission = apps.get_model("auth", "permission") global_perms = Permission.objects.filter(content_type=ctype_global) for modelname, codenames in perms.items(): - model = apps.get_model('kfet', modelname) + model = apps.get_model("kfet", modelname) ctype = ContentType.objects.get_for_model(model) - ( - global_perms - .filter(codename__in=codenames) - .update(content_type=ctype) - ) + (global_perms.filter(codename__in=codenames).update(content_type=ctype)) ctype_global.delete() @@ -55,27 +56,64 @@ def forwards_perms(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('kfet', '0054_delete_settings'), - ('contenttypes', '__latest__'), - ('auth', '__latest__'), + ("kfet", "0054_delete_settings"), + ("contenttypes", "__latest__"), + ("auth", "__latest__"), ] operations = [ migrations.AlterModelOptions( - name='account', - options={'permissions': (('is_team', 'Is part of the team'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'équipe"), ('special_add_account', 'Créer un compte avec une balance initiale'))}, + name="account", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ("edit_balance_account", "Modifier la balance d'un compte"), + ( + "change_account_password", + "Modifier le mot de passe d'une personne de l'équipe", + ), + ( + "special_add_account", + "Créer un compte avec une balance initiale", + ), + ) + }, ), migrations.AlterModelOptions( - name='accountnegative', - options={'permissions': (('view_negs', 'Voir la liste des négatifs'),)}, + name="accountnegative", + options={"permissions": (("view_negs", "Voir la liste des négatifs"),)}, ), migrations.AlterModelOptions( - name='inventory', - options={'ordering': ['-at'], 'permissions': (('order_to_inventory', "Générer un inventaire à partir d'une commande"),)}, + name="inventory", + options={ + "ordering": ["-at"], + "permissions": ( + ( + "order_to_inventory", + "Générer un inventaire à partir d'une commande", + ), + ), + }, ), migrations.AlterModelOptions( - name='operation', - options={'permissions': (('perform_deposit', 'Effectuer une charge'), ('perform_negative_operations', 'Enregistrer des commandes en négatif'), ('override_frozen_protection', "Forcer le gel d'un compte"), ('cancel_old_operations', 'Annuler des commandes non récentes'), ('perform_commented_operations', 'Enregistrer des commandes avec commentaires'))}, + name="operation", + options={ + "permissions": ( + ("perform_deposit", "Effectuer une charge"), + ( + "perform_negative_operations", + "Enregistrer des commandes en négatif", + ), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), + ) + }, ), migrations.RunPython(forwards_perms), ] diff --git a/kfet/migrations/0056_change_account_meta.py b/kfet/migrations/0056_change_account_meta.py index 3992bf3c..27e51417 100644 --- a/kfet/migrations/0056_change_account_meta.py +++ b/kfet/migrations/0056_change_account_meta.py @@ -6,13 +6,27 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0055_move_permissions'), - ] + dependencies = [("kfet", "0055_move_permissions")] operations = [ migrations.AlterModelOptions( - name='account', - options={'permissions': (('is_team', 'Is part of the team'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'équipe"), ('special_add_account', 'Créer un compte avec une balance initiale'), ('can_force_close', 'Fermer manuellement la K-Fêt'))}, - ), + name="account", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ("edit_balance_account", "Modifier la balance d'un compte"), + ( + "change_account_password", + "Modifier le mot de passe d'une personne de l'équipe", + ), + ( + "special_add_account", + "Créer un compte avec une balance initiale", + ), + ("can_force_close", "Fermer manuellement la K-Fêt"), + ) + }, + ) ] diff --git a/kfet/migrations/0057_merge.py b/kfet/migrations/0057_merge.py index 48f63399..456bbeb0 100644 --- a/kfet/migrations/0057_merge.py +++ b/kfet/migrations/0057_merge.py @@ -7,9 +7,8 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('kfet', '0056_change_account_meta'), - ('kfet', '0054_update_promos'), + ("kfet", "0056_change_account_meta"), + ("kfet", "0054_update_promos"), ] - operations = [ - ] + operations = [] diff --git a/kfet/migrations/0058_delete_genericteamtoken.py b/kfet/migrations/0058_delete_genericteamtoken.py index ea8b55cd..3b3216e9 100644 --- a/kfet/migrations/0058_delete_genericteamtoken.py +++ b/kfet/migrations/0058_delete_genericteamtoken.py @@ -6,12 +6,6 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0057_merge'), - ] + dependencies = [("kfet", "0057_merge")] - operations = [ - migrations.DeleteModel( - name='GenericTeamToken', - ), - ] + operations = [migrations.DeleteModel(name="GenericTeamToken")] diff --git a/kfet/migrations/0059_create_generic.py b/kfet/migrations/0059_create_generic.py index 4f04770c..7408a300 100644 --- a/kfet/migrations/0059_create_generic.py +++ b/kfet/migrations/0059_create_generic.py @@ -15,31 +15,22 @@ def setup_kfet_generic_user(apps, schema_editor): See also setup_kfet_generic_user from kfet.auth.utils module. """ - User = apps.get_model('auth', 'User') - CofProfile = apps.get_model('gestioncof', 'CofProfile') - Account = apps.get_model('kfet', 'Account') + User = apps.get_model("auth", "User") + CofProfile = apps.get_model("gestioncof", "CofProfile") + Account = apps.get_model("kfet", "Account") user, _ = User.objects.update_or_create( username=KFET_GENERIC_USERNAME, - defaults={ - 'first_name': 'Compte générique K-Fêt', - }, + defaults={"first_name": "Compte générique K-Fêt"}, ) profile, _ = CofProfile.objects.update_or_create(user=user) account, _ = Account.objects.update_or_create( - cofprofile=profile, - defaults={ - 'trigramme': KFET_GENERIC_TRIGRAMME, - }, + cofprofile=profile, defaults={"trigramme": KFET_GENERIC_TRIGRAMME} ) class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0058_delete_genericteamtoken'), - ] + dependencies = [("kfet", "0058_delete_genericteamtoken")] - operations = [ - migrations.RunPython(setup_kfet_generic_user), - ] + operations = [migrations.RunPython(setup_kfet_generic_user)] diff --git a/kfet/migrations/0060_amend_supplier.py b/kfet/migrations/0060_amend_supplier.py index 4eb569f8..0a56640d 100644 --- a/kfet/migrations/0060_amend_supplier.py +++ b/kfet/migrations/0060_amend_supplier.py @@ -6,34 +6,39 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0059_create_generic'), - ] + dependencies = [("kfet", "0059_create_generic")] operations = [ migrations.AlterField( - model_name='supplier', - name='address', - field=models.TextField(verbose_name='adresse', blank=True), + model_name="supplier", + name="address", + field=models.TextField(verbose_name="adresse", blank=True), ), migrations.AlterField( - model_name='supplier', - name='articles', - field=models.ManyToManyField(verbose_name='articles vendus', through='kfet.SupplierArticle', related_name='suppliers', to='kfet.Article'), + model_name="supplier", + name="articles", + field=models.ManyToManyField( + verbose_name="articles vendus", + through="kfet.SupplierArticle", + related_name="suppliers", + to="kfet.Article", + ), ), migrations.AlterField( - model_name='supplier', - name='comment', - field=models.TextField(verbose_name='commentaire', blank=True), + model_name="supplier", + name="comment", + field=models.TextField(verbose_name="commentaire", blank=True), ), migrations.AlterField( - model_name='supplier', - name='email', - field=models.EmailField(max_length=254, verbose_name='adresse mail', blank=True), + model_name="supplier", + name="email", + field=models.EmailField( + max_length=254, verbose_name="adresse mail", blank=True + ), ), migrations.AlterField( - model_name='supplier', - name='phone', - field=models.CharField(max_length=20, verbose_name='téléphone', blank=True), + model_name="supplier", + name="phone", + field=models.CharField(max_length=20, verbose_name="téléphone", blank=True), ), ] diff --git a/kfet/migrations/0061_add_perms_config.py b/kfet/migrations/0061_add_perms_config.py index 01bdf51d..7d10da31 100644 --- a/kfet/migrations/0061_add_perms_config.py +++ b/kfet/migrations/0061_add_perms_config.py @@ -6,13 +6,29 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0060_amend_supplier'), - ] + dependencies = [("kfet", "0060_amend_supplier")] operations = [ migrations.AlterModelOptions( - name='account', - options={'permissions': (('is_team', 'Is part of the team'), ('manage_perms', 'Gérer les permissions K-Fêt'), ('manage_addcosts', 'Gérer les majorations'), ('edit_balance_account', "Modifier la balance d'un compte"), ('change_account_password', "Modifier le mot de passe d'une personne de l'équipe"), ('special_add_account', 'Créer un compte avec une balance initiale'), ('can_force_close', 'Fermer manuellement la K-Fêt'), ('see_config', 'Voir la configuration K-Fêt'), ('change_config', 'Modifier la configuration K-Fêt'))}, - ), + name="account", + options={ + "permissions": ( + ("is_team", "Is part of the team"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ("edit_balance_account", "Modifier la balance d'un compte"), + ( + "change_account_password", + "Modifier le mot de passe d'une personne de l'équipe", + ), + ( + "special_add_account", + "Créer un compte avec une balance initiale", + ), + ("can_force_close", "Fermer manuellement la K-Fêt"), + ("see_config", "Voir la configuration K-Fêt"), + ("change_config", "Modifier la configuration K-Fêt"), + ) + }, + ) ] diff --git a/kfet/migrations/0062_delete_globalpermissions.py b/kfet/migrations/0062_delete_globalpermissions.py index ee245412..9a357a2d 100644 --- a/kfet/migrations/0062_delete_globalpermissions.py +++ b/kfet/migrations/0062_delete_globalpermissions.py @@ -3,12 +3,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0061_add_perms_config'), - ] + dependencies = [("kfet", "0061_add_perms_config")] - operations = [ - migrations.DeleteModel( - name='GlobalPermissions', - ), - ] + operations = [migrations.DeleteModel(name="GlobalPermissions")] diff --git a/kfet/migrations/0063_promo.py b/kfet/migrations/0063_promo.py index 3fac5a8a..de04573a 100644 --- a/kfet/migrations/0063_promo.py +++ b/kfet/migrations/0063_promo.py @@ -7,14 +7,57 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0062_delete_globalpermissions'), - ] + dependencies = [("kfet", "0062_delete_globalpermissions")] operations = [ migrations.AlterField( - model_name='account', - name='promo', - field=models.IntegerField(blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018)], default=2017, null=True), - ), + model_name="account", + name="promo", + field=models.IntegerField( + blank=True, + choices=[ + (1980, 1980), + (1981, 1981), + (1982, 1982), + (1983, 1983), + (1984, 1984), + (1985, 1985), + (1986, 1986), + (1987, 1987), + (1988, 1988), + (1989, 1989), + (1990, 1990), + (1991, 1991), + (1992, 1992), + (1993, 1993), + (1994, 1994), + (1995, 1995), + (1996, 1996), + (1997, 1997), + (1998, 1998), + (1999, 1999), + (2000, 2000), + (2001, 2001), + (2002, 2002), + (2003, 2003), + (2004, 2004), + (2005, 2005), + (2006, 2006), + (2007, 2007), + (2008, 2008), + (2009, 2009), + (2010, 2010), + (2011, 2011), + (2012, 2012), + (2013, 2013), + (2014, 2014), + (2015, 2015), + (2016, 2016), + (2017, 2017), + (2018, 2018), + ], + default=2017, + null=True, + ), + ) ] diff --git a/kfet/migrations/0064_promo_2018.py b/kfet/migrations/0064_promo_2018.py index c99d85b5..7fe5e160 100644 --- a/kfet/migrations/0064_promo_2018.py +++ b/kfet/migrations/0064_promo_2018.py @@ -7,14 +7,57 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('kfet', '0063_promo'), - ] + dependencies = [("kfet", "0063_promo")] operations = [ migrations.AlterField( - model_name='account', - name='promo', - field=models.IntegerField(blank=True, choices=[(1980, 1980), (1981, 1981), (1982, 1982), (1983, 1983), (1984, 1984), (1985, 1985), (1986, 1986), (1987, 1987), (1988, 1988), (1989, 1989), (1990, 1990), (1991, 1991), (1992, 1992), (1993, 1993), (1994, 1994), (1995, 1995), (1996, 1996), (1997, 1997), (1998, 1998), (1999, 1999), (2000, 2000), (2001, 2001), (2002, 2002), (2003, 2003), (2004, 2004), (2005, 2005), (2006, 2006), (2007, 2007), (2008, 2008), (2009, 2009), (2010, 2010), (2011, 2011), (2012, 2012), (2013, 2013), (2014, 2014), (2015, 2015), (2016, 2016), (2017, 2017), (2018, 2018)], default=2018, null=True), - ), + model_name="account", + name="promo", + field=models.IntegerField( + blank=True, + choices=[ + (1980, 1980), + (1981, 1981), + (1982, 1982), + (1983, 1983), + (1984, 1984), + (1985, 1985), + (1986, 1986), + (1987, 1987), + (1988, 1988), + (1989, 1989), + (1990, 1990), + (1991, 1991), + (1992, 1992), + (1993, 1993), + (1994, 1994), + (1995, 1995), + (1996, 1996), + (1997, 1997), + (1998, 1998), + (1999, 1999), + (2000, 2000), + (2001, 2001), + (2002, 2002), + (2003, 2003), + (2004, 2004), + (2005, 2005), + (2006, 2006), + (2007, 2007), + (2008, 2008), + (2009, 2009), + (2010, 2010), + (2011, 2011), + (2012, 2012), + (2013, 2013), + (2014, 2014), + (2015, 2015), + (2016, 2016), + (2017, 2017), + (2018, 2018), + ], + default=2018, + null=True, + ), + ) ] diff --git a/kfet/models.py b/kfet/models.py index e952e85a..6b16505e 100644 --- a/kfet/models.py +++ b/kfet/models.py @@ -1,30 +1,31 @@ +import re +from datetime import date from functools import reduce -from django.db import models -from django.core.validators import RegexValidator from django.contrib.auth.models import User -from gestioncof.models import CofProfile -from django.urls import reverse -from django.utils.six.moves import reduce -from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ -from django.db import transaction +from django.core.validators import RegexValidator +from django.db import models, transaction from django.db.models import F -from datetime import date -import re +from django.urls import reverse +from django.utils import timezone +from django.utils.six.moves import reduce +from django.utils.translation import ugettext_lazy as _ + +from gestioncof.models import CofProfile from .auth import KFET_GENERIC_TRIGRAMME from .auth.models import GenericTeamToken # noqa - from .config import kfet_config from .utils import to_ukf + def choices_length(choices): return reduce(lambda m, choice: max(m, len(choice[0])), choices, 0) + def default_promo(): now = date.today() - return now.month <= 8 and now.year-1 or now.year + return now.month <= 8 and now.year - 1 or now.year class AccountManager(models.Manager): @@ -32,8 +33,7 @@ class AccountManager(models.Manager): def get_queryset(self): """Always append related data to this Account.""" - return super().get_queryset().select_related('cofprofile__user', - 'negative') + return super().get_queryset().select_related("cofprofile__user", "negative") def get_generic(self): """ @@ -48,6 +48,7 @@ class AccountManager(models.Manager): Raises Account.DoesNotExist if no Account has this password. """ from .auth.utils import hash_password + if password is None: raise self.model.DoesNotExist return self.get(password=hash_password(password)) @@ -57,69 +58,71 @@ class Account(models.Model): objects = AccountManager() cofprofile = models.OneToOneField( - CofProfile, on_delete = models.PROTECT, - related_name = "account_kfet") + CofProfile, on_delete=models.PROTECT, related_name="account_kfet" + ) trigramme = models.CharField( - unique = True, - max_length = 3, - validators = [RegexValidator(regex='^[^a-z]{3}$')], - db_index = True) - balance = models.DecimalField( - max_digits = 6, decimal_places = 2, - default = 0) - is_frozen = models.BooleanField("est gelé", default = False) + unique=True, + max_length=3, + validators=[RegexValidator(regex="^[^a-z]{3}$")], + db_index=True, + ) + balance = models.DecimalField(max_digits=6, decimal_places=2, default=0) + is_frozen = models.BooleanField("est gelé", default=False) created_at = models.DateTimeField(default=timezone.now) # Optional - PROMO_CHOICES = [(r,r) for r in range(1980, date.today().year+1)] + PROMO_CHOICES = [(r, r) for r in range(1980, date.today().year + 1)] promo = models.IntegerField( - choices = PROMO_CHOICES, - blank = True, null = True, default = default_promo()) - nickname = models.CharField( - "surnom(s)", - max_length = 255, - blank = True, default = "") + choices=PROMO_CHOICES, blank=True, null=True, default=default_promo() + ) + nickname = models.CharField("surnom(s)", max_length=255, blank=True, default="") password = models.CharField( - max_length = 255, - unique = True, - blank = True, null = True, default = None) + max_length=255, unique=True, blank=True, null=True, default=None + ) class Meta: permissions = ( - ('is_team', 'Is part of the team'), - ('manage_perms', 'Gérer les permissions K-Fêt'), - ('manage_addcosts', 'Gérer les majorations'), - ('edit_balance_account', "Modifier la balance d'un compte"), - ('change_account_password', - "Modifier le mot de passe d'une personne de l'équipe"), - ('special_add_account', - "Créer un compte avec une balance initiale"), - ('can_force_close', "Fermer manuellement la K-Fêt"), - ('see_config', "Voir la configuration K-Fêt"), - ('change_config', "Modifier la configuration K-Fêt"), + ("is_team", "Is part of the team"), + ("manage_perms", "Gérer les permissions K-Fêt"), + ("manage_addcosts", "Gérer les majorations"), + ("edit_balance_account", "Modifier la balance d'un compte"), + ( + "change_account_password", + "Modifier le mot de passe d'une personne de l'équipe", + ), + ("special_add_account", "Créer un compte avec une balance initiale"), + ("can_force_close", "Fermer manuellement la K-Fêt"), + ("see_config", "Voir la configuration K-Fêt"), + ("change_config", "Modifier la configuration K-Fêt"), ) def __str__(self): - return '%s (%s)' % (self.trigramme, self.name) + return "%s (%s)" % (self.trigramme, self.name) # Propriétés pour accéder aux attributs de cofprofile et user @property def user(self): return self.cofprofile.user + @property def username(self): return self.cofprofile.user.username + @property def first_name(self): return self.cofprofile.user.first_name + @property def last_name(self): return self.cofprofile.user.last_name + @property def email(self): return self.cofprofile.user.email + @property def departement(self): return self.cofprofile.departement + @property def is_cof(self): return self.cofprofile.is_cof @@ -131,7 +134,7 @@ class Account(models.Model): @property def real_balance(self): - if hasattr(self, 'negative') and self.negative.balance_offset: + if hasattr(self, "negative") and self.negative.balance_offset: return self.balance - self.negative.balance_offset return self.balance @@ -141,29 +144,29 @@ class Account(models.Model): @property def is_cash(self): - return self.trigramme == 'LIQ' + return self.trigramme == "LIQ" @property def need_comment(self): - return self.trigramme == '#13' + return self.trigramme == "#13" @property def readable(self): - return self.trigramme != 'GNR' + return self.trigramme != "GNR" @property def is_team(self): - return self.has_perm('kfet.is_team') + return self.has_perm("kfet.is_team") @staticmethod def is_validandfree(trigramme): - data = { 'is_valid' : False, 'is_free' : False } + data = {"is_valid": False, "is_free": False} pattern = re.compile("^[^a-z]{3}$") - data['is_valid'] = pattern.match(trigramme) and True or False + data["is_valid"] = pattern.match(trigramme) and True or False try: account = Account.objects.get(trigramme=trigramme) except Account.DoesNotExist: - data['is_free'] = True + data["is_free"] = True return data def perms_to_perform_operation(self, amount): @@ -176,31 +179,34 @@ class Account(models.Model): # Yes, so no perms and no stop return set(), False if self.need_comment: - perms.add('kfet.perform_commented_operations') + perms.add("kfet.perform_commented_operations") # Checking is frozen account if self.is_frozen: - perms.add('kfet.override_frozen_protection') + perms.add("kfet.override_frozen_protection") new_balance = self.balance + amount if new_balance < 0 and amount < 0: # Retrieving overdraft amount limit - if (hasattr(self, 'negative') - and self.negative.authz_overdraft_amount is not None): - overdraft_amount = - self.negative.authz_overdraft_amount + if ( + hasattr(self, "negative") + and self.negative.authz_overdraft_amount is not None + ): + overdraft_amount = -self.negative.authz_overdraft_amount else: - overdraft_amount = - overdraft_amount_max + overdraft_amount = -overdraft_amount_max # Retrieving overdraft datetime limit - if (hasattr(self, 'negative') - and self.negative.authz_overdraft_until is not None): + if ( + hasattr(self, "negative") + and self.negative.authz_overdraft_until is not None + ): overdraft_until = self.negative.authz_overdraft_until - elif hasattr(self, 'negative'): - overdraft_until = \ - self.negative.start + overdraft_duration_max + elif hasattr(self, "negative"): + overdraft_until = self.negative.start + overdraft_duration_max else: overdraft_until = timezone.now() + overdraft_duration_max # Checking it doesn't break 1 rule if new_balance < overdraft_amount or timezone.now() > overdraft_until: stop_ope = True - perms.add('kfet.perform_negative_operations') + perms.add("kfet.perform_negative_operations") return perms, stop_ope # Surcharge Méthode save() avec gestions de User et CofProfile @@ -209,7 +215,7 @@ class Account(models.Model): # Action: # - Enregistre User, CofProfile à partir de "data" # - Enregistre Account - def save(self, data = {}, *args, **kwargs): + def save(self, data={}, *args, **kwargs): if self.pk and data: # Account update @@ -217,8 +223,8 @@ class Account(models.Model): # Updating User with data user = self.user user.first_name = data.get("first_name", user.first_name) - user.last_name = data.get("last_name", user.last_name) - user.email = data.get("email", user.email) + user.last_name = data.get("last_name", user.last_name) + user.email = data.get("email", user.email) user.save() # Updating CofProfile with data cof = self.cofprofile @@ -240,18 +246,18 @@ class Account(models.Model): # Creating or updating User instance (user, _) = User.objects.get_or_create(username=username) if "first_name" in data: - user.first_name = data['first_name'] + user.first_name = data["first_name"] if "last_name" in data: - user.last_name = data['last_name'] + user.last_name = data["last_name"] if "email" in data: - user.email = data['email'] + user.email = data["email"] user.save() # Creating or updating CofProfile instance (cof, _) = CofProfile.objects.get_or_create(user=user) if "login_clipper" in data: - cof.login_clipper = data['login_clipper'] + cof.login_clipper = data["login_clipper"] if "departement" in data: - cof.departement = data['departement'] + cof.departement = data["departement"] cof.save() if data: self.cofprofile = cof @@ -259,6 +265,7 @@ class Account(models.Model): def change_pwd(self, clear_password): from .auth.utils import hash_password + self.password = hash_password(clear_password) # Surcharge de delete @@ -269,23 +276,21 @@ class Account(models.Model): def update_negative(self): if self.real_balance < 0: - if hasattr(self, 'negative') and not self.negative.start: + if hasattr(self, "negative") and not self.negative.start: self.negative.start = timezone.now() self.negative.save() - elif not hasattr(self, 'negative'): - self.negative = ( - AccountNegative.objects.create( - account=self, start=timezone.now(), - ) + elif not hasattr(self, "negative"): + self.negative = AccountNegative.objects.create( + account=self, start=timezone.now() ) - elif hasattr(self, 'negative'): + elif hasattr(self, "negative"): # self.real_balance >= 0 balance_offset = self.negative.balance_offset if balance_offset: ( - Account.objects - .filter(pk=self.pk) - .update(balance=F('balance')-balance_offset) + Account.objects.filter(pk=self.pk).update( + balance=F("balance") - balance_offset + ) ) self.refresh_from_db() self.negative.delete() @@ -299,41 +304,40 @@ class AccountNegativeManager(models.Manager): """Manager for AccountNegative model.""" def get_queryset(self): - return ( - super().get_queryset() - .select_related('account__cofprofile__user') - ) + return super().get_queryset().select_related("account__cofprofile__user") class AccountNegative(models.Model): objects = AccountNegativeManager() account = models.OneToOneField( - Account, on_delete=models.PROTECT, - related_name="negative", + Account, on_delete=models.PROTECT, related_name="negative" ) start = models.DateTimeField(blank=True, null=True, default=None) balance_offset = models.DecimalField( "décalage de balance", help_text="Montant non compris dans l'autorisation de négatif", - max_digits=6, decimal_places=2, - blank=True, null=True, default=None, + max_digits=6, + decimal_places=2, + blank=True, + null=True, + default=None, ) authz_overdraft_amount = models.DecimalField( "négatif autorisé", - max_digits=6, decimal_places=2, - blank=True, null=True, default=None, + max_digits=6, + decimal_places=2, + blank=True, + null=True, + default=None, ) authz_overdraft_until = models.DateTimeField( - "expiration du négatif", - blank=True, null=True, default=None, + "expiration du négatif", blank=True, null=True, default=None ) comment = models.CharField("commentaire", max_length=255, blank=True) class Meta: - permissions = ( - ('view_negs', 'Voir la liste des négatifs'), - ) + permissions = (("view_negs", "Voir la liste des négatifs"),) @property def until_default(self): @@ -341,31 +345,26 @@ class AccountNegative(models.Model): class CheckoutQuerySet(models.QuerySet): - def is_valid(self): now = timezone.now() return self.filter(valid_from__lte=now, valid_to__gte=now) class Checkout(models.Model): - created_by = models.ForeignKey( - Account, on_delete = models.PROTECT, - related_name = "+") - name = models.CharField(max_length = 45) + created_by = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="+") + name = models.CharField(max_length=45) valid_from = models.DateTimeField() - valid_to = models.DateTimeField() - balance = models.DecimalField( - max_digits = 6, decimal_places = 2, - default = 0) - is_protected = models.BooleanField(default = False) + valid_to = models.DateTimeField() + balance = models.DecimalField(max_digits=6, decimal_places=2, default=0) + is_protected = models.BooleanField(default=False) objects = CheckoutQuerySet.as_manager() def get_absolute_url(self): - return reverse('kfet.checkout.read', kwargs={'pk': self.pk}) + return reverse("kfet.checkout.read", kwargs={"pk": self.pk}) class Meta: - ordering = ['-valid_to'] + ordering = ["-valid_to"] def __str__(self): return self.name @@ -388,31 +387,30 @@ class Checkout(models.Model): class CheckoutTransfer(models.Model): from_checkout = models.ForeignKey( - Checkout, on_delete = models.PROTECT, - related_name = "transfers_from") + Checkout, on_delete=models.PROTECT, related_name="transfers_from" + ) to_checkout = models.ForeignKey( - Checkout, on_delete = models.PROTECT, - related_name = "transfers_to") - amount = models.DecimalField( - max_digits = 6, decimal_places = 2) + Checkout, on_delete=models.PROTECT, related_name="transfers_to" + ) + amount = models.DecimalField(max_digits=6, decimal_places=2) class CheckoutStatement(models.Model): - by = models.ForeignKey( - Account, on_delete = models.PROTECT, - related_name = "+") + by = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="+") checkout = models.ForeignKey( - Checkout, on_delete = models.PROTECT, - related_name = "statements") - balance_old = models.DecimalField("ancienne balance", - max_digits = 6, decimal_places = 2) - balance_new = models.DecimalField("nouvelle balance", - max_digits = 6, decimal_places = 2) - amount_taken = models.DecimalField("montant pris", - max_digits = 6, decimal_places = 2) - amount_error = models.DecimalField("montant de l'erreur", - max_digits = 6, decimal_places = 2) - at = models.DateTimeField(auto_now_add = True) + Checkout, on_delete=models.PROTECT, related_name="statements" + ) + balance_old = models.DecimalField( + "ancienne balance", max_digits=6, decimal_places=2 + ) + balance_new = models.DecimalField( + "nouvelle balance", max_digits=6, decimal_places=2 + ) + amount_taken = models.DecimalField("montant pris", max_digits=6, decimal_places=2) + amount_error = models.DecimalField( + "montant de l'erreur", max_digits=6, decimal_places=2 + ) + at = models.DateTimeField(auto_now_add=True) not_count = models.BooleanField("caisse non comptée", default=False) taken_001 = models.PositiveSmallIntegerField("pièces de 1¢", default=0) @@ -431,68 +429,76 @@ class CheckoutStatement(models.Model): taken_200 = models.PositiveSmallIntegerField("billets de 200€", default=0) taken_500 = models.PositiveSmallIntegerField("billets de 500€", default=0) taken_cheque = models.DecimalField( - "montant des chèques", - default=0, max_digits=6, decimal_places=2) + "montant des chèques", default=0, max_digits=6, decimal_places=2 + ) def __str__(self): - return '%s %s' % (self.checkout, self.at) + return "%s %s" % (self.checkout, self.at) def save(self, *args, **kwargs): if not self.pk: checkout_id = self.checkout_id - self.balance_old = (Checkout.objects - .values_list('balance', flat=True).get(pk=checkout_id)) + self.balance_old = Checkout.objects.values_list("balance", flat=True).get( + pk=checkout_id + ) if self.not_count: self.balance_new = self.balance_old - self.amount_taken - self.amount_error = ( - self.balance_new + self.amount_taken - self.balance_old) + self.amount_error = self.balance_new + self.amount_taken - self.balance_old with transaction.atomic(): Checkout.objects.filter(pk=checkout_id).update(balance=self.balance_new) super().save(*args, **kwargs) else: - self.amount_error = ( - self.balance_new + self.amount_taken - self.balance_old) + self.amount_error = self.balance_new + self.amount_taken - self.balance_old # Si on modifie le dernier relevé d'une caisse et que la nouvelle # balance est modifiée alors on modifie la balance actuelle de la caisse - last_statement = (CheckoutStatement.objects - .filter(checkout=self.checkout) - .order_by('at') - .last()) - if (last_statement.pk == self.pk - and last_statement.balance_new != self.balance_new): + last_statement = ( + CheckoutStatement.objects.filter(checkout=self.checkout) + .order_by("at") + .last() + ) + if ( + last_statement.pk == self.pk + and last_statement.balance_new != self.balance_new + ): Checkout.objects.filter(pk=self.checkout_id).update( - balance=F('balance') - last_statement.balance_new + self.balance_new) + balance=F("balance") - last_statement.balance_new + self.balance_new + ) super().save(*args, **kwargs) class ArticleCategory(models.Model): name = models.CharField("nom", max_length=45) - has_addcost = models.BooleanField("majorée", default=True, - help_text="Si oui et qu'une majoration " - "est active, celle-ci sera " - "appliquée aux articles de " - "cette catégorie.") + has_addcost = models.BooleanField( + "majorée", + default=True, + help_text="Si oui et qu'une majoration " + "est active, celle-ci sera " + "appliquée aux articles de " + "cette catégorie.", + ) def __str__(self): return self.name class Article(models.Model): - name = models.CharField("nom", max_length = 45) - is_sold = models.BooleanField("en vente", default = True) - hidden = models.BooleanField("caché", - default=False, - help_text="Si oui, ne sera pas affiché " - "au public ; par exemple " - "sur la carte.") - price = models.DecimalField( - "prix", - max_digits = 6, decimal_places = 2, - default = 0) - stock = models.IntegerField(default = 0) + name = models.CharField("nom", max_length=45) + is_sold = models.BooleanField("en vente", default=True) + hidden = models.BooleanField( + "caché", + default=False, + help_text="Si oui, ne sera pas affiché " + "au public ; par exemple " + "sur la carte.", + ) + price = models.DecimalField("prix", max_digits=6, decimal_places=2, default=0) + stock = models.IntegerField(default=0) category = models.ForeignKey( - ArticleCategory, on_delete = models.PROTECT, - related_name = "articles", verbose_name='catégorie') + ArticleCategory, + on_delete=models.PROTECT, + related_name="articles", + verbose_name="catégorie", + ) BOX_TYPE_CHOICES = ( ("caisse", "caisse"), ("carton", "carton"), @@ -501,18 +507,21 @@ class Article(models.Model): ) box_type = models.CharField( "type de contenant", - choices = BOX_TYPE_CHOICES, - max_length = choices_length(BOX_TYPE_CHOICES), - blank = True, null = True, default = None) + choices=BOX_TYPE_CHOICES, + max_length=choices_length(BOX_TYPE_CHOICES), + blank=True, + null=True, + default=None, + ) box_capacity = models.PositiveSmallIntegerField( - "capacité du contenant", - blank = True, null = True, default = None) + "capacité du contenant", blank=True, null=True, default=None + ) def __str__(self): - return '%s - %s' % (self.category.name, self.name) + return "%s - %s" % (self.category.name, self.name) def get_absolute_url(self): - return reverse('kfet.article.read', kwargs={'pk': self.pk}) + return reverse("kfet.article.read", kwargs={"pk": self.pk}) def price_ukf(self): return to_ukf(self.price) @@ -520,43 +529,43 @@ class Article(models.Model): class ArticleRule(models.Model): article_on = models.OneToOneField( - Article, on_delete = models.PROTECT, - related_name = "rule_on") + Article, on_delete=models.PROTECT, related_name="rule_on" + ) article_to = models.OneToOneField( - Article, on_delete = models.PROTECT, - related_name = "rule_to") + Article, on_delete=models.PROTECT, related_name="rule_to" + ) ratio = models.PositiveSmallIntegerField() + class Inventory(models.Model): articles = models.ManyToManyField( - Article, - through = 'InventoryArticle', - related_name = "inventories") - by = models.ForeignKey( - Account, on_delete = models.PROTECT, - related_name = "+") - at = models.DateTimeField(auto_now_add = True) + Article, through="InventoryArticle", related_name="inventories" + ) + by = models.ForeignKey(Account, on_delete=models.PROTECT, related_name="+") + at = models.DateTimeField(auto_now_add=True) # Optional order = models.OneToOneField( - 'Order', on_delete = models.PROTECT, - related_name = "inventory", - blank = True, null = True, default = None) + "Order", + on_delete=models.PROTECT, + related_name="inventory", + blank=True, + null=True, + default=None, + ) class Meta: - ordering = ['-at'] + ordering = ["-at"] permissions = ( - ('order_to_inventory', "Générer un inventaire à partir d'une commande"), + ("order_to_inventory", "Générer un inventaire à partir d'une commande"), ) class InventoryArticle(models.Model): - inventory = models.ForeignKey( - Inventory, on_delete = models.PROTECT) - article = models.ForeignKey( - Article, on_delete = models.PROTECT) - stock_old = models.IntegerField() - stock_new = models.IntegerField() - stock_error = models.IntegerField(default = 0) + inventory = models.ForeignKey(Inventory, on_delete=models.PROTECT) + article = models.ForeignKey(Article, on_delete=models.PROTECT) + stock_old = models.IntegerField() + stock_new = models.IntegerField() + stock_error = models.IntegerField(default=0) def save(self, *args, **kwargs): # S'il s'agit d'un inventaire provenant d'une livraison, il n'y a pas @@ -570,8 +579,8 @@ class Supplier(models.Model): articles = models.ManyToManyField( Article, verbose_name=_("articles vendus"), - through='SupplierArticle', - related_name='suppliers', + through="SupplierArticle", + related_name="suppliers", ) name = models.CharField(_("nom"), max_length=45) address = models.TextField(_("adresse"), blank=True) @@ -584,175 +593,187 @@ class Supplier(models.Model): class SupplierArticle(models.Model): - supplier = models.ForeignKey( - Supplier, on_delete = models.PROTECT) - article = models.ForeignKey( - Article, on_delete = models.PROTECT) - at = models.DateTimeField(auto_now_add = True) + supplier = models.ForeignKey(Supplier, on_delete=models.PROTECT) + article = models.ForeignKey(Article, on_delete=models.PROTECT) + at = models.DateTimeField(auto_now_add=True) price_HT = models.DecimalField( - max_digits = 7, decimal_places = 4, - blank = True, null = True, default = None) + max_digits=7, decimal_places=4, blank=True, null=True, default=None + ) TVA = models.DecimalField( - max_digits = 4, decimal_places = 2, - blank = True, null = True, default = None) + max_digits=4, decimal_places=2, blank=True, null=True, default=None + ) rights = models.DecimalField( - max_digits = 7, decimal_places = 4, - blank = True, null = True, default = None) + max_digits=7, decimal_places=4, blank=True, null=True, default=None + ) + class Order(models.Model): supplier = models.ForeignKey( - Supplier, on_delete = models.PROTECT, - related_name = "orders") + Supplier, on_delete=models.PROTECT, related_name="orders" + ) articles = models.ManyToManyField( - Article, - through = "OrderArticle", - related_name = "orders") - at = models.DateTimeField(auto_now_add = True) - amount = models.DecimalField( - max_digits = 6, decimal_places = 2, default = 0) + Article, through="OrderArticle", related_name="orders" + ) + at = models.DateTimeField(auto_now_add=True) + amount = models.DecimalField(max_digits=6, decimal_places=2, default=0) class Meta: - ordering = ['-at'] + ordering = ["-at"] + class OrderArticle(models.Model): - order = models.ForeignKey( - Order, on_delete = models.PROTECT) - article = models.ForeignKey( - Article, on_delete = models.PROTECT) + order = models.ForeignKey(Order, on_delete=models.PROTECT) + article = models.ForeignKey(Article, on_delete=models.PROTECT) quantity_ordered = models.IntegerField() - quantity_received = models.IntegerField(default = 0) + quantity_received = models.IntegerField(default=0) + class TransferGroup(models.Model): at = models.DateTimeField(default=timezone.now) # Optional - comment = models.CharField( - max_length = 255, - blank = True, default = "") + comment = models.CharField(max_length=255, blank=True, default="") valid_by = models.ForeignKey( - Account, on_delete = models.PROTECT, - related_name = "+", - blank = True, null = True, default = None) + Account, + on_delete=models.PROTECT, + related_name="+", + blank=True, + null=True, + default=None, + ) class Transfer(models.Model): group = models.ForeignKey( - TransferGroup, on_delete=models.PROTECT, - related_name="transfers") + TransferGroup, on_delete=models.PROTECT, related_name="transfers" + ) from_acc = models.ForeignKey( - Account, on_delete=models.PROTECT, - related_name="transfers_from") + Account, on_delete=models.PROTECT, related_name="transfers_from" + ) to_acc = models.ForeignKey( - Account, on_delete=models.PROTECT, - related_name="transfers_to") + Account, on_delete=models.PROTECT, related_name="transfers_to" + ) amount = models.DecimalField(max_digits=6, decimal_places=2) # Optional canceled_by = models.ForeignKey( - Account, on_delete=models.PROTECT, - null=True, blank=True, default=None, - related_name="+") - canceled_at = models.DateTimeField( - null=True, blank=True, default=None) + Account, + on_delete=models.PROTECT, + null=True, + blank=True, + default=None, + related_name="+", + ) + canceled_at = models.DateTimeField(null=True, blank=True, default=None) def __str__(self): - return '{} -> {}: {}€'.format(self.from_acc, self.to_acc, self.amount) + return "{} -> {}: {}€".format(self.from_acc, self.to_acc, self.amount) class OperationGroup(models.Model): on_acc = models.ForeignKey( - Account, on_delete = models.PROTECT, - related_name = "opesgroup") + Account, on_delete=models.PROTECT, related_name="opesgroup" + ) checkout = models.ForeignKey( - Checkout, on_delete = models.PROTECT, - related_name = "opesgroup") + Checkout, on_delete=models.PROTECT, related_name="opesgroup" + ) at = models.DateTimeField(default=timezone.now) - amount = models.DecimalField( - max_digits = 6, decimal_places = 2, - default = 0) - is_cof = models.BooleanField(default = False) + amount = models.DecimalField(max_digits=6, decimal_places=2, default=0) + is_cof = models.BooleanField(default=False) # Optional - comment = models.CharField( - max_length = 255, - blank = True, default = "") + comment = models.CharField(max_length=255, blank=True, default="") valid_by = models.ForeignKey( - Account, on_delete = models.PROTECT, - related_name = "+", - blank = True, null = True, default = None) + Account, + on_delete=models.PROTECT, + related_name="+", + blank=True, + null=True, + default=None, + ) def __str__(self): - return ', '.join(map(str, self.opes.all())) + return ", ".join(map(str, self.opes.all())) class Operation(models.Model): - PURCHASE = 'purchase' - DEPOSIT = 'deposit' - WITHDRAW = 'withdraw' - INITIAL = 'initial' - EDIT = 'edit' + PURCHASE = "purchase" + DEPOSIT = "deposit" + WITHDRAW = "withdraw" + INITIAL = "initial" + EDIT = "edit" TYPE_ORDER_CHOICES = ( - (PURCHASE, 'Achat'), - (DEPOSIT, 'Charge'), - (WITHDRAW, 'Retrait'), - (INITIAL, 'Initial'), - (EDIT, 'Édition'), + (PURCHASE, "Achat"), + (DEPOSIT, "Charge"), + (WITHDRAW, "Retrait"), + (INITIAL, "Initial"), + (EDIT, "Édition"), ) group = models.ForeignKey( - OperationGroup, on_delete=models.PROTECT, - related_name="opes") + OperationGroup, on_delete=models.PROTECT, related_name="opes" + ) type = models.CharField( - choices=TYPE_ORDER_CHOICES, - max_length=choices_length(TYPE_ORDER_CHOICES)) - amount = models.DecimalField( - max_digits=6, decimal_places=2, - blank=True, default=0) + choices=TYPE_ORDER_CHOICES, max_length=choices_length(TYPE_ORDER_CHOICES) + ) + amount = models.DecimalField(max_digits=6, decimal_places=2, blank=True, default=0) # Optional article = models.ForeignKey( - Article, on_delete=models.PROTECT, + Article, + on_delete=models.PROTECT, related_name="operations", - blank=True, null=True, default=None) - article_nb = models.PositiveSmallIntegerField( - blank=True, null=True, default=None) + blank=True, + null=True, + default=None, + ) + article_nb = models.PositiveSmallIntegerField(blank=True, null=True, default=None) canceled_by = models.ForeignKey( - Account, on_delete=models.PROTECT, + Account, + on_delete=models.PROTECT, related_name="+", - blank=True, null=True, default=None) - canceled_at = models.DateTimeField( - blank=True, null=True, default=None) + blank=True, + null=True, + default=None, + ) + canceled_at = models.DateTimeField(blank=True, null=True, default=None) addcost_for = models.ForeignKey( - Account, on_delete=models.PROTECT, + Account, + on_delete=models.PROTECT, related_name="addcosts", - blank=True, null=True, default=None) + blank=True, + null=True, + default=None, + ) addcost_amount = models.DecimalField( - max_digits=6, decimal_places=2, - blank=True, null=True, default=None) + max_digits=6, decimal_places=2, blank=True, null=True, default=None + ) class Meta: permissions = ( - ('perform_deposit', 'Effectuer une charge'), - ('perform_negative_operations', - 'Enregistrer des commandes en négatif'), - ('override_frozen_protection', "Forcer le gel d'un compte"), - ('cancel_old_operations', 'Annuler des commandes non récentes'), - ('perform_commented_operations', - 'Enregistrer des commandes avec commentaires'), + ("perform_deposit", "Effectuer une charge"), + ("perform_negative_operations", "Enregistrer des commandes en négatif"), + ("override_frozen_protection", "Forcer le gel d'un compte"), + ("cancel_old_operations", "Annuler des commandes non récentes"), + ( + "perform_commented_operations", + "Enregistrer des commandes avec commentaires", + ), ) @property def is_checkout(self): - return (self.type == Operation.DEPOSIT or - self.type == Operation.WITHDRAW or - (self.type == Operation.PURCHASE and self.group.on_acc.is_cash) - ) + return ( + self.type == Operation.DEPOSIT + or self.type == Operation.WITHDRAW + or (self.type == Operation.PURCHASE and self.group.on_acc.is_cash) + ) def __str__(self): templates = { - self.PURCHASE: "{nb} {article.name} ({amount}€)", - self.DEPOSIT: "charge ({amount}€)", - self.WITHDRAW: "retrait ({amount}€)", - self.INITIAL: "initial ({amount}€)", - self.EDIT: "édition ({amount}€)", - } - return templates[self.type].format(nb=self.article_nb, - article=self.article, - amount=self.amount) + self.PURCHASE: "{nb} {article.name} ({amount}€)", + self.DEPOSIT: "charge ({amount}€)", + self.WITHDRAW: "retrait ({amount}€)", + self.INITIAL: "initial ({amount}€)", + self.EDIT: "édition ({amount}€)", + } + return templates[self.type].format( + nb=self.article_nb, article=self.article, amount=self.amount + ) diff --git a/kfet/open/consumers.py b/kfet/open/consumers.py index b28a4664..8b800c76 100644 --- a/kfet/open/consumers.py +++ b/kfet/open/consumers.py @@ -1,6 +1,5 @@ from ..decorators import kfet_is_team from ..utils import DjangoJsonWebsocketConsumer, PermConsumerMixin - from .open import kfet_open @@ -16,8 +15,8 @@ class OpenKfetConsumer(PermConsumerMixin, DjangoJsonWebsocketConsumer): def connection_groups(self, user, **kwargs): """Select which group the user should be connected.""" if kfet_is_team(user): - return ['kfet.open.team'] - return ['kfet.open.base'] + return ["kfet.open.team"] + return ["kfet.open.base"] def connect(self, message, *args, **kwargs): """Send current status on connect.""" diff --git a/kfet/open/open.py b/kfet/open/open.py index 82d6217a..d0e0c901 100644 --- a/kfet/open/open.py +++ b/kfet/open/open.py @@ -15,23 +15,20 @@ class OpenKfet(CachedMixin, object): Current state persists through cache. """ + # status is unknown after this duration time_unknown = timedelta(minutes=15) # status - OPENED = 'opened' - CLOSED = 'closed' - UNKNOWN = 'unknown' + OPENED = "opened" + CLOSED = "closed" + UNKNOWN = "unknown" # admin status - FAKE_CLOSED = 'fake_closed' + FAKE_CLOSED = "fake_closed" # cached attributes config - cached = { - '_raw_open': False, - '_last_update': None, - 'force_close': False, - } - cache_prefix = 'kfetopen' + cached = {"_raw_open": False, "_last_update": None, "force_close": False} + cache_prefix = "kfetopen" @property def raw_open(self): @@ -54,8 +51,10 @@ class OpenKfet(CachedMixin, object): return False if self.force_close else self.raw_open def status(self): - if (self.last_update is None or - timezone.now() - self.last_update >= self.time_unknown): + if ( + self.last_update is None + or timezone.now() - self.last_update >= self.time_unknown + ): return self.UNKNOWN return self.OPENED if self.is_open else self.CLOSED @@ -78,12 +77,10 @@ class OpenKfet(CachedMixin, object): """ status = self.status() - base = { - 'status': status, - } + base = {"status": status} restrict = { - 'admin_status': self.admin_status(status), - 'force_close': self.force_close, + "admin_status": self.admin_status(status), + "force_close": self.force_close, } return base, dict(base, **restrict) @@ -101,9 +98,10 @@ class OpenKfet(CachedMixin, object): def send_ws(self): """Send internal state to websocket channels.""" from .consumers import OpenKfetConsumer + base, team = self._export() - OpenKfetConsumer.group_send('kfet.open.base', base) - OpenKfetConsumer.group_send('kfet.open.team', team) + OpenKfetConsumer.group_send("kfet.open.base", base) + OpenKfetConsumer.group_send("kfet.open.team", team) kfet_open = OpenKfet() diff --git a/kfet/open/routing.py b/kfet/open/routing.py index 681bfab2..811ae56e 100644 --- a/kfet/open/routing.py +++ b/kfet/open/routing.py @@ -2,7 +2,4 @@ from channels.routing import route_class from . import consumers - -routing = [ - route_class(consumers.OpenKfetConsumer) -] +routing = [route_class(consumers.OpenKfetConsumer)] diff --git a/kfet/open/tests.py b/kfet/open/tests.py index 476eb6c0..75a9bf8a 100644 --- a/kfet/open/tests.py +++ b/kfet/open/tests.py @@ -2,14 +2,13 @@ import json from datetime import timedelta from unittest import mock +from channels.channel import Group +from channels.test import ChannelTestCase, WSClient from django.contrib.auth.models import AnonymousUser, Permission, User from django.test import Client from django.utils import timezone -from channels.channel import Group -from channels.test import ChannelTestCase, WSClient - -from . import kfet_open, OpenKfet +from . import OpenKfet, kfet_open from .consumers import OpenKfetConsumer @@ -79,40 +78,28 @@ class OpenKfetTest(ChannelTestCase): def test_export_user(self): """Export is limited for an anonymous user.""" export = self.kfet_open.export(AnonymousUser()) - self.assertSetEqual( - set(['status']), - set(export), - ) + self.assertSetEqual(set(["status"]), set(export)) def test_export_team(self): """Export all values for a team member.""" - user = User.objects.create_user('team', '', 'team') - user.user_permissions.add(Permission.objects.get(codename='is_team')) + user = User.objects.create_user("team", "", "team") + user.user_permissions.add(Permission.objects.get(codename="is_team")) export = self.kfet_open.export(user) - self.assertSetEqual( - set(['status', 'admin_status', 'force_close']), - set(export), - ) + self.assertSetEqual(set(["status", "admin_status", "force_close"]), set(export)) def test_send_ws(self): - Group('kfet.open.base').add('test.open.base') - Group('kfet.open.team').add('test.open.team') + Group("kfet.open.base").add("test.open.base") + Group("kfet.open.team").add("test.open.team") self.kfet_open.send_ws() - recv_base = self.get_next_message('test.open.base', require=True) - base = json.loads(recv_base['text']) - self.assertSetEqual( - set(['status']), - set(base), - ) + recv_base = self.get_next_message("test.open.base", require=True) + base = json.loads(recv_base["text"]) + self.assertSetEqual(set(["status"]), set(base)) - recv_admin = self.get_next_message('test.open.team', require=True) - admin = json.loads(recv_admin['text']) - self.assertSetEqual( - set(['status', 'admin_status', 'force_close']), - set(admin), - ) + recv_admin = self.get_next_message("test.open.team", require=True) + admin = json.loads(recv_admin["text"]) + self.assertSetEqual(set(["status", "admin_status", "force_close"]), set(admin)) class OpenKfetViewsTest(ChannelTestCase): @@ -120,34 +107,34 @@ class OpenKfetViewsTest(ChannelTestCase): def setUp(self): # Need this (and here) because of '.login' in setUp - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) # get some permissions perms = { - 'kfet.is_team': Permission.objects.get(codename='is_team'), - 'kfet.can_force_close': Permission.objects.get(codename='can_force_close'), + "kfet.is_team": Permission.objects.get(codename="is_team"), + "kfet.can_force_close": Permission.objects.get(codename="can_force_close"), } # authenticated user and its client - self.u = User.objects.create_user('user', '', 'user') + self.u = User.objects.create_user("user", "", "user") self.c = Client() - self.c.login(username='user', password='user') + self.c.login(username="user", password="user") # team user and its clients - self.t = User.objects.create_user('team', '', 'team') - self.t.user_permissions.add(perms['kfet.is_team']) + self.t = User.objects.create_user("team", "", "team") + self.t.user_permissions.add(perms["kfet.is_team"]) self.c_t = Client() - self.c_t.login(username='team', password='team') + self.c_t.login(username="team", password="team") # admin user and its client - self.a = User.objects.create_user('admin', '', 'admin') + self.a = User.objects.create_user("admin", "", "admin") self.a.user_permissions.add( - perms['kfet.is_team'], perms['kfet.can_force_close'], + perms["kfet.is_team"], perms["kfet.can_force_close"] ) self.c_a = Client() - self.c_a.login(username='admin', password='admin') + self.c_a.login(username="admin", password="admin") def tearDown(self): kfet_open.clear_cache() @@ -155,17 +142,16 @@ class OpenKfetViewsTest(ChannelTestCase): def test_door(self): """Edit raw_status.""" for sent, expected in [(1, True), (0, False)]: - resp = Client().post('/k-fet/open/raw_open', { - 'raw_open': sent, - 'token': 'plop', - }) + resp = Client().post( + "/k-fet/open/raw_open", {"raw_open": sent, "token": "plop"} + ) self.assertEqual(200, resp.status_code) self.assertEqual(expected, kfet_open.raw_open) def test_force_close(self): """Edit force_close.""" for sent, expected in [(1, True), (0, False)]: - resp = self.c_a.post('/k-fet/open/force_close', {'force_close': sent}) + resp = self.c_a.post("/k-fet/open/force_close", {"force_close": sent}) self.assertEqual(200, resp.status_code) self.assertEqual(expected, kfet_open.force_close) @@ -173,7 +159,7 @@ class OpenKfetViewsTest(ChannelTestCase): """Can't edit force_close without kfet.can_force_close permission.""" clients = [Client(), self.c, self.c_t] for client in clients: - resp = client.post('/k-fet/open/force_close', {'force_close': 0}) + resp = client.post("/k-fet/open/force_close", {"force_close": 0}) self.assertEqual(403, resp.status_code) @@ -186,44 +172,44 @@ class OpenKfetConsumerTest(ChannelTestCase): c = WSClient() # connect - c.send_and_consume('websocket.connect', path='/ws/k-fet/open', - fail_on_none=True) + c.send_and_consume( + "websocket.connect", path="/ws/k-fet/open", fail_on_none=True + ) # initialization data is replied on connection self.assertIsNotNone(c.receive()) # client belongs to the 'kfet.open' group... - OpenKfetConsumer.group_send('kfet.open.base', {'test': 'plop'}) - self.assertEqual(c.receive(), {'test': 'plop'}) + OpenKfetConsumer.group_send("kfet.open.base", {"test": "plop"}) + self.assertEqual(c.receive(), {"test": "plop"}) # ...but not to the 'kfet.open.admin' one - OpenKfetConsumer.group_send('kfet.open.team', {'test': 'plop'}) + OpenKfetConsumer.group_send("kfet.open.team", {"test": "plop"}) self.assertIsNone(c.receive()) - @mock.patch('gestioncof.signals.messages') + @mock.patch("gestioncof.signals.messages") def test_team_user(self, mock_messages): """Team user is added to kfet.open.team group.""" # setup team user and its client - t = User.objects.create_user('team', '', 'team') - t.user_permissions.add( - Permission.objects.get(codename='is_team') - ) + t = User.objects.create_user("team", "", "team") + t.user_permissions.add(Permission.objects.get(codename="is_team")) c = WSClient() c.force_login(t) # connect - c.send_and_consume('websocket.connect', path='/ws/k-fet/open', - fail_on_none=True) + c.send_and_consume( + "websocket.connect", path="/ws/k-fet/open", fail_on_none=True + ) # initialization data is replied on connection self.assertIsNotNone(c.receive()) # client belongs to the 'kfet.open.admin' group... - OpenKfetConsumer.group_send('kfet.open.team', {'test': 'plop'}) - self.assertEqual(c.receive(), {'test': 'plop'}) + OpenKfetConsumer.group_send("kfet.open.team", {"test": "plop"}) + self.assertEqual(c.receive(), {"test": "plop"}) # ... but not to the 'kfet.open' one - OpenKfetConsumer.group_send('kfet.open.base', {'test': 'plop'}) + OpenKfetConsumer.group_send("kfet.open.base", {"test": "plop"}) self.assertIsNone(c.receive()) @@ -232,7 +218,7 @@ class OpenKfetScenarioTest(ChannelTestCase): def setUp(self): # Need this (and here) because of '.login' in setUp - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) @@ -242,10 +228,10 @@ class OpenKfetScenarioTest(ChannelTestCase): self.c_ws = WSClient() # root user - self.r = User.objects.create_superuser('root', '', 'root') + self.r = User.objects.create_superuser("root", "", "root") # its client (for views) self.r_c = Client() - self.r_c.login(username='root', password='root') + self.r_c.login(username="root", password="root") # its client (for websockets) self.r_c_ws = WSClient() self.r_c_ws.force_login(self.r) @@ -255,8 +241,7 @@ class OpenKfetScenarioTest(ChannelTestCase): def ws_connect(self, ws_client): ws_client.send_and_consume( - 'websocket.connect', path='/ws/k-fet/open', - fail_on_none=True, + "websocket.connect", path="/ws/k-fet/open", fail_on_none=True ) return ws_client.receive(json=True) @@ -264,17 +249,11 @@ class OpenKfetScenarioTest(ChannelTestCase): """Clients connect.""" # test for anonymous user msg = self.ws_connect(self.c_ws) - self.assertSetEqual( - set(['status']), - set(msg), - ) + self.assertSetEqual(set(["status"]), set(msg)) # test for root user msg = self.ws_connect(self.r_c_ws) - self.assertSetEqual( - set(['status', 'admin_status', 'force_close']), - set(msg), - ) + self.assertSetEqual(set(["status", "admin_status", "force_close"]), set(msg)) def test_scenario_1(self): """Clients connect, door opens, enable force close.""" @@ -282,33 +261,30 @@ class OpenKfetScenarioTest(ChannelTestCase): self.ws_connect(self.r_c_ws) # door sent "I'm open!" - self.c.post('/k-fet/open/raw_open', { - 'raw_open': True, - 'token': 'plop', - }) + self.c.post("/k-fet/open/raw_open", {"raw_open": True, "token": "plop"}) # anonymous user agree msg = self.c_ws.receive(json=True) - self.assertEqual(OpenKfet.OPENED, msg['status']) + self.assertEqual(OpenKfet.OPENED, msg["status"]) # root user too msg = self.r_c_ws.receive(json=True) - self.assertEqual(OpenKfet.OPENED, msg['status']) - self.assertEqual(OpenKfet.OPENED, msg['admin_status']) + self.assertEqual(OpenKfet.OPENED, msg["status"]) + self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) # admin says "no it's closed" - self.r_c.post('/k-fet/open/force_close', {'force_close': True}) + self.r_c.post("/k-fet/open/force_close", {"force_close": True}) # so anonymous user see it's closed msg = self.c_ws.receive(json=True) - self.assertEqual(OpenKfet.CLOSED, msg['status']) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) # root user too msg = self.r_c_ws.receive(json=True) - self.assertEqual(OpenKfet.CLOSED, msg['status']) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) # but root knows things - self.assertEqual(OpenKfet.FAKE_CLOSED, msg['admin_status']) - self.assertTrue(msg['force_close']) + self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) + self.assertTrue(msg["force_close"]) def test_scenario_2(self): """Starting falsely closed, clients connect, disable force close.""" @@ -316,19 +292,19 @@ class OpenKfetScenarioTest(ChannelTestCase): kfet_open.force_close = True msg = self.ws_connect(self.c_ws) - self.assertEqual(OpenKfet.CLOSED, msg['status']) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) msg = self.ws_connect(self.r_c_ws) - self.assertEqual(OpenKfet.CLOSED, msg['status']) - self.assertEqual(OpenKfet.FAKE_CLOSED, msg['admin_status']) - self.assertTrue(msg['force_close']) + self.assertEqual(OpenKfet.CLOSED, msg["status"]) + self.assertEqual(OpenKfet.FAKE_CLOSED, msg["admin_status"]) + self.assertTrue(msg["force_close"]) - self.r_c.post('/k-fet/open/force_close', {'force_close': False}) + self.r_c.post("/k-fet/open/force_close", {"force_close": False}) msg = self.c_ws.receive(json=True) - self.assertEqual(OpenKfet.OPENED, msg['status']) + self.assertEqual(OpenKfet.OPENED, msg["status"]) msg = self.r_c_ws.receive(json=True) - self.assertEqual(OpenKfet.OPENED, msg['status']) - self.assertEqual(OpenKfet.OPENED, msg['admin_status']) - self.assertFalse(msg['force_close']) + self.assertEqual(OpenKfet.OPENED, msg["status"]) + self.assertEqual(OpenKfet.OPENED, msg["admin_status"]) + self.assertFalse(msg["force_close"]) diff --git a/kfet/open/urls.py b/kfet/open/urls.py index bd227b96..c38b9ce4 100644 --- a/kfet/open/urls.py +++ b/kfet/open/urls.py @@ -2,10 +2,7 @@ from django.conf.urls import url from . import views - urlpatterns = [ - url(r'^raw_open$', views.raw_open, - name='kfet.open.edit_raw_open'), - url(r'^force_close$', views.force_close, - name='kfet.open.edit_force_close'), + url(r"^raw_open$", views.raw_open, name="kfet.open.edit_raw_open"), + url(r"^force_close$", views.force_close, name="kfet.open.edit_force_close"), ] diff --git a/kfet/open/views.py b/kfet/open/views.py index 4f1efa5f..49b91f4a 100644 --- a/kfet/open/views.py +++ b/kfet/open/views.py @@ -1,32 +1,31 @@ from django.conf import settings -from django.core.exceptions import PermissionDenied from django.contrib.auth.decorators import permission_required +from django.core.exceptions import PermissionDenied from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from .open import kfet_open - -TRUE_STR = ['1', 'True', 'true'] +TRUE_STR = ["1", "True", "true"] @csrf_exempt @require_POST def raw_open(request): - token = request.POST.get('token') + token = request.POST.get("token") if token != settings.KFETOPEN_TOKEN: raise PermissionDenied - raw_open = request.POST.get('raw_open') in TRUE_STR + raw_open = request.POST.get("raw_open") in TRUE_STR kfet_open.raw_open = raw_open kfet_open.send_ws() return HttpResponse() -@permission_required('kfet.can_force_close', raise_exception=True) +@permission_required("kfet.can_force_close", raise_exception=True) @require_POST def force_close(request): - force_close = request.POST.get('force_close') in TRUE_STR + force_close = request.POST.get("force_close") in TRUE_STR kfet_open.force_close = force_close kfet_open.send_ws() return HttpResponse() diff --git a/kfet/routing.py b/kfet/routing.py index f1305d4b..ceafca06 100644 --- a/kfet/routing.py +++ b/kfet/routing.py @@ -2,8 +2,7 @@ from channels.routing import include, route_class from . import consumers - routing = [ - route_class(consumers.KPsul, path=r'^/k-psul/$'), - include('kfet.open.routing.routing', path=r'^/open'), + route_class(consumers.KPsul, path=r"^/k-psul/$"), + include("kfet.open.routing.routing", path=r"^/open"), ] diff --git a/kfet/statistic.py b/kfet/statistic.py index 0aba4dda..02171267 100644 --- a/kfet/statistic.py +++ b/kfet/statistic.py @@ -1,11 +1,10 @@ from datetime import date, datetime, time, timedelta -from dateutil.relativedelta import relativedelta -from dateutil.parser import parse as dateutil_parse import pytz - -from django.utils import timezone +from dateutil.parser import parse as dateutil_parse +from dateutil.relativedelta import relativedelta from django.db.models import Sum +from django.utils import timezone KFET_WAKES_UP_AT = time(7, 0) @@ -13,7 +12,7 @@ KFET_WAKES_UP_AT = time(7, 0) def kfet_day(year, month, day, start_at=KFET_WAKES_UP_AT): """datetime wrapper with time offset.""" naive = datetime.combine(date(year, month, day), start_at) - return pytz.timezone('Europe/Paris').localize(naive, is_dst=None) + return pytz.timezone("Europe/Paris").localize(naive, is_dst=None) def to_kfet_day(dt, start_at=KFET_WAKES_UP_AT): @@ -27,8 +26,7 @@ class Scale(object): name = None step = None - def __init__(self, n_steps=0, begin=None, end=None, - last=False, std_chunk=True): + def __init__(self, n_steps=0, begin=None, end=None, last=False, std_chunk=True): self.std_chunk = std_chunk if last: end = timezone.now() @@ -48,9 +46,11 @@ class Scale(object): self.begin = begin self.end = end else: - raise Exception('Two of these args must be specified: ' - 'n_steps, begin, end; ' - 'or use last and n_steps') + raise Exception( + "Two of these args must be specified: " + "n_steps, begin, end; " + "or use last and n_steps" + ) self.datetimes = self.get_datetimes() @@ -65,7 +65,7 @@ class Scale(object): return self.std_chunk and self.get_chunk_start(dt) or dt def __getitem__(self, i): - return self.datetimes[i], self.datetimes[i+1] + return self.datetimes[i], self.datetimes[i + 1] def __len__(self): return len(self.datetimes) - 1 @@ -85,21 +85,18 @@ class Scale(object): if label_fmt is None: label_fmt = self.label_fmt return [ - begin.strftime(label_fmt.format(i=i, rev_i=len(self)-i)) + begin.strftime(label_fmt.format(i=i, rev_i=len(self) - i)) for i, (begin, end) in enumerate(self) ] def chunkify_qs(self, qs, field=None): if field is None: - field = 'at' - begin_f = '{}__gte'.format(field) - end_f = '{}__lte'.format(field) - return [ - qs.filter(**{begin_f: begin, end_f: end}) - for begin, end in self - ] + field = "at" + begin_f = "{}__gte".format(field) + end_f = "{}__lte".format(field) + return [qs.filter(**{begin_f: begin, end_f: end}) for begin, end in self] - def get_by_chunks(self, qs, field_callback=None, field_db='at'): + def get_by_chunks(self, qs, field_callback=None, field_db="at"): """Objects of queryset ranked according to the scale. Returns a generator whose each item, corresponding to a scale chunk, @@ -122,16 +119,14 @@ class Scale(object): """ if field_callback is None: + def field_callback(obj): return getattr(obj, field_db) - begin_f = '{}__gte'.format(field_db) - end_f = '{}__lte'.format(field_db) + begin_f = "{}__gte".format(field_db) + end_f = "{}__lte".format(field_db) - qs = ( - qs - .filter(**{begin_f: self.begin, end_f: self.end}) - ) + qs = qs.filter(**{begin_f: self.begin, end_f: self.end}) obj_iter = iter(qs) @@ -184,9 +179,9 @@ class Scale(object): class DayScale(Scale): - name = 'day' + name = "day" step = timedelta(days=1) - label_fmt = '%A' + label_fmt = "%A" @classmethod def get_chunk_start(cls, dt): @@ -194,9 +189,9 @@ class DayScale(Scale): class WeekScale(Scale): - name = 'week' + name = "week" step = timedelta(days=7) - label_fmt = 'Semaine %W' + label_fmt = "Semaine %W" @classmethod def get_chunk_start(cls, dt): @@ -206,62 +201,63 @@ class WeekScale(Scale): class MonthScale(Scale): - name = 'month' + name = "month" step = relativedelta(months=1) - label_fmt = '%B' + label_fmt = "%B" @classmethod def get_chunk_start(cls, dt): return to_kfet_day(dt).replace(day=1) -def stat_manifest(scales_def=None, scale_args=None, scale_prefix=None, - **other_url_params): +def stat_manifest( + scales_def=None, scale_args=None, scale_prefix=None, **other_url_params +): if scale_prefix is None: - scale_prefix = 'scale_' + scale_prefix = "scale_" if scales_def is None: scales_def = [] if scale_args is None: scale_args = {} manifest = [] for label, cls in scales_def: - url_params = {scale_prefix+'name': cls.name} - url_params.update({scale_prefix+key: value - for key, value in scale_args.items()}) + url_params = {scale_prefix + "name": cls.name} + url_params.update( + {scale_prefix + key: value for key, value in scale_args.items()} + ) url_params.update(other_url_params) - manifest.append(dict( - label=label, - url_params=url_params, - )) + manifest.append(dict(label=label, url_params=url_params)) return manifest -def last_stats_manifest(scales_def=None, scale_args=None, scale_prefix=None, - **url_params): +def last_stats_manifest( + scales_def=None, scale_args=None, scale_prefix=None, **url_params +): scales_def = [ - ('Derniers mois', MonthScale, ), - ('Dernières semaines', WeekScale, ), - ('Derniers jours', DayScale, ), + ("Derniers mois", MonthScale), + ("Dernières semaines", WeekScale), + ("Derniers jours", DayScale), ] if scale_args is None: scale_args = {} - scale_args.update(dict( - last=True, - n_steps=7, - )) - return stat_manifest(scales_def=scales_def, scale_args=scale_args, - scale_prefix=scale_prefix, **url_params) + scale_args.update(dict(last=True, n_steps=7)) + return stat_manifest( + scales_def=scales_def, + scale_args=scale_args, + scale_prefix=scale_prefix, + **url_params + ) # Étant donné un queryset d'operations # rend la somme des article_nb def tot_ventes(queryset): - res = queryset.aggregate(Sum('article_nb'))['article_nb__sum'] + res = queryset.aggregate(Sum("article_nb"))["article_nb__sum"] return res and res or 0 class ScaleMixin(object): - scale_args_prefix = 'scale_' + scale_args_prefix = "scale_" def get_scale_args(self, params=None, prefix=None): """Retrieve scale args from params. @@ -282,26 +278,25 @@ class ScaleMixin(object): scale_args = {} - name = params.get(prefix+'name', None) + name = params.get(prefix + "name", None) if name is not None: - scale_args['name'] = name + scale_args["name"] = name - n_steps = params.get(prefix+'n_steps', None) + n_steps = params.get(prefix + "n_steps", None) if n_steps is not None: - scale_args['n_steps'] = int(n_steps) + scale_args["n_steps"] = int(n_steps) - begin = params.get(prefix+'begin', None) + begin = params.get(prefix + "begin", None) if begin is not None: - scale_args['begin'] = dateutil_parse(begin) + scale_args["begin"] = dateutil_parse(begin) - end = params.get(prefix+'send', None) + end = params.get(prefix + "send", None) if end is not None: - scale_args['end'] = dateutil_parse(end) + scale_args["end"] = dateutil_parse(end) - last = params.get(prefix+'last', None) + last = params.get(prefix + "last", None) if last is not None: - scale_args['last'] = ( - last in ['true', 'True', '1'] and True or False) + scale_args["last"] = last in ["true", "True", "1"] and True or False return scale_args @@ -309,7 +304,7 @@ class ScaleMixin(object): context = super().get_context_data(*args, **kwargs) scale_args = self.get_scale_args() - scale_name = scale_args.pop('name', None) + scale_name = scale_args.pop("name", None) scale_cls = Scale.by_name(scale_name) if scale_cls is None: @@ -318,7 +313,7 @@ class ScaleMixin(object): scale = scale_cls(**scale_args) self.scale = scale - context['labels'] = scale.get_labels() + context["labels"] = scale.get_labels() return context def get_default_scale(self): diff --git a/kfet/templatetags/dictionary_extras.py b/kfet/templatetags/dictionary_extras.py index fafaad8d..181fd012 100644 --- a/kfet/templatetags/dictionary_extras.py +++ b/kfet/templatetags/dictionary_extras.py @@ -1,5 +1,6 @@ from django.template.defaulttags import register + @register.filter def get_item(dictionary, key): return dictionary.get(key) diff --git a/kfet/templatetags/kfet_tags.py b/kfet/templatetags/kfet_tags.py index 68b74738..4c26dd17 100644 --- a/kfet/templatetags/kfet_tags.py +++ b/kfet/templatetags/kfet_tags.py @@ -6,10 +6,9 @@ from django.utils.safestring import mark_safe from ..utils import to_ukf - register = template.Library() -register.filter('ukf', to_ukf) +register.filter("ukf", to_ukf) @register.filter() diff --git a/kfet/tests/test_config.py b/kfet/tests/test_config.py index 43497ca8..02ff1ff0 100644 --- a/kfet/tests/test_config.py +++ b/kfet/tests/test_config.py @@ -1,10 +1,9 @@ from decimal import Decimal +import djconfig from django.test import TestCase from django.utils import timezone -import djconfig - from gestioncof.models import User from kfet.config import kfet_config from kfet.models import Account @@ -18,18 +17,18 @@ class ConfigTest(TestCase): djconfig.reload_maybe() def test_get(self): - self.assertTrue(hasattr(kfet_config, 'subvention_cof')) + self.assertTrue(hasattr(kfet_config, "subvention_cof")) def test_subvention_cof(self): - reduction_cof = Decimal('20') - subvention_cof = Decimal('25') + reduction_cof = Decimal("20") + subvention_cof = Decimal("25") kfet_config.set(reduction_cof=reduction_cof) self.assertEqual(kfet_config.subvention_cof, subvention_cof) def test_set_decimal(self): """Test field of decimal type.""" - reduction_cof = Decimal('10') + reduction_cof = Decimal("10") # IUT kfet_config.set(reduction_cof=reduction_cof) # check @@ -37,9 +36,8 @@ class ConfigTest(TestCase): def test_set_modelinstance(self): """Test field of model instance type.""" - user = User.objects.create(username='foo_user') - account = Account.objects.create(trigramme='FOO', - cofprofile=user.profile) + user = User.objects.create(username="foo_user") + account = Account.objects.create(trigramme="FOO", cofprofile=user.profile) # IUT kfet_config.set(addcost_for=account) # check diff --git a/kfet/tests/test_forms.py b/kfet/tests/test_forms.py index e946d39d..37b05e74 100644 --- a/kfet/tests/test_forms.py +++ b/kfet/tests/test_forms.py @@ -11,14 +11,14 @@ from .utils import create_user class KPsulCheckoutFormTests(TestCase): - def setUp(self): self.now = timezone.now() user = create_user() self.c1 = Checkout.objects.create( - name='C1', balance=10, + name="C1", + balance=10, created_by=user.profile.account_kfet, valid_from=self.now, valid_to=self.now + datetime.timedelta(days=1), @@ -27,13 +27,12 @@ class KPsulCheckoutFormTests(TestCase): self.form = KPsulCheckoutForm() def test_checkout(self): - checkout_f = self.form.fields['checkout'] - self.assertListEqual(list(checkout_f.choices), [ - ('', '---------'), - (self.c1.pk, 'C1'), - ]) + checkout_f = self.form.fields["checkout"] + self.assertListEqual( + list(checkout_f.choices), [("", "---------"), (self.c1.pk, "C1")] + ) - @mock.patch('django.utils.timezone.now') + @mock.patch("django.utils.timezone.now") def test_checkout_valid(self, mock_now): """ Checkout are filtered using the current datetime. @@ -44,5 +43,5 @@ class KPsulCheckoutFormTests(TestCase): form = KPsulCheckoutForm() - checkout_f = form.fields['checkout'] - self.assertListEqual(list(checkout_f.choices), [('', '---------')]) + checkout_f = form.fields["checkout"] + self.assertListEqual(list(checkout_f.choices), [("", "---------")]) diff --git a/kfet/tests/test_models.py b/kfet/tests/test_models.py index 727cac4e..7ce6605c 100644 --- a/kfet/tests/test_models.py +++ b/kfet/tests/test_models.py @@ -12,26 +12,24 @@ User = get_user_model() class AccountTests(TestCase): - def setUp(self): - self.account = Account(trigramme='000') - self.account.save({'username': 'user'}) + self.account = Account(trigramme="000") + self.account.save({"username": "user"}) def test_password(self): - self.account.change_pwd('anna') + self.account.change_pwd("anna") self.account.save() - self.assertEqual(Account.objects.get_by_password('anna'), self.account) + self.assertEqual(Account.objects.get_by_password("anna"), self.account) with self.assertRaises(Account.DoesNotExist): Account.objects.get_by_password(None) with self.assertRaises(Account.DoesNotExist): - Account.objects.get_by_password('bernard') + Account.objects.get_by_password("bernard") class CheckoutTests(TestCase): - def setUp(self): self.now = timezone.now() diff --git a/kfet/tests/test_statistic.py b/kfet/tests/test_statistic.py index 93de27a0..f0ed7f74 100644 --- a/kfet/tests/test_statistic.py +++ b/kfet/tests/test_statistic.py @@ -1,14 +1,13 @@ from unittest.mock import patch -from django.test import TestCase, Client -from django.contrib.auth.models import User, Permission +from django.contrib.auth.models import Permission, User +from django.test import Client, TestCase from kfet.models import Account, Article, ArticleCategory class TestStats(TestCase): - - @patch('gestioncof.signals.messages') + @patch("gestioncof.signals.messages") def test_user_stats(self, mock_messages): """ Checks that we can get the stat-related pages without any problem. @@ -28,8 +27,7 @@ class TestStats(TestCase): Account.objects.create(trigramme="BAR", cofprofile=user2.profile) article = Article.objects.create( - name="article", - category=ArticleCategory.objects.create(name="C") + name="article", category=ArticleCategory.objects.create(name="C") ) # Each user have its own client @@ -43,12 +41,17 @@ class TestStats(TestCase): user_urls = [ "/k-fet/accounts/FOO/stat/operations/list", "/k-fet/accounts/FOO/stat/operations?{}".format( - '&'.join(["scale=day", - "types=['purchase']", - "scale_args={'n_steps':+7,+'last':+True}", - "format=json"])), + "&".join( + [ + "scale=day", + "types=['purchase']", + "scale_args={'n_steps':+7,+'last':+True}", + "format=json", + ] + ) + ), "/k-fet/accounts/FOO/stat/balance/list", - "/k-fet/accounts/FOO/stat/balance?format=json" + "/k-fet/accounts/FOO/stat/balance?format=json", ] for url in user_urls: resp = client.get(url) @@ -60,7 +63,7 @@ class TestStats(TestCase): # receives a Redirect response articles_urls = [ "/k-fet/articles/{}/stat/sales/list".format(article.pk), - "/k-fet/articles/{}/stat/sales".format(article.pk) + "/k-fet/articles/{}/stat/sales".format(article.pk), ] for url in articles_urls: resp = client.get(url) diff --git a/kfet/tests/test_tests_utils.py b/kfet/tests/test_tests_utils.py index 8308bd5b..45ca2348 100644 --- a/kfet/tests/test_tests_utils.py +++ b/kfet/tests/test_tests_utils.py @@ -7,86 +7,79 @@ from gestioncof.models import CofProfile from ..models import Account from .testcases import TestCaseMixin -from .utils import ( - create_user, create_team, create_root, get_perms, user_add_perms, -) - +from .utils import create_root, create_team, create_user, get_perms, user_add_perms User = get_user_model() class UserHelpersTests(TestCaseMixin, TestCase): - def test_create_user(self): """create_user creates a basic user and its account.""" u = create_user() a = u.profile.account_kfet - self.assertInstanceExpected(u, { - 'get_full_name': 'first last', - 'username': 'user', - }) + self.assertInstanceExpected( + u, {"get_full_name": "first last", "username": "user"} + ) self.assertFalse(u.user_permissions.exists()) - self.assertEqual('000', a.trigramme) + self.assertEqual("000", a.trigramme) def test_create_team(self): u = create_team() a = u.profile.account_kfet - self.assertInstanceExpected(u, { - 'get_full_name': 'team member', - 'username': 'team', - }) - self.assertTrue(u.has_perm('kfet.is_team')) + self.assertInstanceExpected( + u, {"get_full_name": "team member", "username": "team"} + ) + self.assertTrue(u.has_perm("kfet.is_team")) - self.assertEqual('100', a.trigramme) + self.assertEqual("100", a.trigramme) def test_create_root(self): u = create_root() a = u.profile.account_kfet - self.assertInstanceExpected(u, { - 'get_full_name': 'super user', - 'username': 'root', - 'is_superuser': True, - 'is_staff': True, - }) + self.assertInstanceExpected( + u, + { + "get_full_name": "super user", + "username": "root", + "is_superuser": True, + "is_staff": True, + }, + ) - self.assertEqual('200', a.trigramme) + self.assertEqual("200", a.trigramme) class PermHelpersTest(TestCaseMixin, TestCase): - def setUp(self): cts = ContentType.objects.get_for_models(Account, CofProfile) self.perm1 = Permission.objects.create( - content_type=cts[Account], - codename='test_perm', - name='Perm for test', + content_type=cts[Account], codename="test_perm", name="Perm for test" ) self.perm2 = Permission.objects.create( content_type=cts[CofProfile], - codename='another_test_perm', - name='Another one', + codename="another_test_perm", + name="Another one", ) self.perm_team = Permission.objects.get( - content_type__app_label='kfet', - codename='is_team', + content_type__app_label="kfet", codename="is_team" ) def test_get_perms(self): - perms = get_perms('kfet.test_perm', 'gestioncof.another_test_perm') - self.assertDictEqual(perms, { - 'kfet.test_perm': self.perm1, - 'gestioncof.another_test_perm': self.perm2, - }) + perms = get_perms("kfet.test_perm", "gestioncof.another_test_perm") + self.assertDictEqual( + perms, + {"kfet.test_perm": self.perm1, "gestioncof.another_test_perm": self.perm2}, + ) def test_user_add_perms(self): - user = User.objects.create_user(username='user', password='user') + user = User.objects.create_user(username="user", password="user") user.user_permissions.add(self.perm1) - user_add_perms(user, ['kfet.is_team', 'gestioncof.another_test_perm']) + user_add_perms(user, ["kfet.is_team", "gestioncof.another_test_perm"]) self.assertQuerysetEqual( user.user_permissions.all(), diff --git a/kfet/tests/test_views.py b/kfet/tests/test_views.py index 28599937..bd57b6f8 100644 --- a/kfet/tests/test_views.py +++ b/kfet/tests/test_views.py @@ -10,20 +10,33 @@ from django.utils import timezone from ..config import kfet_config from ..models import ( - Account, AccountNegative, Article, ArticleCategory, Checkout, - CheckoutStatement, Inventory, InventoryArticle, Operation, OperationGroup, - Order, OrderArticle, Supplier, SupplierArticle, Transfer, TransferGroup, + Account, + AccountNegative, + Article, + ArticleCategory, + Checkout, + CheckoutStatement, + Inventory, + InventoryArticle, + Operation, + OperationGroup, + Order, + OrderArticle, + Supplier, + SupplierArticle, + Transfer, + TransferGroup, ) from .testcases import ViewTestCaseMixin from .utils import create_team, create_user, get_perms, user_add_perms class AccountListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account' - url_expected = '/k-fet/accounts/' + url_name = "kfet.account" + url_expected = "/k-fet/accounts/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): r = self.client.get(self.url) @@ -31,58 +44,53 @@ class AccountListViewTests(ViewTestCaseMixin, TestCase): class AccountValidFreeTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.is_validandfree.ajax' - url_expected = '/k-fet/accounts/is_validandfree' + url_name = "kfet.account.is_validandfree.ajax" + url_expected = "/k-fet/accounts/is_validandfree" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok_isvalid_isfree(self): """Upper case trigramme not taken is valid and free.""" - r = self.client.get(self.url, {'trigramme': 'AAA'}) - self.assertDictEqual(json.loads(r.content.decode('utf-8')), { - 'is_valid': True, - 'is_free': True, - }) + r = self.client.get(self.url, {"trigramme": "AAA"}) + self.assertDictEqual( + json.loads(r.content.decode("utf-8")), {"is_valid": True, "is_free": True} + ) def test_ok_isvalid_notfree(self): """Already taken trigramme is not free, but valid.""" - r = self.client.get(self.url, {'trigramme': '000'}) - self.assertDictEqual(json.loads(r.content.decode('utf-8')), { - 'is_valid': True, - 'is_free': False, - }) + r = self.client.get(self.url, {"trigramme": "000"}) + self.assertDictEqual( + json.loads(r.content.decode("utf-8")), {"is_valid": True, "is_free": False} + ) def test_ok_notvalid_isfree(self): """Lower case if forbidden but free.""" - r = self.client.get(self.url, {'trigramme': 'aaa'}) - self.assertDictEqual(json.loads(r.content.decode('utf-8')), { - 'is_valid': False, - 'is_free': True, - }) + r = self.client.get(self.url, {"trigramme": "aaa"}) + self.assertDictEqual( + json.loads(r.content.decode("utf-8")), {"is_valid": False, "is_free": True} + ) class AccountCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.create' - url_expected = '/k-fet/accounts/new' + url_name = "kfet.account.create" + url_expected = "/k-fet/accounts/new" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] post_data = { - 'trigramme': 'AAA', - 'username': 'plopplopplop', - 'first_name': 'first', - 'last_name': 'last', - 'email': 'email@domain.net', + "trigramme": "AAA", + "username": "plopplopplop", + "first_name": "first", + "last_name": "last", + "email": "email@domain.net", } def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.add_account']), - } + return {"team1": create_team("team1", "101", perms=["kfet.add_account"])} def test_get_ok(self): r = self.client.get(self.url) @@ -90,18 +98,17 @@ class AccountCreateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.account.create')) + self.assertRedirects(r, reverse("kfet.account.create")) - account = Account.objects.get(trigramme='AAA') + account = Account.objects.get(trigramme="AAA") - self.assertInstanceExpected(account, { - 'username': 'plopplopplop', - 'first_name': 'first', - 'last_name': 'last', - }) + self.assertInstanceExpected( + account, + {"username": "plopplopplop", "first_name": "first", "last_name": "last"}, + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -109,132 +116,128 @@ class AccountCreateViewTests(ViewTestCaseMixin, TestCase): class AccountCreateAjaxViewTests(ViewTestCaseMixin, TestCase): - urls_conf = [{ - 'name': 'kfet.account.create.fromuser', - 'kwargs': {'username': 'user'}, - 'expected': '/k-fet/accounts/new/user/user', - }, { - 'name': 'kfet.account.create.fromclipper', - 'kwargs': { - 'login_clipper': 'myclipper', - 'fullname': 'first last1 last2', + urls_conf = [ + { + "name": "kfet.account.create.fromuser", + "kwargs": {"username": "user"}, + "expected": "/k-fet/accounts/new/user/user", }, - 'expected': ( - '/k-fet/accounts/new/clipper/myclipper/first%20last1%20last2' - ), - }, { - 'name': 'kfet.account.create.empty', - 'expected': '/k-fet/accounts/new/empty', - }] + { + "name": "kfet.account.create.fromclipper", + "kwargs": {"login_clipper": "myclipper", "fullname": "first last1 last2"}, + "expected": ("/k-fet/accounts/new/clipper/myclipper/first%20last1%20last2"), + }, + {"name": "kfet.account.create.empty", "expected": "/k-fet/accounts/new/empty"}, + ] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_fromuser(self): r = self.client.get(self.t_urls[0]) self.assertEqual(r.status_code, 200) - user = self.users['user'] + user = self.users["user"] - self.assertEqual(r.context['user_form'].instance, user) - self.assertEqual(r.context['cof_form'].instance, user.profile) - self.assertIn('account_form', r.context) + self.assertEqual(r.context["user_form"].instance, user) + self.assertEqual(r.context["cof_form"].instance, user.profile) + self.assertIn("account_form", r.context) def test_fromclipper(self): r = self.client.get(self.t_urls[1]) self.assertEqual(r.status_code, 200) - self.assertIn('user_form', r.context) - self.assertIn('cof_form', r.context) - self.assertIn('account_form', r.context) + self.assertIn("user_form", r.context) + self.assertIn("cof_form", r.context) + self.assertIn("account_form", r.context) def test_empty(self): r = self.client.get(self.t_urls[2]) self.assertEqual(r.status_code, 200) - self.assertIn('user_form', r.context) - self.assertIn('cof_form', r.context) - self.assertIn('account_form', r.context) + self.assertIn("user_form", r.context) + self.assertIn("cof_form", r.context) + self.assertIn("account_form", r.context) class AccountCreateAutocompleteViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.create.autocomplete' - url_expected = '/k-fet/autocomplete/account_new' + url_name = "kfet.account.create.autocomplete" + url_expected = "/k-fet/autocomplete/account_new" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): - r = self.client.get(self.url, {'q': 'first'}) + r = self.client.get(self.url, {"q": "first"}) self.assertEqual(r.status_code, 200) - self.assertEqual(len(r.context['users_notcof']), 0) - self.assertEqual(len(r.context['users_cof']), 0) - self.assertSetEqual(set(r.context['kfet']), set([ - (self.accounts['user'], self.users['user']), - ])) + self.assertEqual(len(r.context["users_notcof"]), 0) + self.assertEqual(len(r.context["users_cof"]), 0) + self.assertSetEqual( + set(r.context["kfet"]), set([(self.accounts["user"], self.users["user"])]) + ) class AccountSearchViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.search.autocomplete' - url_expected = '/k-fet/autocomplete/account_search' + url_name = "kfet.account.search.autocomplete" + url_expected = "/k-fet/autocomplete/account_search" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): - r = self.client.get(self.url, {'q': 'first'}) + r = self.client.get(self.url, {"q": "first"}) self.assertEqual(r.status_code, 200) - self.assertSetEqual(set(r.context['accounts']), set([ - ('000', 'first last'), - ])) + self.assertSetEqual(set(r.context["accounts"]), set([("000", "first last")])) class AccountReadViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.read' - url_kwargs = {'trigramme': '001'} - url_expected = '/k-fet/accounts/001' + url_name = "kfet.account.read" + url_kwargs = {"trigramme": "001"} + url_expected = "/k-fet/accounts/001" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def get_users_extra(self): - return { - 'user1': create_user('user1', '001'), - } + return {"user1": create_user("user1", "001")} def setUp(self): super().setUp() - user1_acc = self.accounts['user1'] - team_acc = self.accounts['team'] + user1_acc = self.accounts["user1"] + team_acc = self.accounts["team"] # Dummy operations and operation groups checkout = Checkout.objects.create( - created_by=team_acc, name="checkout", + created_by=team_acc, + name="checkout", valid_from=timezone.now(), - valid_to=timezone.now() + timezone.timedelta(days=365) + valid_to=timezone.now() + timezone.timedelta(days=365), ) opeg_data = [ - (timezone.now(), Decimal('10')), - (timezone.now() - timezone.timedelta(days=3), Decimal('3')), + (timezone.now(), Decimal("10")), + (timezone.now() - timezone.timedelta(days=3), Decimal("3")), ] - OperationGroup.objects.bulk_create([ - OperationGroup( - on_acc=user1_acc, checkout=checkout, at=at, is_cof=False, - amount=amount - ) - for (at, amount) in opeg_data - ]) + OperationGroup.objects.bulk_create( + [ + OperationGroup( + on_acc=user1_acc, + checkout=checkout, + at=at, + is_cof=False, + amount=amount, + ) + for (at, amount) in opeg_data + ] + ) self.operation_groups = OperationGroup.objects.order_by("-amount") Operation.objects.create( group=self.operation_groups[0], type=Operation.PURCHASE, - amount=Decimal('10') + amount=Decimal("10"), ) Operation.objects.create( - group=self.operation_groups[1], - type=Operation.PURCHASE, - amount=Decimal('3') + group=self.operation_groups[1], type=Operation.PURCHASE, amount=Decimal("3") ) def test_ok(self): @@ -244,44 +247,42 @@ class AccountReadViewTests(ViewTestCaseMixin, TestCase): def test_ok_self(self): client = Client() - client.login(username='user1', password='user1') + client.login(username="user1", password="user1") r = client.get(self.url) self.assertEqual(r.status_code, 200) class AccountUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.update' - url_kwargs = {'trigramme': '001'} - url_expected = '/k-fet/accounts/001/edit' + url_name = "kfet.account.update" + url_kwargs = {"trigramme": "001"} + url_expected = "/k-fet/accounts/001/edit" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] post_data = { # User - 'first_name': 'The first', - 'last_name': 'The last', - 'email': '', + "first_name": "The first", + "last_name": "The last", + "email": "", # Group - 'groups[]': [], + "groups[]": [], # Account - 'trigramme': '051', - 'nickname': '', - 'promo': '', + "trigramme": "051", + "nickname": "", + "promo": "", # 'is_frozen': not checked # Account password - 'pwd1': '', - 'pwd2': '', + "pwd1": "", + "pwd2": "", } def get_users_extra(self): return { - 'user1': create_user('user1', '001'), - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_account', - ]), + "user1": create_user("user1", "001"), + "team1": create_team("team1", "101", perms=["kfet.change_account"]), } def test_get_ok(self): @@ -290,45 +291,40 @@ class AccountUpdateViewTests(ViewTestCaseMixin, TestCase): def test_get_ok_self(self): client = Client() - client.login(username='user1', password='user1') + client.login(username="user1", password="user1") r = client.get(self.url) self.assertEqual(r.status_code, 200) def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.account.read', args=['051'])) + self.assertRedirects(r, reverse("kfet.account.read", args=["051"])) - self.accounts['user1'].refresh_from_db() - self.users['user1'].refresh_from_db() + self.accounts["user1"].refresh_from_db() + self.users["user1"].refresh_from_db() - self.assertInstanceExpected(self.accounts['user1'], { - 'first_name': 'The first', - 'last_name': 'The last', - 'trigramme': '051', - }) + self.assertInstanceExpected( + self.accounts["user1"], + {"first_name": "The first", "last_name": "The last", "trigramme": "051"}, + ) def test_post_ok_self(self): client = Client() - client.login(username='user1', password='user1') + client.login(username="user1", password="user1") - post_data = { - 'first_name': 'The first', - 'last_name': 'The last', - } + post_data = {"first_name": "The first", "last_name": "The last"} r = client.post(self.url, post_data) - self.assertRedirects(r, reverse('kfet.account.read', args=['001'])) + self.assertRedirects(r, reverse("kfet.account.read", args=["001"])) - self.accounts['user1'].refresh_from_db() - self.users['user1'].refresh_from_db() + self.accounts["user1"].refresh_from_db() + self.users["user1"].refresh_from_db() - self.assertInstanceExpected(self.accounts['user1'], { - 'first_name': 'The first', - 'last_name': 'The last', - }) + self.assertInstanceExpected( + self.accounts["user1"], {"first_name": "The first", "last_name": "The last"} + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -336,63 +332,54 @@ class AccountUpdateViewTests(ViewTestCaseMixin, TestCase): class AccountGroupListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.group' - url_expected = '/k-fet/accounts/groups' + url_name = "kfet.account.group" + url_expected = "/k-fet/accounts/groups" - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.manage_perms']), - } + return {"team1": create_team("team1", "101", perms=["kfet.manage_perms"])} def setUp(self): super().setUp() - self.group1 = Group.objects.create(name='K-Fêt - Group1') - self.group2 = Group.objects.create(name='K-Fêt - Group2') + self.group1 = Group.objects.create(name="K-Fêt - Group1") + self.group2 = Group.objects.create(name="K-Fêt - Group2") def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['groups'], - map(repr, [self.group1, self.group2]), - ordered=False, + r.context["groups"], map(repr, [self.group1, self.group2]), ordered=False ) class AccountGroupCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.group.create' - url_expected = '/k-fet/accounts/groups/new' + url_name = "kfet.account.group.create" + url_expected = "/k-fet/accounts/groups/new" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.manage_perms']), - } + return {"team1": create_team("team1", "101", perms=["kfet.manage_perms"])} @property def post_data(self): return { - 'name': 'The Group', - 'permissions': [ - str(self.perms['kfet.is_team'].pk), - str(self.perms['kfet.manage_perms'].pk), + "name": "The Group", + "permissions": [ + str(self.perms["kfet.is_team"].pk), + str(self.perms["kfet.manage_perms"].pk), ], } def setUp(self): super().setUp() - self.perms = get_perms( - 'kfet.is_team', - 'kfet.manage_perms', - ) + self.perms = get_perms("kfet.is_team", "kfet.manage_perms") def test_get_ok(self): r = self.client.get(self.url) @@ -400,58 +387,50 @@ class AccountGroupCreateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): r = self.client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.account.group')) + self.assertRedirects(r, reverse("kfet.account.group")) - group = Group.objects.get(name='K-Fêt The Group') + group = Group.objects.get(name="K-Fêt The Group") self.assertQuerysetEqual( group.permissions.all(), - map(repr, [ - self.perms['kfet.is_team'], - self.perms['kfet.manage_perms'], - ]), + map(repr, [self.perms["kfet.is_team"], self.perms["kfet.manage_perms"]]), ordered=False, ) class AccountGroupUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.group.update' + url_name = "kfet.account.group.update" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] @property def url_kwargs(self): - return {'pk': self.group.pk} + return {"pk": self.group.pk} @property def url_expected(self): - return '/k-fet/accounts/groups/{}/edit'.format(self.group.pk) + return "/k-fet/accounts/groups/{}/edit".format(self.group.pk) def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.manage_perms']), - } + return {"team1": create_team("team1", "101", perms=["kfet.manage_perms"])} @property def post_data(self): return { - 'name': 'The Group', - 'permissions': [ - str(self.perms['kfet.is_team'].pk), - str(self.perms['kfet.manage_perms'].pk), + "name": "The Group", + "permissions": [ + str(self.perms["kfet.is_team"].pk), + str(self.perms["kfet.manage_perms"].pk), ], } def setUp(self): super().setUp() - self.perms = get_perms( - 'kfet.is_team', - 'kfet.manage_perms', - ) - self.group = Group.objects.create(name='K-Fêt - Group') + self.perms = get_perms("kfet.is_team", "kfet.manage_perms") + self.group = Group.objects.create(name="K-Fêt - Group") self.group.permissions.set(self.perms.values()) def test_get_ok(self): @@ -460,36 +439,31 @@ class AccountGroupUpdateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): r = self.client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.account.group')) + self.assertRedirects(r, reverse("kfet.account.group")) self.group.refresh_from_db() - self.assertEqual(self.group.name, 'K-Fêt The Group') + self.assertEqual(self.group.name, "K-Fêt The Group") self.assertQuerysetEqual( self.group.permissions.all(), - map(repr, [ - self.perms['kfet.is_team'], - self.perms['kfet.manage_perms'], - ]), + map(repr, [self.perms["kfet.is_team"], self.perms["kfet.manage_perms"]]), ordered=False, ) class AccountNegativeListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.negative' - url_expected = '/k-fet/accounts/negatives' + url_name = "kfet.account.negative" + url_expected = "/k-fet/accounts/negatives" - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.view_negs']), - } + return {"team1": create_team("team1", "101", perms=["kfet.view_negs"])} def setUp(self): super().setUp() - account = self.accounts['user'] + account = self.accounts["user"] account.balance = -5 account.save() account.update_negative() @@ -498,82 +472,86 @@ class AccountNegativeListViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['negatives'], - map(repr, [self.accounts['user'].negative]), + r.context["negatives"], + map(repr, [self.accounts["user"].negative]), ordered=False, ) class AccountStatOperationListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.stat.operation.list' - url_kwargs = {'trigramme': '001'} - url_expected = '/k-fet/accounts/001/stat/operations/list' + url_name = "kfet.account.stat.operation.list" + url_kwargs = {"trigramme": "001"} + url_expected = "/k-fet/accounts/001/stat/operations/list" - auth_user = 'user1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "user1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return {'user1': create_user('user1', '001')} + return {"user1": create_user("user1", "001")} def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - content = json.loads(r.content.decode('utf-8')) + content = json.loads(r.content.decode("utf-8")) - base_url = reverse('kfet.account.stat.operation', args=['001']) + base_url = reverse("kfet.account.stat.operation", args=["001"]) - expected_stats = [{ - 'label': 'Derniers mois', - 'url': { - 'path': base_url, - 'query': { - 'scale_n_steps': ['7'], - 'scale_name': ['month'], - 'types': ["['purchase']"], - 'scale_last': ['True'], + expected_stats = [ + { + "label": "Derniers mois", + "url": { + "path": base_url, + "query": { + "scale_n_steps": ["7"], + "scale_name": ["month"], + "types": ["['purchase']"], + "scale_last": ["True"], + }, }, }, - }, { - 'label': 'Dernières semaines', - 'url': { - 'path': base_url, - 'query': { - 'scale_n_steps': ['7'], - 'scale_name': ['week'], - 'types': ["['purchase']"], - 'scale_last': ['True'], + { + "label": "Dernières semaines", + "url": { + "path": base_url, + "query": { + "scale_n_steps": ["7"], + "scale_name": ["week"], + "types": ["['purchase']"], + "scale_last": ["True"], + }, }, }, - }, { - 'label': 'Derniers jours', - 'url': { - 'path': base_url, - 'query': { - 'scale_n_steps': ['7'], - 'scale_name': ['day'], - 'types': ["['purchase']"], - 'scale_last': ['True'], + { + "label": "Derniers jours", + "url": { + "path": base_url, + "query": { + "scale_n_steps": ["7"], + "scale_name": ["day"], + "types": ["['purchase']"], + "scale_last": ["True"], + }, }, }, - }] + ] - for stat, expected in zip(content['stats'], expected_stats): - expected_url = expected.pop('url') - self.assertUrlsEqual(stat['url'], expected_url) + for stat, expected in zip(content["stats"], expected_stats): + expected_url = expected.pop("url") + self.assertUrlsEqual(stat["url"], expected_url) self.assertDictContainsSubset(expected, stat) class AccountStatOperationViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.stat.operation' - url_kwargs = {'trigramme': '001'} - url_expected = '/k-fet/accounts/001/stat/operations' + url_name = "kfet.account.stat.operation" + url_kwargs = {"trigramme": "001"} + url_expected = "/k-fet/accounts/001/stat/operations" - auth_user = 'user1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "user1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return {'user1': create_user('user1', '001')} + return {"user1": create_user("user1", "001")} def test_ok(self): r = self.client.get(self.url) @@ -581,69 +559,60 @@ class AccountStatOperationViewTests(ViewTestCaseMixin, TestCase): class AccountStatBalanceListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.stat.balance.list' - url_kwargs = {'trigramme': '001'} - url_expected = '/k-fet/accounts/001/stat/balance/list' + url_name = "kfet.account.stat.balance.list" + url_kwargs = {"trigramme": "001"} + url_expected = "/k-fet/accounts/001/stat/balance/list" - auth_user = 'user1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "user1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return {'user1': create_user('user1', '001')} + return {"user1": create_user("user1", "001")} def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - content = json.loads(r.content.decode('utf-8')) + content = json.loads(r.content.decode("utf-8")) - base_url = reverse('kfet.account.stat.balance', args=['001']) + base_url = reverse("kfet.account.stat.balance", args=["001"]) - expected_stats = [{ - 'label': 'Tout le temps', - 'url': base_url, - }, { - 'label': '1 an', - 'url': { - 'path': base_url, - 'query': {'last_days': ['365']}, + expected_stats = [ + {"label": "Tout le temps", "url": base_url}, + { + "label": "1 an", + "url": {"path": base_url, "query": {"last_days": ["365"]}}, }, - }, { - 'label': '6 mois', - 'url': { - 'path': base_url, - 'query': {'last_days': ['183']}, + { + "label": "6 mois", + "url": {"path": base_url, "query": {"last_days": ["183"]}}, }, - }, { - 'label': '3 mois', - 'url': { - 'path': base_url, - 'query': {'last_days': ['90']}, + { + "label": "3 mois", + "url": {"path": base_url, "query": {"last_days": ["90"]}}, }, - }, { - 'label': '30 jours', - 'url': { - 'path': base_url, - 'query': {'last_days': ['30']}, + { + "label": "30 jours", + "url": {"path": base_url, "query": {"last_days": ["30"]}}, }, - }] + ] - for stat, expected in zip(content['stats'], expected_stats): - expected_url = expected.pop('url') - self.assertUrlsEqual(stat['url'], expected_url) + for stat, expected in zip(content["stats"], expected_stats): + expected_url = expected.pop("url") + self.assertUrlsEqual(stat["url"], expected_url) self.assertDictContainsSubset(expected, stat) class AccountStatBalanceViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.stat.balance' - url_kwargs = {'trigramme': '001'} - url_expected = '/k-fet/accounts/001/stat/balance' + url_name = "kfet.account.stat.balance" + url_kwargs = {"trigramme": "001"} + url_expected = "/k-fet/accounts/001/stat/balance" - auth_user = 'user1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "user1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return {'user1': create_user('user1', '001')} + return {"user1": create_user("user1", "001")} def test_ok(self): r = self.client.get(self.url) @@ -651,23 +620,23 @@ class AccountStatBalanceViewTests(ViewTestCaseMixin, TestCase): class CheckoutListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkout' - url_expected = '/k-fet/checkouts/' + url_name = "kfet.checkout" + url_expected = "/k-fet/checkouts/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() self.checkout1 = Checkout.objects.create( - name='Checkout 1', - created_by=self.accounts['team'], + name="Checkout 1", + created_by=self.accounts["team"], valid_from=self.now, valid_to=self.now + timedelta(days=5), ) self.checkout2 = Checkout.objects.create( - name='Checkout 2', - created_by=self.accounts['team'], + name="Checkout 2", + created_by=self.accounts["team"], valid_from=self.now + timedelta(days=10), valid_to=self.now + timedelta(days=15), ) @@ -676,33 +645,31 @@ class CheckoutListViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['checkouts'], + r.context["checkouts"], map(repr, [self.checkout1, self.checkout2]), ordered=False, ) class CheckoutCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkout.create' - url_expected = '/k-fet/checkouts/new' + url_name = "kfet.checkout.create" + url_expected = "/k-fet/checkouts/new" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] post_data = { - 'name': 'Checkout', - 'valid_from': '2017-10-08 17:45:00', - 'valid_to': '2017-11-08 16:00:00', - 'balance': '3.14', + "name": "Checkout", + "valid_from": "2017-10-08 17:45:00", + "valid_to": "2017-11-08 16:00:00", + "balance": "3.14", # 'is_protected': not checked } def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.add_checkout']), - } + return {"team1": create_team("team1", "101", perms=["kfet.add_checkout"])} def test_get_ok(self): r = self.client.get(self.url) @@ -710,20 +677,23 @@ class CheckoutCreateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - checkout = Checkout.objects.get(name='Checkout') + checkout = Checkout.objects.get(name="Checkout") self.assertRedirects(r, checkout.get_absolute_url()) - self.assertInstanceExpected(checkout, { - 'name': 'Checkout', - 'valid_from': timezone.make_aware(datetime(2017, 10, 8, 17, 45)), - 'valid_to': timezone.make_aware(datetime(2017, 11, 8, 16, 00)), - 'balance': Decimal('3.14'), - 'is_protected': False, - }) + self.assertInstanceExpected( + checkout, + { + "name": "Checkout", + "valid_from": timezone.make_aware(datetime(2017, 10, 8, 17, 45)), + "valid_to": timezone.make_aware(datetime(2017, 11, 8, 16, 00)), + "balance": Decimal("3.14"), + "is_protected": False, + }, + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -731,28 +701,29 @@ class CheckoutCreateViewTests(ViewTestCaseMixin, TestCase): class CheckoutReadViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkout.read' + url_name = "kfet.checkout.read" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.checkout.pk} + return {"pk": self.checkout.pk} @property def url_expected(self): - return '/k-fet/checkouts/{}'.format(self.checkout.pk) + return "/k-fet/checkouts/{}".format(self.checkout.pk) def setUp(self): super().setUp() - with mock.patch('django.utils.timezone.now') as mock_now: + with mock.patch("django.utils.timezone.now") as mock_now: mock_now.return_value = self.now self.checkout = Checkout.objects.create( - name='Checkout', balance=Decimal('10'), - created_by=self.accounts['team'], + name="Checkout", + balance=Decimal("10"), + created_by=self.accounts["team"], valid_from=self.now, valid_to=self.now + timedelta(days=1), ) @@ -760,47 +731,43 @@ class CheckoutReadViewTests(ViewTestCaseMixin, TestCase): def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertEqual(r.context['checkout'], self.checkout) + self.assertEqual(r.context["checkout"], self.checkout) class CheckoutUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkout.update' + url_name = "kfet.checkout.update" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] post_data = { - 'name': 'Checkout updated', - 'valid_from': '2018-01-01 08:00:00', - 'valid_to': '2018-07-01 16:00:00', + "name": "Checkout updated", + "valid_from": "2018-01-01 08:00:00", + "valid_to": "2018-07-01 16:00:00", } @property def url_kwargs(self): - return {'pk': self.checkout.pk} + return {"pk": self.checkout.pk} @property def url_expected(self): - return '/k-fet/checkouts/{}/edit'.format(self.checkout.pk) + return "/k-fet/checkouts/{}/edit".format(self.checkout.pk) def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_checkout', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.change_checkout"])} def setUp(self): super().setUp() self.checkout = Checkout.objects.create( - name='Checkout', + name="Checkout", valid_from=self.now, valid_to=self.now + timedelta(days=5), - balance=Decimal('3.14'), + balance=Decimal("3.14"), is_protected=False, - created_by=self.accounts['team'], + created_by=self.accounts["team"], ) def test_get_ok(self): @@ -809,18 +776,21 @@ class CheckoutUpdateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) self.assertRedirects(r, self.checkout.get_absolute_url()) self.checkout.refresh_from_db() - self.assertInstanceExpected(self.checkout, { - 'name': 'Checkout updated', - 'valid_from': timezone.make_aware(datetime(2018, 1, 1, 8, 0, 0)), - 'valid_to': timezone.make_aware(datetime(2018, 7, 1, 16, 0, 0)), - }) + self.assertInstanceExpected( + self.checkout, + { + "name": "Checkout updated", + "valid_from": timezone.make_aware(datetime(2018, 1, 1, 8, 0, 0)), + "valid_to": timezone.make_aware(datetime(2018, 7, 1, 16, 0, 0)), + }, + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -828,29 +798,29 @@ class CheckoutUpdateViewTests(ViewTestCaseMixin, TestCase): class CheckoutStatementListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkoutstatement' - url_expected = '/k-fet/checkouts/statements/' + url_name = "kfet.checkoutstatement" + url_expected = "/k-fet/checkouts/statements/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() self.checkout1 = Checkout.objects.create( - created_by=self.accounts['team'], - name='Checkout 1', + created_by=self.accounts["team"], + name="Checkout 1", valid_from=self.now, valid_to=self.now + timedelta(days=5), ) self.checkout2 = Checkout.objects.create( - created_by=self.accounts['team'], - name='Checkout 2', + created_by=self.accounts["team"], + name="Checkout 2", valid_from=self.now + timedelta(days=10), valid_to=self.now + timedelta(days=15), ) self.statement1 = CheckoutStatement.objects.create( checkout=self.checkout1, - by=self.accounts['team'], + by=self.accounts["team"], balance_old=5, balance_new=0, amount_taken=5, @@ -860,63 +830,80 @@ class CheckoutStatementListViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - expected_statements = ( - list(self.checkout1.statements.all()) + - list(self.checkout2.statements.all()) + expected_statements = list(self.checkout1.statements.all()) + list( + self.checkout2.statements.all() ) self.assertQuerysetEqual( - r.context['checkoutstatements'], + r.context["checkoutstatements"], map(repr, expected_statements), ordered=False, ) class CheckoutStatementCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkoutstatement.create' + url_name = "kfet.checkoutstatement.create" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] post_data = { # Let - 'balance_001': 0, 'balance_002': 0, 'balance_005': 0, - 'balance_01': 0, 'balance_02': 0, 'balance_05': 0, - 'balance_1': 1, 'balance_2': 0, 'balance_5': 0, - 'balance_10': 1, 'balance_20': 0, 'balance_50': 0, - 'balance_100': 1, 'balance_200': 0, 'balance_500': 0, + "balance_001": 0, + "balance_002": 0, + "balance_005": 0, + "balance_01": 0, + "balance_02": 0, + "balance_05": 0, + "balance_1": 1, + "balance_2": 0, + "balance_5": 0, + "balance_10": 1, + "balance_20": 0, + "balance_50": 0, + "balance_100": 1, + "balance_200": 0, + "balance_500": 0, # Taken - 'taken_001': 0, 'taken_002': 0, 'taken_005': 0, - 'taken_01': 0, 'taken_02': 0, 'taken_05': 0, - 'taken_1': 2, 'taken_2': 0, 'taken_5': 0, - 'taken_10': 2, 'taken_20': 0, 'taken_50': 0, - 'taken_100': 2, 'taken_200': 0, 'taken_500': 0, - 'taken_cheque': 0, + "taken_001": 0, + "taken_002": 0, + "taken_005": 0, + "taken_01": 0, + "taken_02": 0, + "taken_05": 0, + "taken_1": 2, + "taken_2": 0, + "taken_5": 0, + "taken_10": 2, + "taken_20": 0, + "taken_50": 0, + "taken_100": 2, + "taken_200": 0, + "taken_500": 0, + "taken_cheque": 0, # 'not_count': not checked } @property def url_kwargs(self): - return {'pk_checkout': self.checkout.pk} + return {"pk_checkout": self.checkout.pk} @property def url_expected(self): - return '/k-fet/checkouts/{}/statements/add'.format(self.checkout.pk) + return "/k-fet/checkouts/{}/statements/add".format(self.checkout.pk) def get_users_extra(self): return { - 'team1': create_team('team1', '001', perms=[ - 'kfet.add_checkoutstatement', - ]), + "team1": create_team("team1", "001", perms=["kfet.add_checkoutstatement"]) } def setUp(self): super().setUp() self.checkout = Checkout.objects.create( - name='Checkout', - created_by=self.accounts['team'], + name="Checkout", + created_by=self.accounts["team"], balance=5, valid_from=self.now, valid_to=self.now + timedelta(days=5), @@ -926,29 +913,32 @@ class CheckoutStatementCreateViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - @mock.patch('django.utils.timezone.now') + @mock.patch("django.utils.timezone.now") def test_post_ok(self, mock_now): self.now += timedelta(days=2) mock_now.return_value = self.now client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) self.assertRedirects(r, self.checkout.get_absolute_url()) statement = CheckoutStatement.objects.get(at=self.now) - self.assertInstanceExpected(statement, { - 'by': self.accounts['team1'], - 'checkout': self.checkout, - 'balance_old': Decimal('5'), - 'balance_new': Decimal('111'), - 'amount_taken': Decimal('222'), - 'amount_error': Decimal('328'), - 'at': self.now, - 'not_count': False, - }) + self.assertInstanceExpected( + statement, + { + "by": self.accounts["team1"], + "checkout": self.checkout, + "balance_old": Decimal("5"), + "balance_new": Decimal("111"), + "amount_taken": Decimal("222"), + "amount_error": Decimal("328"), + "at": self.now, + "not_count": False, + }, + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -956,59 +946,65 @@ class CheckoutStatementCreateViewTests(ViewTestCaseMixin, TestCase): class CheckoutStatementUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.checkoutstatement.update' + url_name = "kfet.checkoutstatement.update" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] post_data = { - 'amount_taken': 3, - 'amount_error': 2, - 'balance_old': 8, - 'balance_new': 5, + "amount_taken": 3, + "amount_error": 2, + "balance_old": 8, + "balance_new": 5, # Taken - 'taken_001': 0, 'taken_002': 0, 'taken_005': 0, - 'taken_01': 0, 'taken_02': 0, 'taken_05': 0, - 'taken_1': 1, 'taken_2': 1, 'taken_5': 0, - 'taken_10': 0, 'taken_20': 0, 'taken_50': 0, - 'taken_100': 0, 'taken_200': 0, 'taken_500': 0, - 'taken_cheque': 0, + "taken_001": 0, + "taken_002": 0, + "taken_005": 0, + "taken_01": 0, + "taken_02": 0, + "taken_05": 0, + "taken_1": 1, + "taken_2": 1, + "taken_5": 0, + "taken_10": 0, + "taken_20": 0, + "taken_50": 0, + "taken_100": 0, + "taken_200": 0, + "taken_500": 0, + "taken_cheque": 0, } @property def url_kwargs(self): - return { - 'pk_checkout': self.checkout.pk, - 'pk': self.statement.pk, - } + return {"pk_checkout": self.checkout.pk, "pk": self.statement.pk} @property def url_expected(self): - return '/k-fet/checkouts/{pk_checkout}/statements/{pk}/edit'.format( - pk_checkout=self.checkout.pk, - pk=self.statement.pk, + return "/k-fet/checkouts/{pk_checkout}/statements/{pk}/edit".format( + pk_checkout=self.checkout.pk, pk=self.statement.pk ) def get_users_extra(self): return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_checkoutstatement', - ]), + "team1": create_team( + "team1", "101", perms=["kfet.change_checkoutstatement"] + ) } def setUp(self): super().setUp() self.checkout = Checkout.objects.create( - name='Checkout', - created_by=self.accounts['team'], + name="Checkout", + created_by=self.accounts["team"], balance=5, valid_from=self.now, valid_to=self.now + timedelta(days=5), ) self.statement = CheckoutStatement.objects.create( - by=self.accounts['team'], + by=self.accounts["team"], checkout=self.checkout, balance_new=5, balance_old=8, @@ -1020,27 +1016,30 @@ class CheckoutStatementUpdateViewTests(ViewTestCaseMixin, TestCase): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - @mock.patch('django.utils.timezone.now') + @mock.patch("django.utils.timezone.now") def test_post_ok(self, mock_now): self.now += timedelta(days=2) mock_now.return_value = self.now client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) self.assertRedirects(r, self.checkout.get_absolute_url()) self.statement.refresh_from_db() - self.assertInstanceExpected(self.statement, { - 'taken_1': 1, - 'taken_2': 1, - 'balance_new': 5, - 'balance_old': 8, - 'amount_error': 0, - 'amount_taken': 3, - }) + self.assertInstanceExpected( + self.statement, + { + "taken_1": 1, + "taken_2": 1, + "balance_new": 5, + "balance_old": 8, + "amount_error": 0, + "amount_taken": 3, + }, + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -1048,60 +1047,57 @@ class CheckoutStatementUpdateViewTests(ViewTestCaseMixin, TestCase): class ArticleCategoryListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.category' - url_expected = '/k-fet/categories/' + url_name = "kfet.category" + url_expected = "/k-fet/categories/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() - self.category1 = ArticleCategory.objects.create(name='Category 1') - self.category2 = ArticleCategory.objects.create(name='Category 2') + self.category1 = ArticleCategory.objects.create(name="Category 1") + self.category2 = ArticleCategory.objects.create(name="Category 2") def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['categories'], - map(repr, [self.category1, self.category2]), + r.context["categories"], map(repr, [self.category1, self.category2]) ) class ArticleCategoryUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.category.update' + url_name = "kfet.category.update" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.category.pk} + return {"pk": self.category.pk} @property def url_expected(self): - return '/k-fet/categories/{}/edit'.format(self.category.pk) + return "/k-fet/categories/{}/edit".format(self.category.pk) def get_users_extra(self): return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_articlecategory', - ]), + "team1": create_team("team1", "101", perms=["kfet.change_articlecategory"]) } @property def post_data(self): return { - 'name': 'The Category', + "name": "The Category", # 'has_addcost': not checked } def setUp(self): super().setUp() - self.category = ArticleCategory.objects.create(name='Category') + self.category = ArticleCategory.objects.create(name="Category") def test_get_ok(self): r = self.client.get(self.url) @@ -1109,17 +1105,16 @@ class ArticleCategoryUpdateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.category')) + self.assertRedirects(r, reverse("kfet.category")) self.category.refresh_from_db() - self.assertInstanceExpected(self.category, { - 'name': 'The Category', - 'has_addcost': False, - }) + self.assertInstanceExpected( + self.category, {"name": "The Category", "has_addcost": False} + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -1127,59 +1122,50 @@ class ArticleCategoryUpdateViewTests(ViewTestCaseMixin, TestCase): class ArticleListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.article' - url_expected = '/k-fet/articles/' + url_name = "kfet.article" + url_expected = "/k-fet/articles/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Category') - self.article1 = Article.objects.create( - name='Article 1', - category=category, - ) - self.article2 = Article.objects.create( - name='Article 2', - category=category, - ) + category = ArticleCategory.objects.create(name="Category") + self.article1 = Article.objects.create(name="Article 1", category=category) + self.article2 = Article.objects.create(name="Article 2", category=category) def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) self.assertQuerysetEqual( - r.context['articles'], - map(repr, [self.article1, self.article2]), + r.context["articles"], map(repr, [self.article1, self.article2]) ) class ArticleCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.article.create' - url_expected = '/k-fet/articles/new' + url_name = "kfet.article.create" + url_expected = "/k-fet/articles/new" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.add_article']), - } + return {"team1": create_team("team1", "101", perms=["kfet.add_article"])} @property def post_data(self): return { - 'name': 'Article', - 'category': self.category.pk, - 'stock': 5, - 'price': '2.5', + "name": "Article", + "category": self.category.pk, + "stock": 5, + "price": "2.5", } def setUp(self): super().setUp() - self.category = ArticleCategory.objects.create(name='Category') + self.category = ArticleCategory.objects.create(name="Category") def test_get_ok(self): r = self.client.get(self.url) @@ -1187,18 +1173,17 @@ class ArticleCreateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - article = Article.objects.get(name='Article') + article = Article.objects.get(name="Article") self.assertRedirects(r, article.get_absolute_url()) - self.assertInstanceExpected(article, { - 'name': 'Article', - 'category': self.category, - }) + self.assertInstanceExpected( + article, {"name": "Article", "category": self.category} + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -1206,76 +1191,69 @@ class ArticleCreateViewTests(ViewTestCaseMixin, TestCase): class ArticleReadViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.article.read' + url_name = "kfet.article.read" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.article.pk} + return {"pk": self.article.pk} @property def url_expected(self): - return '/k-fet/articles/{}'.format(self.article.pk) + return "/k-fet/articles/{}".format(self.article.pk) def setUp(self): super().setUp() self.article = Article.objects.create( - name='Article', - category=ArticleCategory.objects.create(name='Category'), + name="Article", + category=ArticleCategory.objects.create(name="Category"), stock=5, - price=Decimal('2.5'), + price=Decimal("2.5"), ) def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - self.assertEqual(r.context['article'], self.article) + self.assertEqual(r.context["article"], self.article) class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.article.update' + url_name = "kfet.article.update" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.article.pk} + return {"pk": self.article.pk} @property def url_expected(self): - return '/k-fet/articles/{}/edit'.format(self.article.pk) + return "/k-fet/articles/{}/edit".format(self.article.pk) def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_article', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.change_article"])} @property def post_data(self): return { - 'name': 'The Article', - 'category': self.article.category.pk, - 'is_sold': '1', - 'price': '3.5', - 'box_type': 'carton', + "name": "The Article", + "category": self.article.category.pk, + "is_sold": "1", + "price": "3.5", + "box_type": "carton", # 'hidden': not checked } def setUp(self): super().setUp() - self.category = ArticleCategory.objects.create(name='Category') + self.category = ArticleCategory.objects.create(name="Category") self.article = Article.objects.create( - name='Article', - category=self.category, - stock=5, - price=Decimal('2.5'), + name="Article", category=self.category, stock=5, price=Decimal("2.5") ) def test_get_ok(self): @@ -1284,7 +1262,7 @@ class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) @@ -1292,10 +1270,9 @@ class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase): self.article.refresh_from_db() - self.assertInstanceExpected(self.article, { - 'name': 'The Article', - 'price': Decimal('3.5'), - }) + self.assertInstanceExpected( + self.article, {"name": "The Article", "price": Decimal("3.5")} + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -1303,95 +1280,93 @@ class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase): class ArticleStatSalesListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.article.stat.sales.list' + url_name = "kfet.article.stat.sales.list" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.article.pk} + return {"pk": self.article.pk} @property def url_expected(self): - return '/k-fet/articles/{}/stat/sales/list'.format(self.article.pk) + return "/k-fet/articles/{}/stat/sales/list".format(self.article.pk) def setUp(self): super().setUp() self.article = Article.objects.create( - name='Article', - category=ArticleCategory.objects.create(name='Category'), + name="Article", category=ArticleCategory.objects.create(name="Category") ) def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - content = json.loads(r.content.decode('utf-8')) + content = json.loads(r.content.decode("utf-8")) - base_url = reverse('kfet.article.stat.sales', args=[self.article.pk]) + base_url = reverse("kfet.article.stat.sales", args=[self.article.pk]) expected_stats = [ { - 'label': 'Derniers mois', - 'url': { - 'path': base_url, - 'query': { - 'scale_n_steps': ['7'], - 'scale_name': ['month'], - 'scale_last': ['True'], + "label": "Derniers mois", + "url": { + "path": base_url, + "query": { + "scale_n_steps": ["7"], + "scale_name": ["month"], + "scale_last": ["True"], }, }, }, { - 'label': 'Dernières semaines', - 'url': { - 'path': base_url, - 'query': { - 'scale_n_steps': ['7'], - 'scale_name': ['week'], - 'scale_last': ['True'], + "label": "Dernières semaines", + "url": { + "path": base_url, + "query": { + "scale_n_steps": ["7"], + "scale_name": ["week"], + "scale_last": ["True"], }, }, }, { - 'label': 'Derniers jours', - 'url': { - 'path': base_url, - 'query': { - 'scale_n_steps': ['7'], - 'scale_name': ['day'], - 'scale_last': ['True'], + "label": "Derniers jours", + "url": { + "path": base_url, + "query": { + "scale_n_steps": ["7"], + "scale_name": ["day"], + "scale_last": ["True"], }, }, }, ] - for stat, expected in zip(content['stats'], expected_stats): - expected_url = expected.pop('url') - self.assertUrlsEqual(stat['url'], expected_url) + for stat, expected in zip(content["stats"], expected_stats): + expected_url = expected.pop("url") + self.assertUrlsEqual(stat["url"], expected_url) self.assertDictContainsSubset(expected, stat) class ArticleStatSalesViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.article.stat.sales' + url_name = "kfet.article.stat.sales" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.article.pk} + return {"pk": self.article.pk} @property def url_expected(self): - return '/k-fet/articles/{}/stat/sales'.format(self.article.pk) + return "/k-fet/articles/{}/stat/sales".format(self.article.pk) def setUp(self): super().setUp() self.article = Article.objects.create( - name='Article', - category=ArticleCategory.objects.create(name='Category'), + name="Article", category=ArticleCategory.objects.create(name="Category") ) def test_ok(self): @@ -1400,11 +1375,11 @@ class ArticleStatSalesViewTests(ViewTestCaseMixin, TestCase): class KPsulViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.kpsul' - url_expected = '/k-fet/k-psul/' + url_name = "kfet.kpsul" + url_expected = "/k-fet/k-psul/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): r = self.client.get(self.url) @@ -1412,43 +1387,51 @@ class KPsulViewTests(ViewTestCaseMixin, TestCase): class KPsulCheckoutDataViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.kpsul.checkout_data' - url_expected = '/k-fet/k-psul/checkout_data' + url_name = "kfet.kpsul.checkout_data" + url_expected = "/k-fet/k-psul/checkout_data" - http_methods = ['POST'] + http_methods = ["POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() self.checkout = Checkout.objects.create( - name='Checkout', - balance=Decimal('10'), - created_by=self.accounts['team'], + name="Checkout", + balance=Decimal("10"), + created_by=self.accounts["team"], valid_from=self.now, valid_to=self.now + timedelta(days=5), ) def test_ok(self): - r = self.client.post(self.url, {'pk': self.checkout.pk}) + r = self.client.post(self.url, {"pk": self.checkout.pk}) self.assertEqual(r.status_code, 200) - content = json.loads(r.content.decode('utf-8')) + content = json.loads(r.content.decode("utf-8")) - expected = { - 'name': 'Checkout', - 'balance': '10.00', - } + expected = {"name": "Checkout", "balance": "10.00"} self.assertDictContainsSubset(expected, content) - self.assertSetEqual(set(content.keys()), set([ - 'balance', 'id', 'name', 'valid_from', 'valid_to', - 'last_statement_at', 'last_statement_balance', - 'last_statement_by_first_name', 'last_statement_by_last_name', - 'last_statement_by_trigramme', - ])) + self.assertSetEqual( + set(content.keys()), + set( + [ + "balance", + "id", + "name", + "valid_from", + "valid_to", + "last_statement_at", + "last_statement_balance", + "last_statement_by_first_name", + "last_statement_by_last_name", + "last_statement_by_trigramme", + ] + ), + ) class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): @@ -1488,13 +1471,14 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): For invalid requests, response errors should be tested. """ - url_name = 'kfet.kpsul.perform_operations' - url_expected = '/k-fet/k-psul/perform_operations' - http_methods = ['POST'] + url_name = "kfet.kpsul.perform_operations" + url_expected = "/k-fet/k-psul/perform_operations" - auth_user = 'team' - auth_forbidden = [None, 'user'] + http_methods = ["POST"] + + auth_user = "team" + auth_forbidden = [None, "user"] with_liq = True @@ -1518,7 +1502,7 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) # An Account, trigramme=000, balance=50 # Do not assume user is cof, nor not cof. - self.account = self.accounts['user'] + self.account = self.accounts["user"] self.account.balance = Decimal("50.00") self.account.save() @@ -1539,15 +1523,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): the response. """ - json_data = ( - json.loads(getattr(response, "content", b"{}").decode("utf-8")) - ) + json_data = json.loads(getattr(response, "content", b"{}").decode("utf-8")) try: self.assertEqual(response.status_code, 200) except AssertionError as exc: - msg = ( - "Expected response is 200, got {}. Errors: {}" - .format(response.status_code, json_data.get("errors")) + msg = "Expected response is 200, got {}. Errors: {}".format( + response.status_code, json_data.get("errors") ) raise AssertionError(msg) from exc return json_data @@ -1555,13 +1536,13 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): def get_base_post_data(self): return { # OperationGroup form - 'on_acc': str(self.account.pk), - 'checkout': str(self.checkout.pk), + "on_acc": str(self.account.pk), + "checkout": str(self.checkout.pk), # Operation formset - 'form-TOTAL_FORMS': '0', - 'form-INITIAL_FORMS': '0', - 'form-MIN_NUM_FORMS': '1', - 'form-MAX_NUM_FORMS': '1000', + "form-TOTAL_FORMS": "0", + "form-INITIAL_FORMS": "0", + "form-MIN_NUM_FORMS": "1", + "form-MAX_NUM_FORMS": "1000", } base_post_data = property(get_base_post_data) @@ -1575,40 +1556,42 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(json_data["errors"]["operation_group"], ["on_acc"]) def test_group_on_acc_expects_comment(self): - user_add_perms( - self.users["team"], ["kfet.perform_commented_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_commented_operations"]) self.account.trigramme = "#13" self.account.save() self.assertTrue(self.account.need_comment) - data = dict(self.base_post_data, **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "comment": "A comment to explain it", + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) def test_invalid_group_on_acc_expects_comment(self): - user_add_perms( - self.users["team"], ["kfet.perform_commented_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_commented_operations"]) self.account.trigramme = "#13" self.account.save() self.assertTrue(self.account.need_comment) - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -1620,14 +1603,17 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.save() self.assertTrue(self.account.need_comment) - data = dict(self.base_post_data, **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "comment": "A comment to explain it", + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) @@ -1638,20 +1624,21 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) def test_group_on_acc_frozen(self): - user_add_perms( - self.users["team"], ["kfet.override_frozen_protection"] - ) + user_add_perms(self.users["team"], ["kfet.override_frozen_protection"]) self.account.is_frozen = True self.account.save() - data = dict(self.base_post_data, **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "comment": "A comment to explain it", + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -1660,21 +1647,23 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.is_frozen = True self.account.save() - data = dict(self.base_post_data, **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "comment": "A comment to explain it", + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( - json_data["errors"]["missing_perms"], - ["Forcer le gel d'un compte"], + json_data["errors"]["missing_perms"], ["Forcer le gel d'un compte"] ) def test_invalid_group_checkout(self): @@ -1701,13 +1690,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.cofprofile.is_cof = False self.account.cofprofile.save() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) # Check response status @@ -1715,39 +1707,48 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): # Check object creations operation_group = OperationGroup.objects.get() - self.assertDictEqual(operation_group.__dict__, { - "_state": mock.ANY, - "at": mock.ANY, - "amount": Decimal("-5.00"), - "checkout_id": self.checkout.pk, - "comment": "", - "id": mock.ANY, - "is_cof": False, - "on_acc_id": self.account.pk, - "valid_by_id": None, - }) + self.assertDictEqual( + operation_group.__dict__, + { + "_state": mock.ANY, + "at": mock.ANY, + "amount": Decimal("-5.00"), + "checkout_id": self.checkout.pk, + "comment": "", + "id": mock.ANY, + "is_cof": False, + "on_acc_id": self.account.pk, + "valid_by_id": None, + }, + ) operation = Operation.objects.get() - self.assertDictEqual(operation.__dict__, { - "_state": mock.ANY, - "addcost_amount": None, - "addcost_for_id": None, - "amount": Decimal("-5.00"), - "article_id": self.article.pk, - "article_nb": 2, - "canceled_at": None, - "canceled_by_id": None, - "group_id": operation_group.pk, - "id": mock.ANY, - "type": "purchase", - }) + self.assertDictEqual( + operation.__dict__, + { + "_state": mock.ANY, + "addcost_amount": None, + "addcost_for_id": None, + "amount": Decimal("-5.00"), + "article_id": self.article.pk, + "article_nb": 2, + "canceled_at": None, + "canceled_by_id": None, + "group_id": operation_group.pk, + "id": mock.ANY, + "type": "purchase", + }, + ) # Check response content - self.assertDictEqual(json_data, { - "operationgroup": operation_group.pk, - "operations": [operation.pk], - "warnings": {}, - "errors": {}, - }) + self.assertDictEqual( + json_data, + { + "operationgroup": operation_group.pk, + "operations": [operation.pk], + "warnings": {}, + "errors": {}, + }, + ) # Check object updates self.account.refresh_from_db() @@ -1784,22 +1785,12 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): "canceled_by__trigramme": None, "group_id": operation_group.pk, "type": "purchase", - }, + } ], - }, - ], - "checkouts": [ - { - "id": self.checkout.pk, - "balance": Decimal("100.00"), - }, - ], - "articles": [ - { - "id": self.article.pk, - "stock": 18, - }, + } ], + "checkouts": [{"id": self.checkout.pk, "balance": Decimal("100.00")}], + "articles": [{"id": self.article.pk, "stock": 18}], }, ) @@ -1808,13 +1799,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.cofprofile.is_cof = True self.account.cofprofile.save() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -1833,14 +1827,17 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(self.article.stock, 18) def test_purchase_with_cash(self): - data = dict(self.base_post_data, **{ - "on_acc": str(self.accounts["liq"].pk), - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "on_acc": str(self.accounts["liq"].pk), + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -1857,53 +1854,56 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(self.article.stock, 18) def test_invalid_purchase_expects_article(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": "", - "form-0-article_nb": "1", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": "", + "form-0-article_nb": "1", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( json_data["errors"]["operations"], - [ - {"__all__": ["Un achat nécessite un article et une quantité"]}, - ], + [{"__all__": ["Un achat nécessite un article et une quantité"]}], ) def test_invalid_purchase_expects_article_nb(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( json_data["errors"]["operations"], - [ - {"__all__": ["Un achat nécessite un article et une quantité"]}, - ], + [{"__all__": ["Un achat nécessite un article et une quantité"]}], ) - def test_invalid_purchase_expects_article_nb_greater_than_1( - self - ): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "-1", - }) + def test_invalid_purchase_expects_article_nb_greater_than_1(self): + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "-1", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -1912,26 +1912,26 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): json_data["errors"]["operations"], [ { - "__all__": [ - "Un achat nécessite un article et une quantité", - ], + "__all__": ["Un achat nécessite un article et une quantité"], "article_nb": [ - "Assurez-vous que cette valeur est supérieure ou " - "égale à 1.", + "Assurez-vous que cette valeur est supérieure ou " "égale à 1." ], - }, + } ], ) def test_invalid_operation_not_purchase_with_cash(self): - data = dict(self.base_post_data, **{ - "on_acc": str(self.accounts["liq"].pk), - "form-TOTAL_FORMS": "1", - "form-0-type": "deposit", - "form-0-amount": "10.00", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "on_acc": str(self.accounts["liq"].pk), + "form-TOTAL_FORMS": "1", + "form-0-type": "deposit", + "form-0-amount": "10.00", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -1940,50 +1940,62 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): def test_deposit(self): user_add_perms(self.users["team"], ["kfet.perform_deposit"]) - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "deposit", - "form-0-amount": "10.75", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "deposit", + "form-0-amount": "10.75", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) json_data = self._assertResponseOk(resp) operation_group = OperationGroup.objects.get() - self.assertDictEqual(operation_group.__dict__, { - "_state": mock.ANY, - "at": mock.ANY, - "amount": Decimal("10.75"), - "checkout_id": self.checkout.pk, - "comment": "", - "id": mock.ANY, - "is_cof": False, - "on_acc_id": self.account.pk, - "valid_by_id": self.accounts["team"].pk, - }) + self.assertDictEqual( + operation_group.__dict__, + { + "_state": mock.ANY, + "at": mock.ANY, + "amount": Decimal("10.75"), + "checkout_id": self.checkout.pk, + "comment": "", + "id": mock.ANY, + "is_cof": False, + "on_acc_id": self.account.pk, + "valid_by_id": self.accounts["team"].pk, + }, + ) operation = Operation.objects.get() - self.assertDictEqual(operation.__dict__, { - "_state": mock.ANY, - "addcost_amount": None, - "addcost_for_id": None, - "amount": Decimal("10.75"), - "article_id": None, - "article_nb": None, - "canceled_at": None, - "canceled_by_id": None, - "group_id": operation_group.pk, - "id": mock.ANY, - "type": "deposit", - }) + self.assertDictEqual( + operation.__dict__, + { + "_state": mock.ANY, + "addcost_amount": None, + "addcost_for_id": None, + "amount": Decimal("10.75"), + "article_id": None, + "article_nb": None, + "canceled_at": None, + "canceled_by_id": None, + "group_id": operation_group.pk, + "id": mock.ANY, + "type": "deposit", + }, + ) - self.assertDictEqual(json_data, { - "operationgroup": operation_group.pk, - "operations": [operation.pk], - "warnings": {}, - "errors": {}, - }) + self.assertDictEqual( + json_data, + { + "operationgroup": operation_group.pk, + "operations": [operation.pk], + "warnings": {}, + "errors": {}, + }, + ) self.account.refresh_from_db() self.assertEqual(self.account.balance, Decimal("60.75")) @@ -2016,28 +2028,26 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): "canceled_by__trigramme": None, "group_id": operation_group.pk, "type": "deposit", - }, + } ], - }, - ], - "checkouts": [ - { - "id": self.checkout.pk, - "balance": Decimal("110.75"), - }, + } ], + "checkouts": [{"id": self.checkout.pk, "balance": Decimal("110.75")}], "articles": [], }, ) def test_invalid_deposit_expects_amount(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "deposit", - "form-0-amount": "", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "deposit", + "form-0-amount": "", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -2047,13 +2057,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) def test_invalid_deposit_too_many_params(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "deposit", - "form-0-amount": "10", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "3", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "deposit", + "form-0-amount": "10", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "3", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -2063,83 +2076,98 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) def test_invalid_deposit_expects_positive_amount(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "deposit", - "form-0-amount": "-10", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "deposit", + "form-0-amount": "-10", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( - json_data["errors"]["operations"], - [{"__all__": ["Charge non positive"]}] + json_data["errors"]["operations"], [{"__all__": ["Charge non positive"]}] ) def test_invalid_deposit_requires_perm(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "deposit", - "form-0-amount": "10.75", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "deposit", + "form-0-amount": "10.75", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) json_data = json.loads(resp.content.decode("utf-8")) - self.assertEqual( - json_data["errors"]["missing_perms"], ["Effectuer une charge"] - ) + self.assertEqual(json_data["errors"]["missing_perms"], ["Effectuer une charge"]) def test_withdraw(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "withdraw", - "form-0-amount": "-10.75", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "withdraw", + "form-0-amount": "-10.75", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) json_data = self._assertResponseOk(resp) operation_group = OperationGroup.objects.get() - self.assertDictEqual(operation_group.__dict__, { - "_state": mock.ANY, - "at": mock.ANY, - "amount": Decimal("-10.75"), - "checkout_id": self.checkout.pk, - "comment": "", - "id": mock.ANY, - "is_cof": False, - "on_acc_id": self.account.pk, - "valid_by_id": None, - }) + self.assertDictEqual( + operation_group.__dict__, + { + "_state": mock.ANY, + "at": mock.ANY, + "amount": Decimal("-10.75"), + "checkout_id": self.checkout.pk, + "comment": "", + "id": mock.ANY, + "is_cof": False, + "on_acc_id": self.account.pk, + "valid_by_id": None, + }, + ) operation = Operation.objects.get() - self.assertDictEqual(operation.__dict__, { - "_state": mock.ANY, - "addcost_amount": None, - "addcost_for_id": None, - "amount": Decimal("-10.75"), - "article_id": None, - "article_nb": None, - "canceled_at": None, - "canceled_by_id": None, - "group_id": operation_group.pk, - "id": mock.ANY, - "type": "withdraw", - }) + self.assertDictEqual( + operation.__dict__, + { + "_state": mock.ANY, + "addcost_amount": None, + "addcost_for_id": None, + "amount": Decimal("-10.75"), + "article_id": None, + "article_nb": None, + "canceled_at": None, + "canceled_by_id": None, + "group_id": operation_group.pk, + "id": mock.ANY, + "type": "withdraw", + }, + ) - self.assertDictEqual(json_data, { - "operationgroup": operation_group.pk, - "operations": [operation.pk], - "warnings": {}, - "errors": {}, - }) + self.assertDictEqual( + json_data, + { + "operationgroup": operation_group.pk, + "operations": [operation.pk], + "warnings": {}, + "errors": {}, + }, + ) self.account.refresh_from_db() self.assertEqual(self.account.balance, Decimal("39.25")) @@ -2172,28 +2200,26 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): "canceled_by__trigramme": None, "group_id": operation_group.pk, "type": "withdraw", - }, + } ], - }, - ], - "checkouts": [ - { - "id": self.checkout.pk, - "balance": Decimal("89.25"), - }, + } ], + "checkouts": [{"id": self.checkout.pk, "balance": Decimal("89.25")}], "articles": [], }, ) def test_invalid_withdraw_expects_amount(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "withdraw", - "form-0-amount": "", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "withdraw", + "form-0-amount": "", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -2203,13 +2229,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) def test_invalid_withdraw_too_many_params(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "withdraw", - "form-0-amount": "-10", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "3", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "withdraw", + "form-0-amount": "-10", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "3", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -2219,70 +2248,84 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): ) def test_invalid_withdraw_expects_negative_amount(self): - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "withdraw", - "form-0-amount": "10", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "withdraw", + "form-0-amount": "10", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( - json_data["errors"]["operations"], - [{"__all__": ["Retrait non négatif"]}] + json_data["errors"]["operations"], [{"__all__": ["Retrait non négatif"]}] ) def test_edit(self): user_add_perms(self.users["team"], ["kfet.edit_balance_account"]) - data = dict(self.base_post_data, **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "edit", - "form-0-amount": "10.75", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "comment": "A comment to explain it", + "form-TOTAL_FORMS": "1", + "form-0-type": "edit", + "form-0-amount": "10.75", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) json_data = self._assertResponseOk(resp) operation_group = OperationGroup.objects.get() - self.assertDictEqual(operation_group.__dict__, { - "_state": mock.ANY, - "at": mock.ANY, - "amount": Decimal("10.75"), - "checkout_id": self.checkout.pk, - "comment": "A comment to explain it", - "id": mock.ANY, - "is_cof": False, - "on_acc_id": self.account.pk, - "valid_by_id": self.accounts["team"].pk, - }) + self.assertDictEqual( + operation_group.__dict__, + { + "_state": mock.ANY, + "at": mock.ANY, + "amount": Decimal("10.75"), + "checkout_id": self.checkout.pk, + "comment": "A comment to explain it", + "id": mock.ANY, + "is_cof": False, + "on_acc_id": self.account.pk, + "valid_by_id": self.accounts["team"].pk, + }, + ) operation = Operation.objects.get() - self.assertDictEqual(operation.__dict__, { - "_state": mock.ANY, - "addcost_amount": None, - "addcost_for_id": None, - "amount": Decimal("10.75"), - "article_id": None, - "article_nb": None, - "canceled_at": None, - "canceled_by_id": None, - "group_id": operation_group.pk, - "id": mock.ANY, - "type": "edit", - }) + self.assertDictEqual( + operation.__dict__, + { + "_state": mock.ANY, + "addcost_amount": None, + "addcost_for_id": None, + "amount": Decimal("10.75"), + "article_id": None, + "article_nb": None, + "canceled_at": None, + "canceled_by_id": None, + "group_id": operation_group.pk, + "id": mock.ANY, + "type": "edit", + }, + ) - self.assertDictEqual(json_data, { - "operationgroup": operation_group.pk, - "operations": [operation.pk], - "warnings": {}, - "errors": {}, - }) + self.assertDictEqual( + json_data, + { + "operationgroup": operation_group.pk, + "operations": [operation.pk], + "warnings": {}, + "errors": {}, + }, + ) self.account.refresh_from_db() self.assertEqual(self.account.balance, Decimal("60.75")) @@ -2315,48 +2358,48 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): "canceled_by__trigramme": None, "group_id": operation_group.pk, "type": "edit", - }, + } ], - }, - ], - "checkouts": [ - { - "id": self.checkout.pk, - "balance": Decimal("100.00"), - }, + } ], + "checkouts": [{"id": self.checkout.pk, "balance": Decimal("100.00")}], "articles": [], }, ) def test_invalid_edit_requires_perm(self): - data = dict(self.base_post_data, **{ - "comment": "A comment to explain it", - "form-TOTAL_FORMS": "1", - "form-0-type": "edit", - "form-0-amount": "10.75", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "comment": "A comment to explain it", + "form-TOTAL_FORMS": "1", + "form-0-type": "edit", + "form-0-amount": "10.75", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( - json_data["errors"]["missing_perms"], - ["Modifier la balance d'un compte"], + json_data["errors"]["missing_perms"], ["Modifier la balance d'un compte"] ) def test_invalid_edit_expects_comment(self): user_add_perms(self.users["team"], ["kfet.edit_balance_account"]) - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "edit", - "form-0-amount": "10.75", - "form-0-article": "", - "form-0-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "edit", + "form-0-amount": "10.75", + "form-0-article": "", + "form-0-article_nb": "", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 400) @@ -2366,8 +2409,7 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): def _setup_addcost(self): self.register_user("addcost", create_user("addcost", "ADD")) kfet_config.set( - addcost_amount=Decimal("0.50"), - addcost_for=self.accounts["addcost"], + addcost_amount=Decimal("0.50"), addcost_for=self.accounts["addcost"] ) def test_addcost_user_is_not_cof(self): @@ -2375,13 +2417,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.cofprofile.save() self._setup_addcost() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2400,10 +2445,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - ws_data_ope = ( - self.kpsul_consumer_mock.group_send - .call_args[0][1]["opegroups"][0]["opes"][0] - ) + ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["opegroups"][ + 0 + ]["opes"][0] self.assertEqual(ws_data_ope["addcost_amount"], Decimal("1.00")) self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD") @@ -2413,13 +2457,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.cofprofile.save() self._setup_addcost() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2438,10 +2485,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("100.00")) - ws_data_ope = ( - self.kpsul_consumer_mock.group_send - .call_args[0][1]["opegroups"][0]["opes"][0] - ) + ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["opegroups"][ + 0 + ]["opes"][0] self.assertEqual(ws_data_ope["addcost_amount"], Decimal("0.80")) self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD") @@ -2450,14 +2496,17 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.cofprofile.save() self._setup_addcost() - data = dict(self.base_post_data, **{ - "on_acc": str(self.accounts["liq"].pk), - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "on_acc": str(self.accounts["liq"].pk), + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2474,10 +2523,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.checkout.refresh_from_db() self.assertEqual(self.checkout.balance, Decimal("106.00")) - ws_data_ope = ( - self.kpsul_consumer_mock.group_send - .call_args[0][1]["opegroups"][0]["opes"][0] - ) + ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["opegroups"][ + 0 + ]["opes"][0] self.assertEqual(ws_data_ope["addcost_amount"], Decimal("1.00")) self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD") @@ -2486,14 +2534,17 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.accounts["addcost"].balance = Decimal("20.00") self.accounts["addcost"].save() - data = dict(self.base_post_data, **{ - "on_acc": str(self.accounts["addcost"].pk), - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "on_acc": str(self.accounts["addcost"].pk), + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2508,10 +2559,9 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.accounts["addcost"].refresh_from_db() self.assertEqual(self.accounts["addcost"].balance, Decimal("15.00")) - ws_data_ope = ( - self.kpsul_consumer_mock.group_send - .call_args[0][1]["opegroups"][0]["opes"][0] - ) + ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["opegroups"][ + 0 + ]["opes"][0] self.assertEqual(ws_data_ope["addcost_amount"], None) self.assertEqual(ws_data_ope["addcost_for__trigramme"], None) @@ -2520,13 +2570,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.article.category.has_addcost = False self.article.category.save() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2541,27 +2594,27 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.accounts["addcost"].refresh_from_db() self.assertEqual(self.accounts["addcost"].balance, Decimal("0.00")) - ws_data_ope = ( - self.kpsul_consumer_mock.group_send - .call_args[0][1]["opegroups"][0]["opes"][0] - ) + ws_data_ope = self.kpsul_consumer_mock.group_send.call_args[0][1]["opegroups"][ + 0 + ]["opes"][0] self.assertEqual(ws_data_ope["addcost_amount"], None) self.assertEqual(ws_data_ope["addcost_for__trigramme"], None) def test_negative_new(self): - user_add_perms( - self.users["team"], ["kfet.perform_negative_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_negative_operations"]) self.account.balance = Decimal("1.00") self.account.save() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2570,20 +2623,21 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(self.account.balance, Decimal("-4.00")) def test_negative_exists(self): - user_add_perms( - self.users["team"], ["kfet.perform_negative_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_negative_operations"]) self.account.balance = Decimal("-10.00") self.account.save() self.account.update_negative() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2597,17 +2651,20 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.save() self.account.update_negative() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "2", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "1", - "form-1-type": "deposit", - "form-1-amount": "5.00", - "form-1-article": "", - "form-1-article_nb": "", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "2", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "1", + "form-1-type": "deposit", + "form-1-amount": "5.00", + "form-1-article": "", + "form-1-article_nb": "", + } + ) resp = self.client.post(self.url, data) self._assertResponseOk(resp) @@ -2619,43 +2676,44 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.balance = Decimal("1.00") self.account.save() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) json_data = json.loads(resp.content.decode("utf-8")) self.assertEqual( json_data["errors"], - { - "missing_perms": ["Enregistrer des commandes en négatif"], - }, + {"missing_perms": ["Enregistrer des commandes en négatif"]}, ) def test_invalid_negative_exceeds_allowed_duration_from_config(self): - user_add_perms( - self.users["team"], ["kfet.perform_negative_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_negative_operations"]) kfet_config.set(overdraft_duration=timedelta(days=5)) self.account.balance = Decimal("1.00") self.account.save() self.account.negative = AccountNegative.objects.create( - account=self.account, - start=timezone.now() - timedelta(days=5, minutes=1), + account=self.account, start=timezone.now() - timedelta(days=5, minutes=1) ) - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) @@ -2663,9 +2721,7 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(json_data["errors"], {"negative": ["000"]}) def test_invalid_negative_exceeds_allowed_duration_from_account(self): - user_add_perms( - self.users["team"], ["kfet.perform_negative_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_negative_operations"]) kfet_config.set(overdraft_duration=timedelta(days=5)) self.account.balance = Decimal("1.00") self.account.save() @@ -2675,13 +2731,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): authz_overdraft_until=timezone.now() - timedelta(seconds=1), ) - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) @@ -2689,21 +2748,22 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(json_data["errors"], {"negative": ["000"]}) def test_invalid_negative_exceeds_amount_allowed_from_config(self): - user_add_perms( - self.users["team"], ["kfet.perform_negative_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_negative_operations"]) kfet_config.set(overdraft_amount=Decimal("-1.00")) self.account.balance = Decimal("1.00") self.account.save() self.account.update_negative() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) @@ -2711,9 +2771,7 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.assertEqual(json_data["errors"], {"negative": ["000"]}) def test_invalid_negative_exceeds_amount_allowed_from_account(self): - user_add_perms( - self.users["team"], ["kfet.perform_negative_operations"] - ) + user_add_perms(self.users["team"], ["kfet.perform_negative_operations"]) kfet_config.set(overdraft_amount=Decimal("10.00")) self.account.balance = Decimal("1.00") self.account.save() @@ -2724,13 +2782,16 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): authz_overdraft_amount=Decimal("1.00"), ) - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "1", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "1", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + } + ) resp = self.client.post(self.url, data) self.assertEqual(resp.status_code, 403) @@ -2747,17 +2808,20 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): self.account.cofprofile.is_cof = False self.account.cofprofile.save() - data = dict(self.base_post_data, **{ - "form-TOTAL_FORMS": "2", - "form-0-type": "purchase", - "form-0-amount": "", - "form-0-article": str(self.article.pk), - "form-0-article_nb": "2", - "form-1-type": "purchase", - "form-1-amount": "", - "form-1-article": str(article2.pk), - "form-1-article_nb": "1", - }) + data = dict( + self.base_post_data, + **{ + "form-TOTAL_FORMS": "2", + "form-0-type": "purchase", + "form-0-amount": "", + "form-0-article": str(self.article.pk), + "form-0-article_nb": "2", + "form-1-type": "purchase", + "form-1-amount": "", + "form-1-article": str(article2.pk), + "form-1-article_nb": "1", + } + ) resp = self.client.post(self.url, data) # Check response status @@ -2765,53 +2829,65 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): # Check object creations operation_group = OperationGroup.objects.get() - self.assertDictEqual(operation_group.__dict__, { - "_state": mock.ANY, - "at": mock.ANY, - "amount": Decimal("-9.00"), - "checkout_id": self.checkout.pk, - "comment": "", - "id": mock.ANY, - "is_cof": False, - "on_acc_id": self.account.pk, - "valid_by_id": None, - }) + self.assertDictEqual( + operation_group.__dict__, + { + "_state": mock.ANY, + "at": mock.ANY, + "amount": Decimal("-9.00"), + "checkout_id": self.checkout.pk, + "comment": "", + "id": mock.ANY, + "is_cof": False, + "on_acc_id": self.account.pk, + "valid_by_id": None, + }, + ) operation_list = Operation.objects.all() self.assertEqual(len(operation_list), 2) - self.assertDictEqual(operation_list[0].__dict__, { - "_state": mock.ANY, - "addcost_amount": None, - "addcost_for_id": None, - "amount": Decimal("-5.00"), - "article_id": self.article.pk, - "article_nb": 2, - "canceled_at": None, - "canceled_by_id": None, - "group_id": operation_group.pk, - "id": mock.ANY, - "type": "purchase", - }) - self.assertDictEqual(operation_list[1].__dict__, { - "_state": mock.ANY, - "addcost_amount": None, - "addcost_for_id": None, - "amount": Decimal("-4.00"), - "article_id": article2.pk, - "article_nb": 1, - "canceled_at": None, - "canceled_by_id": None, - "group_id": operation_group.pk, - "id": mock.ANY, - "type": "purchase", - }) + self.assertDictEqual( + operation_list[0].__dict__, + { + "_state": mock.ANY, + "addcost_amount": None, + "addcost_for_id": None, + "amount": Decimal("-5.00"), + "article_id": self.article.pk, + "article_nb": 2, + "canceled_at": None, + "canceled_by_id": None, + "group_id": operation_group.pk, + "id": mock.ANY, + "type": "purchase", + }, + ) + self.assertDictEqual( + operation_list[1].__dict__, + { + "_state": mock.ANY, + "addcost_amount": None, + "addcost_for_id": None, + "amount": Decimal("-4.00"), + "article_id": article2.pk, + "article_nb": 1, + "canceled_at": None, + "canceled_by_id": None, + "group_id": operation_group.pk, + "id": mock.ANY, + "type": "purchase", + }, + ) # Check response content - self.assertDictEqual(json_data, { - "operationgroup": operation_group.pk, - "operations": [operation_list[0].pk, operation_list[1].pk], - "warnings": {}, - "errors": {}, - }) + self.assertDictEqual( + json_data, + { + "operationgroup": operation_group.pk, + "operations": [operation_list[0].pk, operation_list[1].pk], + "warnings": {}, + "errors": {}, + }, + ) # Check object updates self.account.refresh_from_db() @@ -2864,120 +2940,99 @@ class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase): "type": "purchase", }, ], - }, - ], - "checkouts": [ - { - "id": self.checkout.pk, - "balance": Decimal("100.00"), - }, + } ], + "checkouts": [{"id": self.checkout.pk, "balance": Decimal("100.00")}], "articles": [ - { - "id": self.article.pk, - "stock": 18, - }, - { - "id": article2.pk, - "stock": -6, - }, + {"id": self.article.pk, "stock": 18}, + {"id": article2.pk, "stock": -6}, ], }, ) class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.kpsul.cancel_operations' - url_expected = '/k-fet/k-psul/cancel_operations' + url_name = "kfet.kpsul.cancel_operations" + url_expected = "/k-fet/k-psul/cancel_operations" - http_methods = ['POST'] + http_methods = ["POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): pass class KPsulArticlesData(ViewTestCaseMixin, TestCase): - url_name = 'kfet.kpsul.articles_data' - url_expected = '/k-fet/k-psul/articles_data' + url_name = "kfet.kpsul.articles_data" + url_expected = "/k-fet/k-psul/articles_data" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Catégorie') - self.article1 = Article.objects.create( - category=category, - name='Article 1', - ) + category = ArticleCategory.objects.create(name="Catégorie") + self.article1 = Article.objects.create(category=category, name="Article 1") self.article2 = Article.objects.create( - category=category, - name='Article 2', - price=Decimal('2.5'), + category=category, name="Article 2", price=Decimal("2.5") ) def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - content = json.loads(r.content.decode('utf-8')) + content = json.loads(r.content.decode("utf-8")) - articles = content['articles'] + articles = content["articles"] - expected_list = [{ - 'category__name': 'Catégorie', - 'name': 'Article 1', - 'price': '0.00', - }, { - 'category__name': 'Catégorie', - 'name': 'Article 2', - 'price': '2.50', - }] + expected_list = [ + {"category__name": "Catégorie", "name": "Article 1", "price": "0.00"}, + {"category__name": "Catégorie", "name": "Article 2", "price": "2.50"}, + ] for expected, article in zip(expected_list, articles): self.assertDictContainsSubset(expected, article) - self.assertSetEqual(set(article.keys()), set([ - 'id', 'name', 'price', 'stock', - 'category_id', 'category__name', 'category__has_addcost', - ])) + self.assertSetEqual( + set(article.keys()), + set( + [ + "id", + "name", + "price", + "stock", + "category_id", + "category__name", + "category__has_addcost", + ] + ), + ) class KPsulUpdateAddcost(ViewTestCaseMixin, TestCase): - url_name = 'kfet.kpsul.update_addcost' - url_expected = '/k-fet/k-psul/update_addcost' + url_name = "kfet.kpsul.update_addcost" + url_expected = "/k-fet/k-psul/update_addcost" - http_methods = ['POST'] + http_methods = ["POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] - post_data = { - 'trigramme': '000', - 'amount': '0.5', - } + post_data = {"trigramme": "000", "amount": "0.5"} def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.manage_addcosts', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.manage_addcosts"])} def test_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) self.assertEqual(r.status_code, 200) - self.assertEqual( - kfet_config.addcost_for, - Account.objects.get(trigramme='000'), - ) - self.assertEqual(kfet_config.addcost_amount, Decimal('0.5')) + self.assertEqual(kfet_config.addcost_for, Account.objects.get(trigramme="000")) + self.assertEqual(kfet_config.addcost_amount, Decimal("0.5")) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -2985,11 +3040,11 @@ class KPsulUpdateAddcost(ViewTestCaseMixin, TestCase): class KPsulGetSettings(ViewTestCaseMixin, TestCase): - url_name = 'kfet.kpsul.get_settings' - url_expected = '/k-fet/k-psul/get_settings' + url_name = "kfet.kpsul.get_settings" + url_expected = "/k-fet/k-psul/get_settings" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): r = self.client.get(self.url) @@ -2997,10 +3052,10 @@ class KPsulGetSettings(ViewTestCaseMixin, TestCase): class HistoryJSONViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.history.json' - url_expected = '/k-fet/history.json' + url_name = "kfet.history.json" + url_expected = "/k-fet/history.json" - auth_user = 'user' + auth_user = "user" auth_forbidden = [None] def test_ok(self): @@ -3009,46 +3064,51 @@ class HistoryJSONViewTests(ViewTestCaseMixin, TestCase): class AccountReadJSONViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.account.read.json' - url_expected = '/k-fet/accounts/read.json' + url_name = "kfet.account.read.json" + url_expected = "/k-fet/accounts/read.json" - http_methods = ['POST'] + http_methods = ["POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): - r = self.client.post(self.url, {'trigramme': '000'}) + r = self.client.post(self.url, {"trigramme": "000"}) self.assertEqual(r.status_code, 200) - content = json.loads(r.content.decode('utf-8')) + content = json.loads(r.content.decode("utf-8")) - expected = { - 'name': 'first last', - 'trigramme': '000', - 'balance': '0.00', - } + expected = {"name": "first last", "trigramme": "000", "balance": "0.00"} self.assertDictContainsSubset(expected, content) - self.assertSetEqual(set(content.keys()), set([ - 'balance', 'departement', 'email', 'id', 'is_cof', 'is_frozen', - 'name', 'nickname', 'promo', 'trigramme', - ])) + self.assertSetEqual( + set(content.keys()), + set( + [ + "balance", + "departement", + "email", + "id", + "is_cof", + "is_frozen", + "name", + "nickname", + "promo", + "trigramme", + ] + ), + ) class SettingsListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.settings' - url_expected = '/k-fet/settings/' + url_name = "kfet.settings" + url_expected = "/k-fet/settings/" - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.see_config', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.see_config"])} def test_ok(self): r = self.client.get(self.url) @@ -3056,31 +3116,27 @@ class SettingsListViewTests(ViewTestCaseMixin, TestCase): class SettingsUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.settings.update' - url_expected = '/k-fet/settings/edit' + url_name = "kfet.settings.update" + url_expected = "/k-fet/settings/edit" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] @property def post_data(self): return { - 'kfet_reduction_cof': '25', - 'kfet_addcost_amount': '0.5', - 'kfet_addcost_for': self.accounts['user'].pk, - 'kfet_overdraft_duration': '2 00:00:00', - 'kfet_overdraft_amount': '25', - 'kfet_cancel_duration': '00:20:00', + "kfet_reduction_cof": "25", + "kfet_addcost_amount": "0.5", + "kfet_addcost_for": self.accounts["user"].pk, + "kfet_overdraft_duration": "2 00:00:00", + "kfet_overdraft_amount": "25", + "kfet_cancel_duration": "00:20:00", } def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_config', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.change_config"])} def test_get_ok(self): r = self.client.get(self.url) @@ -3089,19 +3145,15 @@ class SettingsUpdateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): r = self.client.post(self.url, self.post_data) # Redirect is skipped because client may lack permissions. - self.assertRedirects( - r, - reverse('kfet.settings'), - fetch_redirect_response=False, - ) + self.assertRedirects(r, reverse("kfet.settings"), fetch_redirect_response=False) expected_config = { - 'reduction_cof': Decimal('25'), - 'addcost_amount': Decimal('0.5'), - 'addcost_for': self.accounts['user'], - 'overdraft_duration': timedelta(days=2), - 'overdraft_amount': Decimal('25'), - 'cancel_duration': timedelta(minutes=20), + "reduction_cof": Decimal("25"), + "addcost_amount": Decimal("0.5"), + "addcost_for": self.accounts["user"], + "overdraft_duration": timedelta(days=2), + "overdraft_amount": Decimal("25"), + "cancel_duration": timedelta(minutes=20), } for key, expected in expected_config.items(): @@ -3109,11 +3161,11 @@ class SettingsUpdateViewTests(ViewTestCaseMixin, TestCase): class TransferListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.transfers' - url_expected = '/k-fet/transfers/' + url_name = "kfet.transfers" + url_expected = "/k-fet/transfers/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): r = self.client.get(self.url) @@ -3121,11 +3173,11 @@ class TransferListViewTests(ViewTestCaseMixin, TestCase): class TransferCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.transfers.create' - url_expected = '/k-fet/transfers/new' + url_name = "kfet.transfers.create" + url_expected = "/k-fet/transfers/new" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def test_ok(self): r = self.client.get(self.url) @@ -3133,195 +3185,180 @@ class TransferCreateViewTests(ViewTestCaseMixin, TestCase): class TransferPerformViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.transfers.perform' - url_expected = '/k-fet/transfers/perform' + url_name = "kfet.transfers.perform" + url_expected = "/k-fet/transfers/perform" - http_methods = ['POST'] + http_methods = ["POST"] - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): return { - 'team1': create_team('team1', '101', perms=[ - # Required - 'kfet.add_transfer', - # Convenience - 'kfet.perform_negative_operations', - ]), + "team1": create_team( + "team1", + "101", + perms=[ + # Required + "kfet.add_transfer", + # Convenience + "kfet.perform_negative_operations", + ], + ) } @property def post_data(self): return { # General - 'comment': '', + "comment": "", # Formset management - 'form-TOTAL_FORMS': '10', - 'form-INITIAL_FORMS': '0', - 'form-MIN_NUM_FORMS': '1', - 'form-MAX_NUM_FORMS': '1000', + "form-TOTAL_FORMS": "10", + "form-INITIAL_FORMS": "0", + "form-MIN_NUM_FORMS": "1", + "form-MAX_NUM_FORMS": "1000", # Transfer 1 - 'form-0-from_acc': str(self.accounts['user'].pk), - 'form-0-to_acc': str(self.accounts['team'].pk), - 'form-0-amount': '3.5', + "form-0-from_acc": str(self.accounts["user"].pk), + "form-0-to_acc": str(self.accounts["team"].pk), + "form-0-amount": "3.5", # Transfer 2 - 'form-1-from_acc': str(self.accounts['team'].pk), - 'form-1-to_acc': str(self.accounts['team1'].pk), - 'form-1-amount': '2.4', + "form-1-from_acc": str(self.accounts["team"].pk), + "form-1-to_acc": str(self.accounts["team1"].pk), + "form-1-amount": "2.4", } def test_ok(self): r = self.client.post(self.url, self.post_data) self.assertEqual(r.status_code, 200) - user = self.accounts['user'] + user = self.accounts["user"] user.refresh_from_db() - self.assertEqual(user.balance, Decimal('-3.5')) + self.assertEqual(user.balance, Decimal("-3.5")) - team = self.accounts['team'] + team = self.accounts["team"] team.refresh_from_db() - self.assertEqual(team.balance, Decimal('1.1')) + self.assertEqual(team.balance, Decimal("1.1")) - team1 = self.accounts['team1'] + team1 = self.accounts["team1"] team1.refresh_from_db() - self.assertEqual(team1.balance, Decimal('2.4')) + self.assertEqual(team1.balance, Decimal("2.4")) class TransferCancelViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.transfers.cancel' - url_expected = '/k-fet/transfers/cancel' + url_name = "kfet.transfers.cancel" + url_expected = "/k-fet/transfers/cancel" - http_methods = ['POST'] + http_methods = ["POST"] - auth_user = 'team1' - auth_forbidden = [None, 'user', 'team'] + auth_user = "team1" + auth_forbidden = [None, "user", "team"] def get_users_extra(self): return { - 'team1': create_team('team1', '101', perms=[ - # Convenience - 'kfet.perform_negative_operations', - ]), + "team1": create_team( + "team1", + "101", + perms=[ + # Convenience + "kfet.perform_negative_operations" + ], + ) } @property def post_data(self): - return { - 'transfers[]': [self.transfer1.pk, self.transfer2.pk], - } + return {"transfers[]": [self.transfer1.pk, self.transfer2.pk]} def setUp(self): super().setUp() group = TransferGroup.objects.create() self.transfer1 = Transfer.objects.create( group=group, - from_acc=self.accounts['user'], - to_acc=self.accounts['team'], - amount='3.5', + from_acc=self.accounts["user"], + to_acc=self.accounts["team"], + amount="3.5", ) self.transfer2 = Transfer.objects.create( group=group, - from_acc=self.accounts['team'], - to_acc=self.accounts['root'], - amount='2.4', + from_acc=self.accounts["team"], + to_acc=self.accounts["root"], + amount="2.4", ) def test_ok(self): r = self.client.post(self.url, self.post_data) self.assertEqual(r.status_code, 200) - user = self.accounts['user'] + user = self.accounts["user"] user.refresh_from_db() - self.assertEqual(user.balance, Decimal('3.5')) + self.assertEqual(user.balance, Decimal("3.5")) - team = self.accounts['team'] + team = self.accounts["team"] team.refresh_from_db() - self.assertEqual(team.balance, Decimal('-1.1')) + self.assertEqual(team.balance, Decimal("-1.1")) - root = self.accounts['root'] + root = self.accounts["root"] root.refresh_from_db() - self.assertEqual(root.balance, Decimal('-2.4')) + self.assertEqual(root.balance, Decimal("-2.4")) class InventoryListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.inventory' - url_expected = '/k-fet/inventaires/' + url_name = "kfet.inventory" + url_expected = "/k-fet/inventaires/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() - self.inventory = Inventory.objects.create( - by=self.accounts['team'], - ) - category = ArticleCategory.objects.create(name='Category') - article = Article.objects.create( - name='Article', - category=category, - ) + self.inventory = Inventory.objects.create(by=self.accounts["team"]) + category = ArticleCategory.objects.create(name="Category") + article = Article.objects.create(name="Article", category=category) InventoryArticle.objects.create( - inventory=self.inventory, - article=article, - stock_old=5, - stock_new=0, + inventory=self.inventory, article=article, stock_old=5, stock_new=0 ) def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - inventories = r.context['inventories'] - self.assertQuerysetEqual( - inventories, - map(repr, [self.inventory]), - ) + inventories = r.context["inventories"] + self.assertQuerysetEqual(inventories, map(repr, [self.inventory])) class InventoryCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.inventory.create' - url_expected = '/k-fet/inventaires/new' + url_name = "kfet.inventory.create" + url_expected = "/k-fet/inventaires/new" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.add_inventory', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.add_inventory"])} @property def post_data(self): return { # Formset management - 'form-TOTAL_FORMS': '2', - 'form-INITIAL_FORMS': '2', - 'form-MIN_NUM_FORMS': '0', - 'form-MAX_NUM_FORMS': '1000', + "form-TOTAL_FORMS": "2", + "form-INITIAL_FORMS": "2", + "form-MIN_NUM_FORMS": "0", + "form-MAX_NUM_FORMS": "1000", # Article 1 - 'form-0-article': str(self.article1.pk), - 'form-0-stock_new': '5', + "form-0-article": str(self.article1.pk), + "form-0-stock_new": "5", # Article 2 - 'form-1-article': str(self.article2.pk), - 'form-1-stock_new': '10', + "form-1-article": str(self.article2.pk), + "form-1-stock_new": "10", } def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Category') - self.article1 = Article.objects.create( - category=category, - name='Article 1', - ) - self.article2 = Article.objects.create( - category=category, - name='Article 2', - ) + category = ArticleCategory.objects.create(name="Category") + self.article1 = Article.objects.create(category=category, name="Article 1") + self.article2 = Article.objects.create(category=category, name="Article 2") def test_get_ok(self): r = self.client.get(self.url) @@ -3329,10 +3366,10 @@ class InventoryCreateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.inventory')) + self.assertRedirects(r, reverse("kfet.inventory")) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -3340,34 +3377,26 @@ class InventoryCreateViewTests(ViewTestCaseMixin, TestCase): class InventoryReadViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.inventory.read' + url_name = "kfet.inventory.read" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.inventory.pk} + return {"pk": self.inventory.pk} @property def url_expected(self): - return '/k-fet/inventaires/{}'.format(self.inventory.pk) + return "/k-fet/inventaires/{}".format(self.inventory.pk) def setUp(self): super().setUp() - self.inventory = Inventory.objects.create( - by=self.accounts['team'], - ) - category = ArticleCategory.objects.create(name='Category') - article = Article.objects.create( - name='Article', - category=category, - ) + self.inventory = Inventory.objects.create(by=self.accounts["team"]) + category = ArticleCategory.objects.create(name="Category") + article = Article.objects.create(name="Article", category=category) InventoryArticle.objects.create( - inventory=self.inventory, - article=article, - stock_old=5, - stock_new=0, + inventory=self.inventory, article=article, stock_old=5, stock_new=0 ) def test_ok(self): @@ -3376,65 +3405,58 @@ class InventoryReadViewTests(ViewTestCaseMixin, TestCase): class OrderListViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.order' - url_expected = '/k-fet/orders/' + url_name = "kfet.order" + url_expected = "/k-fet/orders/" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Category') - article = Article.objects.create(name='Article', category=category) + category = ArticleCategory.objects.create(name="Category") + article = Article.objects.create(name="Article", category=category) - supplier = Supplier.objects.create(name='Supplier') + supplier = Supplier.objects.create(name="Supplier") SupplierArticle.objects.create(supplier=supplier, article=article) self.order = Order.objects.create(supplier=supplier) OrderArticle.objects.create( - order=self.order, - article=article, - quantity_ordered=24, + order=self.order, article=article, quantity_ordered=24 ) def test_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - orders = r.context['orders'] - self.assertQuerysetEqual( - orders, - map(repr, [self.order]), - ) + orders = r.context["orders"] + self.assertQuerysetEqual(orders, map(repr, [self.order])) class OrderReadViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.order.read' + url_name = "kfet.order.read" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.order.pk} + return {"pk": self.order.pk} @property def url_expected(self): - return '/k-fet/orders/{}'.format(self.order.pk) + return "/k-fet/orders/{}".format(self.order.pk) def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Category') - article = Article.objects.create(name='Article', category=category) + category = ArticleCategory.objects.create(name="Category") + article = Article.objects.create(name="Article", category=category) - supplier = Supplier.objects.create(name='Supplier') + supplier = Supplier.objects.create(name="Supplier") SupplierArticle.objects.create(supplier=supplier, article=article) self.order = Order.objects.create(supplier=supplier) OrderArticle.objects.create( - order=self.order, - article=article, - quantity_ordered=24, + order=self.order, article=article, quantity_ordered=24 ) def test_ok(self): @@ -3443,41 +3465,37 @@ class OrderReadViewTests(ViewTestCaseMixin, TestCase): class SupplierUpdateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.order.supplier.update' + url_name = "kfet.order.supplier.update" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.supplier.pk} + return {"pk": self.supplier.pk} @property def url_expected(self): - return '/k-fet/orders/suppliers/{}/edit'.format(self.supplier.pk) + return "/k-fet/orders/suppliers/{}/edit".format(self.supplier.pk) def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.change_supplier', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.change_supplier"])} @property def post_data(self): return { - 'name': 'The Supplier', - 'phone': '', - 'comment': '', - 'address': '', - 'email': '', + "name": "The Supplier", + "phone": "", + "comment": "", + "address": "", + "email": "", } def setUp(self): super().setUp() - self.supplier = Supplier.objects.create(name='Supplier') + self.supplier = Supplier.objects.create(name="Supplier") def test_get_ok(self): r = self.client.get(self.url) @@ -3485,13 +3503,13 @@ class SupplierUpdateViewTests(ViewTestCaseMixin, TestCase): def test_post_ok(self): client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.order')) + self.assertRedirects(r, reverse("kfet.order")) self.supplier.refresh_from_db() - self.assertEqual(self.supplier.name, 'The Supplier') + self.assertEqual(self.supplier.name, "The Supplier") def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -3499,67 +3517,59 @@ class SupplierUpdateViewTests(ViewTestCaseMixin, TestCase): class OrderCreateViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.order.new' + url_name = "kfet.order.new" - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.supplier.pk} + return {"pk": self.supplier.pk} @property def url_expected(self): - return '/k-fet/orders/suppliers/{}/new-order'.format(self.supplier.pk) + return "/k-fet/orders/suppliers/{}/new-order".format(self.supplier.pk) def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=['kfet.add_order']), - } + return {"team1": create_team("team1", "101", perms=["kfet.add_order"])} @property def post_data(self): return { # Formset management - 'form-TOTAL_FORMS': '1', - 'form-INITIAL_FORMS': '1', - 'form-MIN_NUM_FORMS': '0', - 'form-MAX_NUM_FORMS': '1000', + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "1", + "form-MIN_NUM_FORMS": "0", + "form-MAX_NUM_FORMS": "1000", # Article - 'form-0-article': self.article.pk, - 'form-0-quantity_ordered': '20', + "form-0-article": self.article.pk, + "form-0-quantity_ordered": "20", } def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Category') - self.article = Article.objects.create( - name='Article', - category=category, - ) + category = ArticleCategory.objects.create(name="Category") + self.article = Article.objects.create(name="Article", category=category) - self.supplier = Supplier.objects.create(name='Supplier') - SupplierArticle.objects.create( - supplier=self.supplier, - article=self.article, - ) + self.supplier = Supplier.objects.create(name="Supplier") + SupplierArticle.objects.create(supplier=self.supplier, article=self.article) def test_get_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - @mock.patch('django.utils.timezone.now') + @mock.patch("django.utils.timezone.now") def test_post_ok(self, mock_now): mock_now.return_value = self.now client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) order = Order.objects.get(at=self.now) - self.assertRedirects(r, reverse('kfet.order.read', args=[order.pk])) + self.assertRedirects(r, reverse("kfet.order.read", args=[order.pk])) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) @@ -3567,95 +3577,80 @@ class OrderCreateViewTests(ViewTestCaseMixin, TestCase): class OrderToInventoryViewTests(ViewTestCaseMixin, TestCase): - url_name = 'kfet.order.to_inventory' + url_name = "kfet.order.to_inventory" - http_methods = ['GET', 'POST'] + http_methods = ["GET", "POST"] - auth_user = 'team' - auth_forbidden = [None, 'user'] + auth_user = "team" + auth_forbidden = [None, "user"] @property def url_kwargs(self): - return {'pk': self.order.pk} + return {"pk": self.order.pk} @property def url_expected(self): - return '/k-fet/orders/{}/to_inventory'.format(self.order.pk) + return "/k-fet/orders/{}/to_inventory".format(self.order.pk) def get_users_extra(self): - return { - 'team1': create_team('team1', '101', perms=[ - 'kfet.order_to_inventory', - ]), - } + return {"team1": create_team("team1", "101", perms=["kfet.order_to_inventory"])} @property def post_data(self): return { # Formset mangaement - 'form-TOTAL_FORMS': '1', - 'form-INITIAL_FORMS': '1', - 'form-MIN_NUM_FORMS': '0', - 'form-MAX_NUM_FORMS': '1000', + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "1", + "form-MIN_NUM_FORMS": "0", + "form-MAX_NUM_FORMS": "1000", # Article 1 - 'form-0-article': self.article.pk, - 'form-0-quantity_received': '20', - 'form-0-price_HT': '', - 'form-0-TVA': '', - 'form-0-rights': '', + "form-0-article": self.article.pk, + "form-0-quantity_received": "20", + "form-0-price_HT": "", + "form-0-TVA": "", + "form-0-rights": "", } def setUp(self): super().setUp() - category = ArticleCategory.objects.create(name='Category') - self.article = Article.objects.create( - name='Article', - category=category, - ) + category = ArticleCategory.objects.create(name="Category") + self.article = Article.objects.create(name="Article", category=category) - supplier = Supplier.objects.create(name='Supplier') + supplier = Supplier.objects.create(name="Supplier") SupplierArticle.objects.create(supplier=supplier, article=self.article) self.order = Order.objects.create(supplier=supplier) OrderArticle.objects.create( - order=self.order, - article=self.article, - quantity_ordered=24, + order=self.order, article=self.article, quantity_ordered=24 ) def test_get_ok(self): r = self.client.get(self.url) self.assertEqual(r.status_code, 200) - @mock.patch('django.utils.timezone.now') + @mock.patch("django.utils.timezone.now") def test_post_ok(self, mock_now): mock_now.return_value = self.now client = Client() - client.login(username='team1', password='team1') + client.login(username="team1", password="team1") r = client.post(self.url, self.post_data) - self.assertRedirects(r, reverse('kfet.order')) + self.assertRedirects(r, reverse("kfet.order")) inventory = Inventory.objects.first() - self.assertInstanceExpected(inventory, { - 'by': self.accounts['team1'], - 'at': self.now, - 'order': self.order, - }) - self.assertQuerysetEqual( - inventory.articles.all(), - map(repr, [self.article]), + self.assertInstanceExpected( + inventory, + {"by": self.accounts["team1"], "at": self.now, "order": self.order}, ) + self.assertQuerysetEqual(inventory.articles.all(), map(repr, [self.article])) compte = InventoryArticle.objects.get(article=self.article) - self.assertInstanceExpected(compte, { - 'stock_old': 0, - 'stock_new': 20, - 'stock_error': 0, - }) + self.assertInstanceExpected( + compte, {"stock_old": 0, "stock_new": 20, "stock_error": 0} + ) def test_post_forbidden(self): r = self.client.post(self.url, self.post_data) diff --git a/kfet/tests/testcases.py b/kfet/tests/testcases.py index 3a69e9ca..36a4ab65 100644 --- a/kfet/tests/testcases.py +++ b/kfet/tests/testcases.py @@ -37,34 +37,32 @@ class TestCaseMixin: full_path = request.get_full_path() querystring = QueryDict(mutable=True) - querystring['next'] = full_path + querystring["next"] = full_path - login_url = '/login?' + querystring.urlencode(safe='/') + login_url = "/login?" + querystring.urlencode(safe="/") # We don't focus on what the login view does. # So don't fetch the redirect. - self.assertRedirects( - response, login_url, - fetch_redirect_response=False, - ) + self.assertRedirects(response, login_url, fetch_redirect_response=False) except AssertionError: raise AssertionError( "%(http_method)s request at %(path)s should be forbidden for " "%(username)s user.\n" "Response isn't 403, nor a redirect to login view. Instead, " - "response code is %(code)d." % { - 'http_method': request.method, - 'path': request.get_full_path(), - 'username': ( + "response code is %(code)d." + % { + "http_method": request.method, + "path": request.get_full_path(), + "username": ( "'{}'".format(request.user) if request.user.is_authenticated - else 'anonymous' + else "anonymous" ), - 'code': response.status_code, + "code": response.status_code, } ) - def assertForbiddenKfet(self, response, form_ctx='form'): + def assertForbiddenKfet(self, response, form_ctx="form"): """ Test that a response (retrieved with a Client) contains error due to lack of kfet permissions. @@ -83,7 +81,7 @@ class TestCaseMixin: form = response.context[form_ctx] self.assertIn("Permission refusée", form.non_field_errors()) except (AssertionError, AttributeError, KeyError): - messages = [str(msg) for msg in response.context['messages']] + messages = [str(msg) for msg in response.context["messages"]] self.assertIn("Permission refusée", messages) except AssertionError: request = response.wsgi_request @@ -91,15 +89,16 @@ class TestCaseMixin: "%(http_method)s request at %(path)s should raise an error " "for %(username)s user.\n" "Cannot find any errors in non-field errors of form " - "'%(form_ctx)s', nor in messages." % { - 'http_method': request.method, - 'path': request.get_full_path(), - 'username': ( + "'%(form_ctx)s', nor in messages." + % { + "http_method": request.method, + "path": request.get_full_path(), + "username": ( "'%s'" % request.user if request.user.is_authenticated - else 'anonymous' + else "anonymous" ), - 'form_ctx': form_ctx, + "form_ctx": form_ctx, } ) @@ -131,10 +130,9 @@ class TestCaseMixin: if type(expected) == dict: parsed = urlparse(actual) for part, expected_part in expected.items(): - if part == 'query': + if part == "query": self.assertDictEqual( - parse_qs(parsed.query), - expected.get('query', {}), + parse_qs(parsed.query), expected.get("query", {}) ) else: self.assertEqual(getattr(parsed, part), expected_part) @@ -215,10 +213,11 @@ class ViewTestCaseMixin(TestCaseMixin): can be given by defining an attribute '_data'. """ + url_name = None url_expected = None - http_methods = ['GET'] + http_methods = ["GET"] auth_user = None auth_forbidden = [] @@ -232,7 +231,7 @@ class ViewTestCaseMixin(TestCaseMixin): # Signals handlers on login/logout send messages. # Due to the way the Django' test Client performs login, this raise an # error. As workaround, we mock the Django' messages module. - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) @@ -268,14 +267,14 @@ class ViewTestCaseMixin(TestCaseMixin): # Format desc: username, password, trigramme users_base = { # user, user, 000 - 'user': create_user(), + "user": create_user(), # team, team, 100 - 'team': create_team(), + "team": create_team(), # root, root, 200 - 'root': create_root(), + "root": create_root(), } if self.with_liq: - users_base['liq'] = create_user('liq', 'LIQ') + users_base["liq"] = create_user("liq", "LIQ") return users_base @cached_property @@ -300,7 +299,7 @@ class ViewTestCaseMixin(TestCaseMixin): def register_user(self, label, user): self.users[label] = user - if hasattr(user.profile, 'account_kfet'): + if hasattr(user.profile, "account_kfet"): self.accounts[label] = user.profile.account_kfet def get_user(self, label): @@ -310,22 +309,25 @@ class ViewTestCaseMixin(TestCaseMixin): @property def urls_conf(self): - return [{ - 'name': self.url_name, - 'args': getattr(self, 'url_args', []), - 'kwargs': getattr(self, 'url_kwargs', {}), - 'expected': self.url_expected, - }] + return [ + { + "name": self.url_name, + "args": getattr(self, "url_args", []), + "kwargs": getattr(self, "url_kwargs", {}), + "expected": self.url_expected, + } + ] @property def t_urls(self): return [ reverse( - url_conf['name'], - args=url_conf.get('args', []), - kwargs=url_conf.get('kwargs', {}), + url_conf["name"], + args=url_conf.get("args", []), + kwargs=url_conf.get("kwargs", {}), ) - for url_conf in self.urls_conf] + for url_conf in self.urls_conf + ] @property def url(self): @@ -333,7 +335,7 @@ class ViewTestCaseMixin(TestCaseMixin): def test_urls(self): for url, conf in zip(self.t_urls, self.urls_conf): - self.assertEqual(url, conf['expected']) + self.assertEqual(url, conf["expected"]) def test_forbidden(self): for method in self.http_methods: @@ -348,7 +350,7 @@ class ViewTestCaseMixin(TestCaseMixin): client.login(username=user, password=user) send_request = getattr(client, method) - data = getattr(self, '{}_data'.format(method), {}) + data = getattr(self, "{}_data".format(method), {}) r = send_request(url, data) self.assertForbidden(r) diff --git a/kfet/tests/utils.py b/kfet/tests/utils.py index f3222e14..f1b6933a 100644 --- a/kfet/tests/utils.py +++ b/kfet/tests/utils.py @@ -3,7 +3,6 @@ from django.contrib.auth.models import Permission from ..models import Account - User = get_user_model() @@ -23,27 +22,27 @@ def _create_user_and_account(user_attrs, account_attrs, perms=None): the account password is 'kfetpwd_'. """ - user_pwd = user_attrs.pop('password', user_attrs['username']) + user_pwd = user_attrs.pop("password", user_attrs["username"]) user = User.objects.create(**user_attrs) user.set_password(user_pwd) user.save() - account_attrs['cofprofile'] = user.profile - kfet_pwd = account_attrs.pop('password', 'kfetpwd_{}'.format(user_pwd)) + account_attrs["cofprofile"] = user.profile + kfet_pwd = account_attrs.pop("password", "kfetpwd_{}".format(user_pwd)) account = Account.objects.create(**account_attrs) if perms is not None: user = user_add_perms(user, perms) - if 'kfet.is_team' in perms: + if "kfet.is_team" in perms: account.change_pwd(kfet_pwd) account.save() return user -def create_user(username='user', trigramme='000', **kwargs): +def create_user(username="user", trigramme="000", **kwargs): """ Create a user without any permission and its kfet account. @@ -65,20 +64,20 @@ def create_user(username='user', trigramme='000', **kwargs): * trigramme: 000 """ - user_attrs = kwargs.setdefault('user_attrs', {}) + user_attrs = kwargs.setdefault("user_attrs", {}) - user_attrs.setdefault('username', username) - user_attrs.setdefault('first_name', 'first') - user_attrs.setdefault('last_name', 'last') - user_attrs.setdefault('email', 'mail@user.net') + user_attrs.setdefault("username", username) + user_attrs.setdefault("first_name", "first") + user_attrs.setdefault("last_name", "last") + user_attrs.setdefault("email", "mail@user.net") - account_attrs = kwargs.setdefault('account_attrs', {}) - account_attrs.setdefault('trigramme', trigramme) + account_attrs = kwargs.setdefault("account_attrs", {}) + account_attrs.setdefault("trigramme", trigramme) return _create_user_and_account(**kwargs) -def create_team(username='team', trigramme='100', **kwargs): +def create_team(username="team", trigramme="100", **kwargs): """ Create a user, member of the kfet team, and its kfet account. @@ -101,23 +100,23 @@ def create_team(username='team', trigramme='100', **kwargs): * kfet password: kfetpwd_team """ - user_attrs = kwargs.setdefault('user_attrs', {}) + user_attrs = kwargs.setdefault("user_attrs", {}) - user_attrs.setdefault('username', username) - user_attrs.setdefault('first_name', 'team') - user_attrs.setdefault('last_name', 'member') - user_attrs.setdefault('email', 'mail@team.net') + user_attrs.setdefault("username", username) + user_attrs.setdefault("first_name", "team") + user_attrs.setdefault("last_name", "member") + user_attrs.setdefault("email", "mail@team.net") - account_attrs = kwargs.setdefault('account_attrs', {}) - account_attrs.setdefault('trigramme', trigramme) + account_attrs = kwargs.setdefault("account_attrs", {}) + account_attrs.setdefault("trigramme", trigramme) - perms = kwargs.setdefault('perms', []) - perms.append('kfet.is_team') + perms = kwargs.setdefault("perms", []) + perms.append("kfet.is_team") return _create_user_and_account(**kwargs) -def create_root(username='root', trigramme='200', **kwargs): +def create_root(username="root", trigramme="200", **kwargs): """ Create a superuser and its kfet account. @@ -141,16 +140,16 @@ def create_root(username='root', trigramme='200', **kwargs): * kfet password: kfetpwd_root """ - user_attrs = kwargs.setdefault('user_attrs', {}) + user_attrs = kwargs.setdefault("user_attrs", {}) - user_attrs.setdefault('username', username) - user_attrs.setdefault('first_name', 'super') - user_attrs.setdefault('last_name', 'user') - user_attrs.setdefault('email', 'mail@root.net') - user_attrs['is_superuser'] = user_attrs['is_staff'] = True + user_attrs.setdefault("username", username) + user_attrs.setdefault("first_name", "super") + user_attrs.setdefault("last_name", "user") + user_attrs.setdefault("email", "mail@root.net") + user_attrs["is_superuser"] = user_attrs["is_staff"] = True - account_attrs = kwargs.setdefault('account_attrs', {}) - account_attrs.setdefault('trigramme', trigramme) + account_attrs = kwargs.setdefault("account_attrs", {}) + account_attrs.setdefault("trigramme", trigramme) return _create_user_and_account(**kwargs) @@ -159,10 +158,9 @@ def get_perms(*labels): """Return Permission instances from a list of '.'.""" perms = {} for label in set(labels): - app_label, codename = label.split('.', 1) + app_label, codename = label.split(".", 1) perms[label] = Permission.objects.get( - content_type__app_label=app_label, - codename=codename, + content_type__app_label=app_label, codename=codename ) return perms diff --git a/kfet/urls.py b/kfet/urls.py index 98d0bbf9..531e0cc9 100644 --- a/kfet/urls.py +++ b/kfet/urls.py @@ -4,238 +4,287 @@ from django.contrib.auth.decorators import permission_required from kfet import autocomplete, views from kfet.decorators import teamkfet_required - urlpatterns = [ - url(r'^login/generic$', views.login_generic, - name='kfet.login.generic'), - url(r'^history$', views.history, - name='kfet.history'), - + url(r"^login/generic$", views.login_generic, name="kfet.login.generic"), + url(r"^history$", views.history, name="kfet.history"), # ----- # Account urls # ----- - # Account - General - url(r'^accounts/$', views.account, - name='kfet.account'), - url(r'^accounts/is_validandfree$', views.account_is_validandfree_ajax, - name='kfet.account.is_validandfree.ajax'), - + url(r"^accounts/$", views.account, name="kfet.account"), + url( + r"^accounts/is_validandfree$", + views.account_is_validandfree_ajax, + name="kfet.account.is_validandfree.ajax", + ), # Account - Create - url(r'^accounts/new$', views.account_create, - name='kfet.account.create'), - url(r'^accounts/new/user/(?P.+)$', views.account_create_ajax, - name='kfet.account.create.fromuser'), - url(r'^accounts/new/clipper/(?P[\w-]+)/(?P.*)$', + url(r"^accounts/new$", views.account_create, name="kfet.account.create"), + url( + r"^accounts/new/user/(?P.+)$", views.account_create_ajax, - name='kfet.account.create.fromclipper'), - url(r'^accounts/new/empty$', views.account_create_ajax, - name='kfet.account.create.empty'), - url(r'^autocomplete/account_new$', autocomplete.account_create, - name='kfet.account.create.autocomplete'), - + name="kfet.account.create.fromuser", + ), + url( + r"^accounts/new/clipper/(?P[\w-]+)/(?P.*)$", + views.account_create_ajax, + name="kfet.account.create.fromclipper", + ), + url( + r"^accounts/new/empty$", + views.account_create_ajax, + name="kfet.account.create.empty", + ), + url( + r"^autocomplete/account_new$", + autocomplete.account_create, + name="kfet.account.create.autocomplete", + ), # Account - Search - url(r'^autocomplete/account_search$', autocomplete.account_search, - name='kfet.account.search.autocomplete'), - + url( + r"^autocomplete/account_search$", + autocomplete.account_search, + name="kfet.account.search.autocomplete", + ), # Account - Read - url(r'^accounts/(?P.{3})$', views.account_read, - name='kfet.account.read'), - + url( + r"^accounts/(?P.{3})$", views.account_read, name="kfet.account.read" + ), # Account - Update - url(r'^accounts/(?P.{3})/edit$', views.account_update, - name='kfet.account.update'), - + url( + r"^accounts/(?P.{3})/edit$", + views.account_update, + name="kfet.account.update", + ), # Account - Groups - url(r'^accounts/groups$', views.account_group, - name='kfet.account.group'), - url(r'^accounts/groups/new$', - permission_required('kfet.manage_perms') - (views.AccountGroupCreate.as_view()), - name='kfet.account.group.create'), - url(r'^accounts/groups/(?P\d+)/edit$', - permission_required('kfet.manage_perms') - (views.AccountGroupUpdate.as_view()), - name='kfet.account.group.update'), - - url(r'^accounts/negatives$', - permission_required('kfet.view_negs') - (views.AccountNegativeList.as_view()), - name='kfet.account.negative'), - + url(r"^accounts/groups$", views.account_group, name="kfet.account.group"), + url( + r"^accounts/groups/new$", + permission_required("kfet.manage_perms")(views.AccountGroupCreate.as_view()), + name="kfet.account.group.create", + ), + url( + r"^accounts/groups/(?P\d+)/edit$", + permission_required("kfet.manage_perms")(views.AccountGroupUpdate.as_view()), + name="kfet.account.group.update", + ), + url( + r"^accounts/negatives$", + permission_required("kfet.view_negs")(views.AccountNegativeList.as_view()), + name="kfet.account.negative", + ), # Account - Statistics - url(r'^accounts/(?P.{3})/stat/operations/list$', + url( + r"^accounts/(?P.{3})/stat/operations/list$", views.AccountStatOperationList.as_view(), - name='kfet.account.stat.operation.list'), - url(r'^accounts/(?P.{3})/stat/operations$', + name="kfet.account.stat.operation.list", + ), + url( + r"^accounts/(?P.{3})/stat/operations$", views.AccountStatOperation.as_view(), - name='kfet.account.stat.operation'), - - url(r'^accounts/(?P.{3})/stat/balance/list$', + name="kfet.account.stat.operation", + ), + url( + r"^accounts/(?P.{3})/stat/balance/list$", views.AccountStatBalanceList.as_view(), - name='kfet.account.stat.balance.list'), - url(r'^accounts/(?P.{3})/stat/balance$', + name="kfet.account.stat.balance.list", + ), + url( + r"^accounts/(?P.{3})/stat/balance$", views.AccountStatBalance.as_view(), - name='kfet.account.stat.balance'), - + name="kfet.account.stat.balance", + ), # ----- # Checkout urls # ----- - # Checkout - General - url('^checkouts/$', + url( + "^checkouts/$", teamkfet_required(views.CheckoutList.as_view()), - name='kfet.checkout'), + name="kfet.checkout", + ), # Checkout - Create - url('^checkouts/new$', + url( + "^checkouts/new$", teamkfet_required(views.CheckoutCreate.as_view()), - name='kfet.checkout.create'), + name="kfet.checkout.create", + ), # Checkout - Read - url('^checkouts/(?P\d+)$', + url( + "^checkouts/(?P\d+)$", teamkfet_required(views.CheckoutRead.as_view()), - name='kfet.checkout.read'), + name="kfet.checkout.read", + ), # Checkout - Update - url('^checkouts/(?P\d+)/edit$', + url( + "^checkouts/(?P\d+)/edit$", teamkfet_required(views.CheckoutUpdate.as_view()), - name='kfet.checkout.update'), - + name="kfet.checkout.update", + ), # ----- # Checkout Statement urls # ----- - # Checkout Statement - General - url('^checkouts/statements/$', + url( + "^checkouts/statements/$", teamkfet_required(views.CheckoutStatementList.as_view()), - name='kfet.checkoutstatement'), + name="kfet.checkoutstatement", + ), # Checkout Statement - Create - url('^checkouts/(?P\d+)/statements/add', + url( + "^checkouts/(?P\d+)/statements/add", teamkfet_required(views.CheckoutStatementCreate.as_view()), - name='kfet.checkoutstatement.create'), + name="kfet.checkoutstatement.create", + ), # Checkout Statement - Update - url('^checkouts/(?P\d+)/statements/(?P\d+)/edit', + url( + "^checkouts/(?P\d+)/statements/(?P\d+)/edit", teamkfet_required(views.CheckoutStatementUpdate.as_view()), - name='kfet.checkoutstatement.update'), - + name="kfet.checkoutstatement.update", + ), # ----- # Article urls # ----- - # Category - General - url('^categories/$', + url( + "^categories/$", teamkfet_required(views.CategoryList.as_view()), - name='kfet.category'), + name="kfet.category", + ), # Category - Update - url('^categories/(?P\d+)/edit$', + url( + "^categories/(?P\d+)/edit$", teamkfet_required(views.CategoryUpdate.as_view()), - name='kfet.category.update'), + name="kfet.category.update", + ), # Article - General - url('^articles/$', + url( + "^articles/$", teamkfet_required(views.ArticleList.as_view()), - name='kfet.article'), + name="kfet.article", + ), # Article - Create - url('^articles/new$', + url( + "^articles/new$", teamkfet_required(views.ArticleCreate.as_view()), - name='kfet.article.create'), + name="kfet.article.create", + ), # Article - Read - url('^articles/(?P\d+)$', + url( + "^articles/(?P\d+)$", teamkfet_required(views.ArticleRead.as_view()), - name='kfet.article.read'), + name="kfet.article.read", + ), # Article - Update - url('^articles/(?P\d+)/edit$', + url( + "^articles/(?P\d+)/edit$", teamkfet_required(views.ArticleUpdate.as_view()), - name='kfet.article.update'), + name="kfet.article.update", + ), # Article - Statistics - url(r'^articles/(?P\d+)/stat/sales/list$', + url( + r"^articles/(?P\d+)/stat/sales/list$", views.ArticleStatSalesList.as_view(), - name='kfet.article.stat.sales.list'), - url(r'^articles/(?P\d+)/stat/sales$', + name="kfet.article.stat.sales.list", + ), + url( + r"^articles/(?P\d+)/stat/sales$", views.ArticleStatSales.as_view(), - name='kfet.article.stat.sales'), - + name="kfet.article.stat.sales", + ), # ----- # K-Psul urls # ----- - - url('^k-psul/$', views.kpsul, name='kfet.kpsul'), - url('^k-psul/checkout_data$', views.kpsul_checkout_data, - name='kfet.kpsul.checkout_data'), - url('^k-psul/perform_operations$', views.kpsul_perform_operations, - name='kfet.kpsul.perform_operations'), - url('^k-psul/cancel_operations$', views.kpsul_cancel_operations, - name='kfet.kpsul.cancel_operations'), - url('^k-psul/articles_data', views.kpsul_articles_data, - name='kfet.kpsul.articles_data'), - url('^k-psul/update_addcost$', views.kpsul_update_addcost, - name='kfet.kpsul.update_addcost'), - url('^k-psul/get_settings$', views.kpsul_get_settings, - name='kfet.kpsul.get_settings'), - + url("^k-psul/$", views.kpsul, name="kfet.kpsul"), + url( + "^k-psul/checkout_data$", + views.kpsul_checkout_data, + name="kfet.kpsul.checkout_data", + ), + url( + "^k-psul/perform_operations$", + views.kpsul_perform_operations, + name="kfet.kpsul.perform_operations", + ), + url( + "^k-psul/cancel_operations$", + views.kpsul_cancel_operations, + name="kfet.kpsul.cancel_operations", + ), + url( + "^k-psul/articles_data", + views.kpsul_articles_data, + name="kfet.kpsul.articles_data", + ), + url( + "^k-psul/update_addcost$", + views.kpsul_update_addcost, + name="kfet.kpsul.update_addcost", + ), + url( + "^k-psul/get_settings$", + views.kpsul_get_settings, + name="kfet.kpsul.get_settings", + ), # ----- # JSON urls # ----- - - url(r'^history.json$', views.history_json, - name='kfet.history.json'), - url(r'^accounts/read.json$', views.account_read_json, - name='kfet.account.read.json'), - - + url(r"^history.json$", views.history_json, name="kfet.history.json"), + url( + r"^accounts/read.json$", views.account_read_json, name="kfet.account.read.json" + ), # ----- # Settings urls # ----- - - url(r'^settings/$', views.config_list, - name='kfet.settings'), - url(r'^settings/edit$', views.config_update, - name='kfet.settings.update'), - - + url(r"^settings/$", views.config_list, name="kfet.settings"), + url(r"^settings/edit$", views.config_update, name="kfet.settings.update"), # ----- # Transfers urls # ----- - - url(r'^transfers/$', views.transfers, - name='kfet.transfers'), - url(r'^transfers/new$', views.transfers_create, - name='kfet.transfers.create'), - url(r'^transfers/perform$', views.perform_transfers, - name='kfet.transfers.perform'), - url(r'^transfers/cancel$', views.cancel_transfers, - name='kfet.transfers.cancel'), - + url(r"^transfers/$", views.transfers, name="kfet.transfers"), + url(r"^transfers/new$", views.transfers_create, name="kfet.transfers.create"), + url(r"^transfers/perform$", views.perform_transfers, name="kfet.transfers.perform"), + url(r"^transfers/cancel$", views.cancel_transfers, name="kfet.transfers.cancel"), # ----- # Inventories urls # ----- - - url(r'^inventaires/$', + url( + r"^inventaires/$", teamkfet_required(views.InventoryList.as_view()), - name='kfet.inventory'), - url(r'^inventaires/new$', views.inventory_create, - name='kfet.inventory.create'), - url(r'^inventaires/(?P\d+)$', + name="kfet.inventory", + ), + url(r"^inventaires/new$", views.inventory_create, name="kfet.inventory.create"), + url( + r"^inventaires/(?P\d+)$", teamkfet_required(views.InventoryRead.as_view()), - name='kfet.inventory.read'), - + name="kfet.inventory.read", + ), # ----- # Order urls # ----- - - url(r'^orders/$', - teamkfet_required(views.OrderList.as_view()), - name='kfet.order'), - url(r'^orders/(?P\d+)$', + url(r"^orders/$", teamkfet_required(views.OrderList.as_view()), name="kfet.order"), + url( + r"^orders/(?P\d+)$", teamkfet_required(views.OrderRead.as_view()), - name='kfet.order.read'), - url(r'^orders/suppliers/(?P\d+)/edit$', + name="kfet.order.read", + ), + url( + r"^orders/suppliers/(?P\d+)/edit$", teamkfet_required(views.SupplierUpdate.as_view()), - name='kfet.order.supplier.update'), - url(r'^orders/suppliers/(?P\d+)/new-order$', views.order_create, - name='kfet.order.new'), - url(r'^orders/(?P\d+)/to_inventory$', views.order_to_inventory, - name='kfet.order.to_inventory'), + name="kfet.order.supplier.update", + ), + url( + r"^orders/suppliers/(?P\d+)/new-order$", + views.order_create, + name="kfet.order.new", + ), + url( + r"^orders/(?P\d+)/to_inventory$", + views.order_to_inventory, + name="kfet.order.to_inventory", + ), ] urlpatterns += [ # K-Fêt Open urls - url('^open/', include('kfet.open.urls')), + url("^open/", include("kfet.open.urls")) ] diff --git a/kfet/utils.py b/kfet/utils.py index 3d06bb0b..0c4f170a 100644 --- a/kfet/utils.py +++ b/kfet/utils.py @@ -1,11 +1,10 @@ -import math import json - -from django.core.cache import cache -from django.core.serializers.json import DjangoJSONEncoder +import math from channels.channel import Group from channels.generic.websockets import JsonWebsocketConsumer +from django.core.cache import cache +from django.core.serializers.json import DjangoJSONEncoder from .config import kfet_config @@ -16,8 +15,10 @@ def to_ukf(balance, is_cof=False): grant = (1 + subvention / 100) if is_cof else 1 return math.floor(balance * 10 * grant) + # Storage + class CachedMixin: """Object with cached properties. @@ -27,8 +28,9 @@ class CachedMixin: cache_prefix (str): Used to prefix keys in cache. """ + cached = {} - cache_prefix = '' + cache_prefix = "" def __init__(self, cache_prefix=None, *args, **kwargs): super().__init__(*args, **kwargs) @@ -36,12 +38,12 @@ class CachedMixin: self.cache_prefix = cache_prefix def cachekey(self, attr): - return '{}__{}'.format(self.cache_prefix, attr) + return "{}__{}".format(self.cache_prefix, attr) def __getattr__(self, attr): if attr in self.cached: return cache.get(self.cachekey(attr), self.cached.get(attr)) - elif hasattr(super(), '__getattr__'): + elif hasattr(super(), "__getattr__"): return super().__getattr__(attr) else: raise AttributeError("can't get attribute") @@ -49,19 +51,18 @@ class CachedMixin: def __setattr__(self, attr, value): if attr in self.cached: cache.set(self.cachekey(attr), value) - elif hasattr(super(), '__setattr__'): + elif hasattr(super(), "__setattr__"): super().__setattr__(attr, value) else: raise AttributeError("can't set attribute") def clear_cache(self): - cache.delete_many([ - self.cachekey(attr) for attr in self.cached.keys() - ]) + cache.delete_many([self.cachekey(attr) for attr in self.cached.keys()]) # Consumers + class DjangoJsonWebsocketConsumer(JsonWebsocketConsumer): """Custom Json Websocket Consumer. @@ -84,6 +85,7 @@ class PermConsumerMixin: message.user is appended as argument to each connection_groups method call. """ + http_user = True # Enable message.user perms_connect = [] @@ -107,7 +109,9 @@ class PermConsumerMixin: # We add user to connection_groups call. groups = self.connection_groups(user=message.user, **kwargs) for group in groups: - Group(group, channel_layer=message.channel_layer).discard(message.reply_channel) + Group(group, channel_layer=message.channel_layer).discard( + message.reply_channel + ) self.disconnect(message, **kwargs) def connection_groups(self, user, **kwargs): diff --git a/kfet/views.py b/kfet/views.py index f3e70dde..088f867e 100644 --- a/kfet/views.py +++ b/kfet/views.py @@ -1,55 +1,92 @@ import ast -from urllib.parse import urlencode - -from django.shortcuts import render, get_object_or_404, redirect -from django.core.exceptions import PermissionDenied -from django.core.cache import cache -from django.views.generic import ListView, DetailView, TemplateView, FormView -from django.views.generic.detail import BaseDetailView -from django.views.generic.edit import CreateView, UpdateView -from django.core.urlresolvers import reverse, reverse_lazy -from django.contrib import messages -from django.contrib.messages.views import SuccessMessageMixin -from django.contrib.auth.decorators import login_required, permission_required -from django.contrib.auth.models import User, Permission -from django.http import JsonResponse, Http404 -from django.forms import formset_factory -from django.db import transaction -from django.db.models import F, Sum, Prefetch, Count -from django.db.models.functions import Coalesce -from django.utils import timezone -from django.utils.decorators import method_decorator - -from gestioncof.models import CofProfile - -from kfet.config import kfet_config -from kfet.decorators import teamkfet_required -from kfet.models import ( - Account, Checkout, Article, AccountNegative, - CheckoutStatement, Supplier, SupplierArticle, Inventory, - InventoryArticle, Order, OrderArticle, Operation, OperationGroup, - TransferGroup, Transfer, ArticleCategory) -from kfet.forms import ( - AccountTriForm, AccountBalanceForm, AccountNoTriForm, UserForm, CofForm, - UserRestrictTeamForm, UserGroupForm, AccountForm, CofRestrictForm, - AccountPwdForm, AccountNegativeForm, UserRestrictForm, AccountRestrictForm, - CheckoutForm, CheckoutRestrictForm, CheckoutStatementCreateForm, - CheckoutStatementUpdateForm, ArticleForm, ArticleRestrictForm, - KPsulOperationGroupForm, KPsulAccountForm, KPsulCheckoutForm, - KPsulOperationFormSet, AddcostForm, FilterHistoryForm, - TransferFormSet, InventoryArticleForm, OrderArticleForm, - OrderArticleToInventoryForm, CategoryForm, KFetConfigForm - ) -from collections import defaultdict -from kfet import consumers -from datetime import timedelta -from decimal import Decimal import heapq import statistics -from kfet.statistic import ScaleMixin, last_stats_manifest, tot_ventes, WeekScale +from collections import defaultdict +from datetime import timedelta +from decimal import Decimal +from urllib.parse import urlencode + +from django.contrib import messages +from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.models import Permission, User +from django.contrib.messages.views import SuccessMessageMixin +from django.core.cache import cache +from django.core.exceptions import PermissionDenied +from django.core.urlresolvers import reverse, reverse_lazy +from django.db import transaction +from django.db.models import Count, F, Prefetch, Sum +from django.db.models.functions import Coalesce +from django.forms import formset_factory +from django.http import Http404, JsonResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.utils import timezone +from django.utils.decorators import method_decorator +from django.views.generic import DetailView, FormView, ListView, TemplateView +from django.views.generic.detail import BaseDetailView +from django.views.generic.edit import CreateView, UpdateView + +from gestioncof.models import CofProfile +from kfet import consumers +from kfet.config import kfet_config +from kfet.decorators import teamkfet_required +from kfet.forms import ( + AccountBalanceForm, + AccountForm, + AccountNegativeForm, + AccountNoTriForm, + AccountPwdForm, + AccountRestrictForm, + AccountTriForm, + AddcostForm, + ArticleForm, + ArticleRestrictForm, + CategoryForm, + CheckoutForm, + CheckoutRestrictForm, + CheckoutStatementCreateForm, + CheckoutStatementUpdateForm, + CofForm, + CofRestrictForm, + FilterHistoryForm, + InventoryArticleForm, + KFetConfigForm, + KPsulAccountForm, + KPsulCheckoutForm, + KPsulOperationFormSet, + KPsulOperationGroupForm, + OrderArticleForm, + OrderArticleToInventoryForm, + TransferFormSet, + UserForm, + UserGroupForm, + UserRestrictForm, + UserRestrictTeamForm, +) +from kfet.models import ( + Account, + AccountNegative, + Article, + ArticleCategory, + Checkout, + CheckoutStatement, + Inventory, + InventoryArticle, + Operation, + OperationGroup, + Order, + OrderArticle, + Supplier, + SupplierArticle, + Transfer, + TransferGroup, +) +from kfet.statistic import ScaleMixin, WeekScale, last_stats_manifest, tot_ventes from .auth.views import ( # noqa - account_group, login_generic, AccountGroupCreate, AccountGroupUpdate, + AccountGroupCreate, + AccountGroupUpdate, + account_group, + login_generic, ) @@ -57,22 +94,25 @@ def put_cleaned_data_in_dict(dict, form): for field in form.cleaned_data: dict[field] = form.cleaned_data[field] + # ----- # Account views # ----- # Account - General + @login_required @teamkfet_required def account(request): - accounts = Account.objects.select_related('cofprofile__user').order_by('trigramme') - return render(request, "kfet/account.html", { 'accounts' : accounts }) + accounts = Account.objects.select_related("cofprofile__user").order_by("trigramme") + return render(request, "kfet/account.html", {"accounts": accounts}) + @login_required @teamkfet_required def account_is_validandfree_ajax(request): - if not request.GET.get("trigramme", ''): + if not request.GET.get("trigramme", ""): raise Http404 trigramme = request.GET.get("trigramme") data = Account.is_validandfree(trigramme) @@ -81,6 +121,7 @@ def account_is_validandfree_ajax(request): # Account - Create + @login_required @teamkfet_required def account_create(request): @@ -90,21 +131,28 @@ def account_create(request): trigramme_form = AccountTriForm(request.POST) # Peuplement des forms - username = request.POST.get('username') - login_clipper = request.POST.get('login_clipper') + username = request.POST.get("username") + login_clipper = request.POST.get("login_clipper") forms = get_account_create_forms( - request, username=username, login_clipper=login_clipper) + request, username=username, login_clipper=login_clipper + ) - account_form = forms['account_form'] - cof_form = forms['cof_form'] - user_form = forms['user_form'] + account_form = forms["account_form"] + cof_form = forms["cof_form"] + user_form = forms["user_form"] - if all((user_form.is_valid(), cof_form.is_valid(), - trigramme_form.is_valid(), account_form.is_valid())): + if all( + ( + user_form.is_valid(), + cof_form.is_valid(), + trigramme_form.is_valid(), + account_form.is_valid(), + ) + ): # Checking permission - if not request.user.has_perm('kfet.add_account'): - messages.error(request, 'Permission refusée') + if not request.user.has_perm("kfet.add_account"): + messages.error(request, "Permission refusée") else: data = {} # Fill data for Account.save() @@ -112,35 +160,44 @@ def account_create(request): put_cleaned_data_in_dict(data, cof_form) try: - account = trigramme_form.save(data = data) + account = trigramme_form.save(data=data) account_form = AccountNoTriForm(request.POST, instance=account) account_form.save() - messages.success(request, 'Compte créé : %s' % account.trigramme) - return redirect('kfet.account.create') + messages.success(request, "Compte créé : %s" % account.trigramme) + return redirect("kfet.account.create") except Account.UserHasAccount as e: - messages.error(request, \ - "Cet utilisateur a déjà un compte K-Fêt : %s" % e.trigramme) + messages.error( + request, + "Cet utilisateur a déjà un compte K-Fêt : %s" % e.trigramme, + ) else: - initial = { 'trigramme': request.GET.get('trigramme', '') } - trigramme_form = AccountTriForm(initial = initial) + initial = {"trigramme": request.GET.get("trigramme", "")} + trigramme_form = AccountTriForm(initial=initial) account_form = None cof_form = None user_form = None - return render(request, "kfet/account_create.html", { - 'trigramme_form': trigramme_form, - 'account_form': account_form, - 'cof_form': cof_form, - 'user_form': user_form, - }) + return render( + request, + "kfet/account_create.html", + { + "trigramme_form": trigramme_form, + "account_form": account_form, + "cof_form": cof_form, + "user_form": user_form, + }, + ) + def account_form_set_readonly_fields(user_form, cof_form): - user_form.fields['username'].widget.attrs['readonly'] = True - cof_form.fields['login_clipper'].widget.attrs['readonly'] = True - cof_form.fields['is_cof'].widget.attrs['disabled'] = True + user_form.fields["username"].widget.attrs["readonly"] = True + cof_form.fields["login_clipper"].widget.attrs["readonly"] = True + cof_form.fields["is_cof"].widget.attrs["disabled"] = True -def get_account_create_forms(request=None, username=None, login_clipper=None, - fullname=None): + +def get_account_create_forms( + request=None, username=None, login_clipper=None, fullname=None +): user = None clipper = False if login_clipper and (login_clipper == username or not username): @@ -158,26 +215,27 @@ def get_account_create_forms(request=None, username=None, login_clipper=None, # UserForm - Prefill user_initial = { - 'username' : login_clipper, - 'email' : "%s@clipper.ens.fr" % login_clipper} + "username": login_clipper, + "email": "%s@clipper.ens.fr" % login_clipper, + } if fullname: # Prefill du nom et prénom names = fullname.split() # Le premier, c'est le prénom - user_initial['first_name'] = names[0] + user_initial["first_name"] = names[0] if len(names) > 1: # Si d'autres noms -> tous dans le nom de famille - user_initial['last_name'] = " ".join(names[1:]) + user_initial["last_name"] = " ".join(names[1:]) # CofForm - Prefill - cof_initial = { 'login_clipper': login_clipper } + cof_initial = {"login_clipper": login_clipper} # Form créations if request: user_form = UserForm(request.POST, initial=user_initial) - cof_form = CofForm(request.POST, initial=cof_initial) + cof_form = CofForm(request.POST, initial=cof_initial) else: user_form = UserForm(initial=user_initial) - cof_form = CofForm(initial=cof_initial) + cof_form = CofForm(initial=cof_initial) # Protection (read-only) des champs username et login_clipper account_form_set_readonly_fields(user_form, cof_form) @@ -189,11 +247,11 @@ def get_account_create_forms(request=None, username=None, login_clipper=None, (cof, _) = CofProfile.objects.get_or_create(user=user) # UserForm + CofForm - Création à partir des instances existantes if request: - user_form = UserForm(request.POST, instance = user) - cof_form = CofForm(request.POST, instance = cof) + user_form = UserForm(request.POST, instance=user) + cof_form = CofForm(request.POST, instance=cof) else: user_form = UserForm(instance=user) - cof_form = CofForm(instance=cof) + cof_form = CofForm(instance=cof) # Protection (read-only) des champs username, login_clipper et is_cof account_form_set_readonly_fields(user_form, cof_form) except User.DoesNotExist: @@ -204,66 +262,64 @@ def get_account_create_forms(request=None, username=None, login_clipper=None, # connaît pas du tout, faut tout remplir if request: user_form = UserForm(request.POST) - cof_form = CofForm(request.POST) + cof_form = CofForm(request.POST) else: user_form = UserForm() - cof_form = CofForm() + cof_form = CofForm() # mais on laisse le username en écriture - cof_form.fields['login_clipper'].widget.attrs['readonly'] = True - cof_form.fields['is_cof'].widget.attrs['disabled'] = True + cof_form.fields["login_clipper"].widget.attrs["readonly"] = True + cof_form.fields["is_cof"].widget.attrs["disabled"] = True if request: account_form = AccountNoTriForm(request.POST) else: account_form = AccountNoTriForm() - return { - 'account_form': account_form, - 'cof_form': cof_form, - 'user_form': user_form, - } + return {"account_form": account_form, "cof_form": cof_form, "user_form": user_form} @login_required @teamkfet_required -def account_create_ajax(request, username=None, login_clipper=None, - fullname=None): +def account_create_ajax(request, username=None, login_clipper=None, fullname=None): forms = get_account_create_forms( - request=None, username=username, login_clipper=login_clipper, - fullname=fullname) - return render(request, "kfet/account_create_form.html", { - 'account_form' : forms['account_form'], - 'cof_form' : forms['cof_form'], - 'user_form' : forms['user_form'], - }) + request=None, username=username, login_clipper=login_clipper, fullname=fullname + ) + return render( + request, + "kfet/account_create_form.html", + { + "account_form": forms["account_form"], + "cof_form": forms["cof_form"], + "user_form": forms["user_form"], + }, + ) # Account - Read + @login_required def account_read(request, trigramme): account = get_object_or_404(Account, trigramme=trigramme) # Checking permissions if not account.readable or ( - not request.user.has_perm('kfet.is_team') and - request.user != account.user): + not request.user.has_perm("kfet.is_team") and request.user != account.user + ): raise PermissionDenied addcosts = ( - OperationGroup.objects - .filter(opes__addcost_for=account, - opes__canceled_at=None) - .extra({'date': "date(at)"}) - .values('date') - .annotate(sum_addcosts=Sum('opes__addcost_amount')) - .order_by('-date') + OperationGroup.objects.filter(opes__addcost_for=account, opes__canceled_at=None) + .extra({"date": "date(at)"}) + .values("date") + .annotate(sum_addcosts=Sum("opes__addcost_amount")) + .order_by("-date") + ) + + return render( + request, "kfet/account_read.html", {"account": account, "addcosts": addcosts} ) - return render(request, "kfet/account_read.html", { - 'account': account, - 'addcosts': addcosts, - }) # Account - Update @@ -273,21 +329,19 @@ def account_update(request, trigramme): account = get_object_or_404(Account, trigramme=trigramme) # Checking permissions - if not request.user.has_perm('kfet.is_team') \ - and request.user != account.user: + if not request.user.has_perm("kfet.is_team") and request.user != account.user: raise PermissionDenied - if request.user.has_perm('kfet.is_team'): + if request.user.has_perm("kfet.is_team"): user_form = UserRestrictTeamForm(instance=account.user) group_form = UserGroupForm(instance=account.user) account_form = AccountForm(instance=account) cof_form = CofRestrictForm(instance=account.cofprofile) pwd_form = AccountPwdForm() - if account.balance < 0 and not hasattr(account, 'negative'): - AccountNegative.objects.create(account=account, - start=timezone.now()) + if account.balance < 0 and not hasattr(account, "negative"): + AccountNegative.objects.create(account=account, start=timezone.now()) account.refresh_from_db() - if hasattr(account, 'negative'): + if hasattr(account, "negative"): negative_form = AccountNegativeForm(instance=account.negative) else: negative_form = None @@ -304,21 +358,23 @@ def account_update(request, trigramme): success = False missing_perm = True - if request.user.has_perm('kfet.is_team'): + if request.user.has_perm("kfet.is_team"): account_form = AccountForm(request.POST, instance=account) - cof_form = CofRestrictForm(request.POST, - instance=account.cofprofile) - user_form = UserRestrictTeamForm(request.POST, - instance=account.user) + cof_form = CofRestrictForm(request.POST, instance=account.cofprofile) + user_form = UserRestrictTeamForm(request.POST, instance=account.user) group_form = UserGroupForm(request.POST, instance=account.user) pwd_form = AccountPwdForm(request.POST) - if hasattr(account, 'negative'): - negative_form = AccountNegativeForm(request.POST, - instance=account.negative) + if hasattr(account, "negative"): + negative_form = AccountNegativeForm( + request.POST, instance=account.negative + ) - if (request.user.has_perm('kfet.change_account') - and account_form.is_valid() and cof_form.is_valid() - and user_form.is_valid()): + if ( + request.user.has_perm("kfet.change_account") + and account_form.is_valid() + and cof_form.is_valid() + and user_form.is_valid() + ): missing_perm = False data = {} # Fill data for Account.save() @@ -329,44 +385,48 @@ def account_update(request, trigramme): account_form.save(data=data) # Checking perm to update password - if (request.user.has_perm('kfet.change_account_password') - and pwd_form.is_valid()): - pwd = pwd_form.cleaned_data['pwd1'] + if ( + request.user.has_perm("kfet.change_account_password") + and pwd_form.is_valid() + ): + pwd = pwd_form.cleaned_data["pwd1"] account.change_pwd(pwd) account.save() - messages.success(request, 'Mot de passe mis à jour') + messages.success(request, "Mot de passe mis à jour") # Checking perm to manage perms - if (request.user.has_perm('kfet.manage_perms') - and group_form.is_valid()): + if request.user.has_perm("kfet.manage_perms") and group_form.is_valid(): group_form.save() # Checking perm to manage negative - if hasattr(account, 'negative'): + if hasattr(account, "negative"): balance_offset_old = 0 if account.negative.balance_offset: balance_offset_old = account.negative.balance_offset - if (hasattr(account, 'negative') - and request.user.has_perm('kfet.change_accountnegative') - and negative_form.is_valid()): - balance_offset_new = \ - negative_form.cleaned_data['balance_offset'] + if ( + hasattr(account, "negative") + and request.user.has_perm("kfet.change_accountnegative") + and negative_form.is_valid() + ): + balance_offset_new = negative_form.cleaned_data["balance_offset"] if not balance_offset_new: balance_offset_new = 0 - balance_offset_diff = (balance_offset_new - - balance_offset_old) + balance_offset_diff = balance_offset_new - balance_offset_old Account.objects.filter(pk=account.pk).update( - balance=F('balance') + balance_offset_diff) + balance=F("balance") + balance_offset_diff + ) negative_form.save() - if Account.objects.get(pk=account.pk).balance >= 0 \ - and not balance_offset_new: + if ( + Account.objects.get(pk=account.pk).balance >= 0 + and not balance_offset_new + ): AccountNegative.objects.get(account=account).delete() success = True messages.success( request, - 'Informations du compte %s mises à jour' - % account.trigramme) + "Informations du compte %s mises à jour" % account.trigramme, + ) # Modification de ses propres informations if request.user == account.user: @@ -380,75 +440,79 @@ def account_update(request, trigramme): user_form.save() account_form.save() success = True - messages.success(request, - 'Vos informations ont été mises à jour') + messages.success(request, "Vos informations ont été mises à jour") - if request.user.has_perm('kfet.is_team') \ - and pwd_form.is_valid(): - pwd = pwd_form.cleaned_data['pwd1'] + if request.user.has_perm("kfet.is_team") and pwd_form.is_valid(): + pwd = pwd_form.cleaned_data["pwd1"] account.change_pwd(pwd) account.save() - messages.success( - request, 'Votre mot de passe a été mis à jour') + messages.success(request, "Votre mot de passe a été mis à jour") if missing_perm: - messages.error(request, 'Permission refusée') + messages.error(request, "Permission refusée") if success: - return redirect('kfet.account.read', account.trigramme) + return redirect("kfet.account.read", account.trigramme) else: messages.error( - request, 'Informations non mises à jour. Corrigez les erreurs') + request, "Informations non mises à jour. Corrigez les erreurs" + ) - return render(request, "kfet/account_update.html", { - 'account': account, - 'account_form': account_form, - 'cof_form': cof_form, - 'user_form': user_form, - 'group_form': group_form, - 'negative_form': negative_form, - 'pwd_form': pwd_form, - }) + return render( + request, + "kfet/account_update.html", + { + "account": account, + "account_form": account_form, + "cof_form": cof_form, + "user_form": user_form, + "group_form": group_form, + "negative_form": negative_form, + "pwd_form": pwd_form, + }, + ) class AccountNegativeList(ListView): - queryset = ( - AccountNegative.objects - .select_related('account', 'account__cofprofile__user') - .exclude(account__trigramme='#13') - ) - template_name = 'kfet/account_negative.html' - context_object_name = 'negatives' + queryset = AccountNegative.objects.select_related( + "account", "account__cofprofile__user" + ).exclude(account__trigramme="#13") + template_name = "kfet/account_negative.html" + context_object_name = "negatives" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) real_balances = (neg.account.real_balance for neg in self.object_list) - context['negatives_sum'] = sum(real_balances) + context["negatives_sum"] = sum(real_balances) return context + # ----- # Checkout views # ----- # Checkout - General + class CheckoutList(ListView): - model = Checkout - template_name = 'kfet/checkout.html' - context_object_name = 'checkouts' + model = Checkout + template_name = "kfet/checkout.html" + context_object_name = "checkouts" + # Checkout - Create + class CheckoutCreate(SuccessMessageMixin, CreateView): - model = Checkout - template_name = 'kfet/checkout_create.html' - form_class = CheckoutForm - success_message = 'Nouvelle caisse : %(name)s' + model = Checkout + template_name = "kfet/checkout_create.html" + form_class = CheckoutForm + success_message = "Nouvelle caisse : %(name)s" # Surcharge de la validation def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.add_checkout'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.add_checkout"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Creating @@ -457,127 +521,161 @@ class CheckoutCreate(SuccessMessageMixin, CreateView): return super().form_valid(form) + # Checkout - Read + class CheckoutRead(DetailView): model = Checkout - template_name = 'kfet/checkout_read.html' - context_object_name = 'checkout' + template_name = "kfet/checkout_read.html" + context_object_name = "checkout" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['statements'] = context['checkout'].statements.order_by('-at') + context["statements"] = context["checkout"].statements.order_by("-at") return context + # Checkout - Update + class CheckoutUpdate(SuccessMessageMixin, UpdateView): - model = Checkout - template_name = 'kfet/checkout_update.html' - form_class = CheckoutRestrictForm - success_message = 'Informations mises à jour pour la caisse : %(name)s' + model = Checkout + template_name = "kfet/checkout_update.html" + form_class = CheckoutRestrictForm + success_message = "Informations mises à jour pour la caisse : %(name)s" # Surcharge de la validation def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.change_checkout'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.change_checkout"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Updating return super().form_valid(form) + # ----- # Checkout Statement views # ----- # Checkout Statement - General + class CheckoutStatementList(ListView): - model = CheckoutStatement - queryset = CheckoutStatement.objects.order_by('-at') - template_name = 'kfet/checkoutstatement.html' - context_object_name= 'checkoutstatements' + model = CheckoutStatement + queryset = CheckoutStatement.objects.order_by("-at") + template_name = "kfet/checkoutstatement.html" + context_object_name = "checkoutstatements" + # Checkout Statement - Create + def getAmountTaken(data): - return Decimal(data.taken_001 * 0.01 + data.taken_002 * 0.02 - + data.taken_005 * 0.05 + data.taken_01 * 0.1 - + data.taken_02 * 0.2 + data.taken_05 * 0.5 - + data.taken_1 * 1 + data.taken_2 * 2 - + data.taken_5 * 5 + data.taken_10 * 10 - + data.taken_20 * 20 + data.taken_50 * 50 - + data.taken_100 * 100 + data.taken_200 * 200 - + data.taken_500 * 500 + float(data.taken_cheque)) + return Decimal( + data.taken_001 * 0.01 + + data.taken_002 * 0.02 + + data.taken_005 * 0.05 + + data.taken_01 * 0.1 + + data.taken_02 * 0.2 + + data.taken_05 * 0.5 + + data.taken_1 * 1 + + data.taken_2 * 2 + + data.taken_5 * 5 + + data.taken_10 * 10 + + data.taken_20 * 20 + + data.taken_50 * 50 + + data.taken_100 * 100 + + data.taken_200 * 200 + + data.taken_500 * 500 + + float(data.taken_cheque) + ) + def getAmountBalance(data): - return Decimal(data['balance_001'] * 0.01 + data['balance_002'] * 0.02 - + data['balance_005'] * 0.05 + data['balance_01'] * 0.1 - + data['balance_02'] * 0.2 + data['balance_05'] * 0.5 - + data['balance_1'] * 1 + data['balance_2'] * 2 - + data['balance_5'] * 5 + data['balance_10'] * 10 - + data['balance_20'] * 20 + data['balance_50'] * 50 - + data['balance_100'] * 100 + data['balance_200'] * 200 - + data['balance_500'] * 500) + return Decimal( + data["balance_001"] * 0.01 + + data["balance_002"] * 0.02 + + data["balance_005"] * 0.05 + + data["balance_01"] * 0.1 + + data["balance_02"] * 0.2 + + data["balance_05"] * 0.5 + + data["balance_1"] * 1 + + data["balance_2"] * 2 + + data["balance_5"] * 5 + + data["balance_10"] * 10 + + data["balance_20"] * 20 + + data["balance_50"] * 50 + + data["balance_100"] * 100 + + data["balance_200"] * 200 + + data["balance_500"] * 500 + ) + class CheckoutStatementCreate(SuccessMessageMixin, CreateView): - model = CheckoutStatement - template_name = 'kfet/checkoutstatement_create.html' - form_class = CheckoutStatementCreateForm - success_message = 'Nouveau relevé : %(checkout)s - %(at)s' + model = CheckoutStatement + template_name = "kfet/checkoutstatement_create.html" + form_class = CheckoutStatementCreateForm + success_message = "Nouveau relevé : %(checkout)s - %(at)s" def get_success_url(self): - return reverse_lazy('kfet.checkout.read', kwargs={'pk':self.kwargs['pk_checkout']}) + return reverse_lazy( + "kfet.checkout.read", kwargs={"pk": self.kwargs["pk_checkout"]} + ) def get_success_message(self, cleaned_data): return self.success_message % dict( - cleaned_data, - checkout = self.object.checkout.name, - at = self.object.at) + cleaned_data, checkout=self.object.checkout.name, at=self.object.at + ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - checkout = Checkout.objects.get(pk=self.kwargs['pk_checkout']) - context['checkout'] = checkout + checkout = Checkout.objects.get(pk=self.kwargs["pk_checkout"]) + context["checkout"] = checkout return context def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.add_checkoutstatement'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.add_checkoutstatement"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Creating form.instance.amount_taken = getAmountTaken(form.instance) if not form.instance.not_count: form.instance.balance_new = getAmountBalance(form.cleaned_data) - form.instance.checkout_id = self.kwargs['pk_checkout'] + form.instance.checkout_id = self.kwargs["pk_checkout"] form.instance.by = self.request.user.profile.account_kfet return super().form_valid(form) + class CheckoutStatementUpdate(SuccessMessageMixin, UpdateView): model = CheckoutStatement - template_name = 'kfet/checkoutstatement_update.html' + template_name = "kfet/checkoutstatement_update.html" form_class = CheckoutStatementUpdateForm - success_message = 'Relevé modifié' + success_message = "Relevé modifié" def get_success_url(self): - return reverse_lazy('kfet.checkout.read', kwargs={'pk':self.kwargs['pk_checkout']}) + return reverse_lazy( + "kfet.checkout.read", kwargs={"pk": self.kwargs["pk_checkout"]} + ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - checkout = Checkout.objects.get(pk=self.kwargs['pk_checkout']) - context['checkout'] = checkout + checkout = Checkout.objects.get(pk=self.kwargs["pk_checkout"]) + context["checkout"] = checkout return context def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.change_checkoutstatement'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.change_checkoutstatement"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Updating form.instance.amount_taken = getAmountTaken(form.instance) return super().form_valid(form) + # ----- # Category views # ----- @@ -585,31 +683,30 @@ class CheckoutStatementUpdate(SuccessMessageMixin, UpdateView): # Category - General class CategoryList(ListView): - queryset = (ArticleCategory.objects - .prefetch_related('articles') - .order_by('name')) - template_name = 'kfet/category.html' - context_object_name = 'categories' + queryset = ArticleCategory.objects.prefetch_related("articles").order_by("name") + template_name = "kfet/category.html" + context_object_name = "categories" # Category - Update class CategoryUpdate(SuccessMessageMixin, UpdateView): model = ArticleCategory - template_name = 'kfet/category_update.html' + template_name = "kfet/category_update.html" form_class = CategoryForm - success_url = reverse_lazy('kfet.category') + success_url = reverse_lazy("kfet.category") success_message = "Informations mises à jour pour la catégorie : %(name)s" # Surcharge de la validation def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.change_articlecategory'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.change_articlecategory"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Updating return super().form_valid(form) + # ----- # Article views # ----- @@ -618,69 +715,65 @@ class CategoryUpdate(SuccessMessageMixin, UpdateView): # Article - General class ArticleList(ListView): queryset = ( - Article.objects - .select_related('category') + Article.objects.select_related("category") .prefetch_related( Prefetch( - 'inventories', - queryset=Inventory.objects.order_by('-at'), - to_attr='inventory', + "inventories", + queryset=Inventory.objects.order_by("-at"), + to_attr="inventory", ) ) - .order_by('category__name', '-is_sold', 'name') + .order_by("category__name", "-is_sold", "name") ) - template_name = 'kfet/article.html' - context_object_name = 'articles' + template_name = "kfet/article.html" + context_object_name = "articles" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) articles = context[self.context_object_name] - context['nb_articles'] = len(articles) + context["nb_articles"] = len(articles) context[self.context_object_name] = articles.filter(is_sold=True) - context['not_sold_articles'] = articles.filter(is_sold=False) + context["not_sold_articles"] = articles.filter(is_sold=False) return context # Article - Create class ArticleCreate(SuccessMessageMixin, CreateView): model = Article - template_name = 'kfet/article_create.html' + template_name = "kfet/article_create.html" form_class = ArticleForm - success_message = 'Nouvel item : %(category)s - %(name)s' + success_message = "Nouvel item : %(category)s - %(name)s" # Surcharge de la validation def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.add_article'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.add_article"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Save ici pour save le manytomany suppliers article = form.save() # Save des suppliers déjà existant - for supplier in form.cleaned_data['suppliers']: - SupplierArticle.objects.create( - article=article, supplier=supplier) + for supplier in form.cleaned_data["suppliers"]: + SupplierArticle.objects.create(article=article, supplier=supplier) # Nouveau supplier - supplier_new = form.cleaned_data['supplier_new'].strip() + supplier_new = form.cleaned_data["supplier_new"].strip() if supplier_new: - supplier, created = Supplier.objects.get_or_create( - name=supplier_new) + supplier, created = Supplier.objects.get_or_create(name=supplier_new) if created: - SupplierArticle.objects.create( - article=article, supplier=supplier) + SupplierArticle.objects.create(article=article, supplier=supplier) # Inventaire avec stock initial inventory = Inventory() inventory.by = self.request.user.profile.account_kfet inventory.save() InventoryArticle.objects.create( - inventory=inventory, - article=article, - stock_old=article.stock, - stock_new=article.stock, - ) + inventory=inventory, + article=article, + stock_old=article.stock, + stock_new=article.stock, + ) # Creating return super().form_valid(form) @@ -689,60 +782,60 @@ class ArticleCreate(SuccessMessageMixin, CreateView): # Article - Read class ArticleRead(DetailView): model = Article - template_name = 'kfet/article_read.html' - context_object_name = 'article' + template_name = "kfet/article_read.html" + context_object_name = "article" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - inventoryarts = (InventoryArticle.objects - .filter(article=self.object) - .select_related('inventory') - .order_by('-inventory__at')) - context['inventoryarts'] = inventoryarts - supplierarts = (SupplierArticle.objects - .filter(article=self.object) - .select_related('supplier') - .order_by('-at')) - context['supplierarts'] = supplierarts + inventoryarts = ( + InventoryArticle.objects.filter(article=self.object) + .select_related("inventory") + .order_by("-inventory__at") + ) + context["inventoryarts"] = inventoryarts + supplierarts = ( + SupplierArticle.objects.filter(article=self.object) + .select_related("supplier") + .order_by("-at") + ) + context["supplierarts"] = supplierarts return context # Article - Update class ArticleUpdate(SuccessMessageMixin, UpdateView): model = Article - template_name = 'kfet/article_update.html' + template_name = "kfet/article_update.html" form_class = ArticleRestrictForm success_message = "Informations mises à jour pour l'article : %(name)s" # Surcharge de la validation def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.change_article'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.change_article"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Save ici pour save le manytomany suppliers article = form.save() # Save des suppliers déjà existant - for supplier in form.cleaned_data['suppliers']: + for supplier in form.cleaned_data["suppliers"]: if supplier not in article.suppliers.all(): - SupplierArticle.objects.create( - article=article, supplier=supplier) + SupplierArticle.objects.create(article=article, supplier=supplier) # On vire les suppliers désélectionnés for supplier in article.suppliers.all(): - if supplier not in form.cleaned_data['suppliers']: + if supplier not in form.cleaned_data["suppliers"]: SupplierArticle.objects.filter( - article=article, supplier=supplier).delete() + article=article, supplier=supplier + ).delete() # Nouveau supplier - supplier_new = form.cleaned_data['supplier_new'].strip() + supplier_new = form.cleaned_data["supplier_new"].strip() if supplier_new: - supplier, created = Supplier.objects.get_or_create( - name=supplier_new) + supplier, created = Supplier.objects.get_or_create(name=supplier_new) if created: - SupplierArticle.objects.create( - article=article, supplier=supplier) + SupplierArticle.objects.create(article=article, supplier=supplier) # Updating return super().form_valid(form) @@ -752,66 +845,82 @@ class ArticleUpdate(SuccessMessageMixin, UpdateView): # K-Psul # ----- + @teamkfet_required def kpsul(request): data = {} - data['operationgroup_form'] = KPsulOperationGroupForm() - data['trigramme_form'] = KPsulAccountForm() - data['checkout_form'] = KPsulCheckoutForm() - data['operation_formset'] = KPsulOperationFormSet( - queryset=Operation.objects.none(), - ) - return render(request, 'kfet/kpsul.html', data) + data["operationgroup_form"] = KPsulOperationGroupForm() + data["trigramme_form"] = KPsulAccountForm() + data["checkout_form"] = KPsulCheckoutForm() + data["operation_formset"] = KPsulOperationFormSet(queryset=Operation.objects.none()) + return render(request, "kfet/kpsul.html", data) @teamkfet_required def kpsul_get_settings(request): addcost_for = kfet_config.addcost_for data = { - 'subvention_cof': kfet_config.subvention_cof, - 'addcost_for': addcost_for and addcost_for.trigramme or '', - 'addcost_amount': kfet_config.addcost_amount, + "subvention_cof": kfet_config.subvention_cof, + "addcost_for": addcost_for and addcost_for.trigramme or "", + "addcost_amount": kfet_config.addcost_amount, } return JsonResponse(data) @teamkfet_required def account_read_json(request): - trigramme = request.POST.get('trigramme', '') - account = get_object_or_404(Account, trigramme=trigramme) - data = { 'id': account.pk, 'name': account.name, 'email': account.email, - 'is_cof': account.is_cof, 'promo': account.promo, - 'balance': account.balance, 'is_frozen': account.is_frozen, - 'departement': account.departement, 'nickname': account.nickname, - 'trigramme': account.trigramme } + trigramme = request.POST.get("trigramme", "") + account = get_object_or_404(Account, trigramme=trigramme) + data = { + "id": account.pk, + "name": account.name, + "email": account.email, + "is_cof": account.is_cof, + "promo": account.promo, + "balance": account.balance, + "is_frozen": account.is_frozen, + "departement": account.departement, + "nickname": account.nickname, + "trigramme": account.trigramme, + } return JsonResponse(data) @teamkfet_required def kpsul_checkout_data(request): - pk = request.POST.get('pk', 0) + pk = request.POST.get("pk", 0) if not pk: pk = 0 data = ( - Checkout.objects - .annotate( - last_statement_by_first_name=F('statements__by__cofprofile__user__first_name'), - last_statement_by_last_name=F('statements__by__cofprofile__user__last_name'), - last_statement_by_trigramme=F('statements__by__trigramme'), - last_statement_balance=F('statements__balance_new'), - last_statement_at=F('statements__at')) + Checkout.objects.annotate( + last_statement_by_first_name=F( + "statements__by__cofprofile__user__first_name" + ), + last_statement_by_last_name=F( + "statements__by__cofprofile__user__last_name" + ), + last_statement_by_trigramme=F("statements__by__trigramme"), + last_statement_balance=F("statements__balance_new"), + last_statement_at=F("statements__at"), + ) .select_related( - 'statements' - 'statements__by', - 'statements__by__cofprofile__user') + "statements" "statements__by", "statements__by__cofprofile__user" + ) .filter(pk=pk) - .order_by('statements__at') + .order_by("statements__at") .values( - 'id', 'name', 'balance', 'valid_from', 'valid_to', - 'last_statement_balance', 'last_statement_at', - 'last_statement_by_trigramme', 'last_statement_by_last_name', - 'last_statement_by_first_name') + "id", + "name", + "balance", + "valid_from", + "valid_to", + "last_statement_balance", + "last_statement_at", + "last_statement_by_trigramme", + "last_statement_by_last_name", + "last_statement_by_first_name", + ) .last() ) if data is None: @@ -824,43 +933,34 @@ def kpsul_update_addcost(request): addcost_form = AddcostForm(request.POST) if not addcost_form.is_valid(): - data = {'errors': {'addcost': list(addcost_form.errors)}} + data = {"errors": {"addcost": list(addcost_form.errors)}} return JsonResponse(data, status=400) - required_perms = ['kfet.manage_addcosts'] + required_perms = ["kfet.manage_addcosts"] if not request.user.has_perms(required_perms): data = { - 'errors': { - 'missing_perms': get_missing_perms(required_perms, - request.user) - } + "errors": {"missing_perms": get_missing_perms(required_perms, request.user)} } return JsonResponse(data, status=403) - trigramme = addcost_form.cleaned_data['trigramme'] + trigramme = addcost_form.cleaned_data["trigramme"] account = trigramme and Account.objects.get(trigramme=trigramme) or None - amount = addcost_form.cleaned_data['amount'] + amount = addcost_form.cleaned_data["amount"] - kfet_config.set(addcost_for=account, - addcost_amount=amount) + kfet_config.set(addcost_for=account, addcost_amount=amount) - data = { - 'addcost': { - 'for': account and account.trigramme or None, - 'amount': amount, - } - } - consumers.KPsul.group_send('kfet.kpsul', data) + data = {"addcost": {"for": account and account.trigramme or None, "amount": amount}} + consumers.KPsul.group_send("kfet.kpsul", data) return JsonResponse(data) def get_missing_perms(required_perms, user): - missing_perms_codenames = [(perm.split('.'))[1] - for perm in required_perms - if not user.has_perm(perm)] + missing_perms_codenames = [ + (perm.split("."))[1] for perm in required_perms if not user.has_perm(perm) + ] missing_perms = list( - Permission.objects - .filter(codename__in=missing_perms_codenames) - .values_list('name', flat=True) + Permission.objects.filter(codename__in=missing_perms_codenames).values_list( + "name", flat=True + ) ) return missing_perms @@ -868,21 +968,20 @@ def get_missing_perms(required_perms, user): @teamkfet_required def kpsul_perform_operations(request): # Initializing response data - data = {'operationgroup': 0, 'operations': [], - 'warnings': {}, 'errors': {}} + data = {"operationgroup": 0, "operations": [], "warnings": {}, "errors": {}} # Checking operationgroup operationgroup_form = KPsulOperationGroupForm(request.POST) if not operationgroup_form.is_valid(): - data['errors']['operation_group'] = list(operationgroup_form.errors) + data["errors"]["operation_group"] = list(operationgroup_form.errors) # Checking operation_formset operation_formset = KPsulOperationFormSet(request.POST) if not operation_formset.is_valid(): - data['errors']['operations'] = list(operation_formset.errors) + data["errors"]["operations"] = list(operation_formset.errors) # Returning BAD REQUEST if errors - if data['errors']: + if data["errors"]: return JsonResponse(data, status=400) # Pre-saving (no commit) @@ -901,19 +1000,19 @@ def kpsul_perform_operations(request): to_addcost_for_balance = 0 # For balance of addcost_for to_checkout_balance = 0 # For balance of selected checkout to_articles_stocks = defaultdict(lambda: 0) # For stocks articles - is_addcost = all((addcost_for, addcost_amount, - addcost_for != operationgroup.on_acc)) + is_addcost = all( + (addcost_for, addcost_amount, addcost_for != operationgroup.on_acc) + ) need_comment = operationgroup.on_acc.need_comment # Filling data of each operations # + operationgroup + calculating other stuffs for operation in operations: if operation.type == Operation.PURCHASE: - operation.amount = - operation.article.price * operation.article_nb + operation.amount = -operation.article.price * operation.article_nb if is_addcost & operation.article.category.has_addcost: operation.addcost_for = addcost_for - operation.addcost_amount = addcost_amount \ - * operation.article_nb + operation.addcost_amount = addcost_amount * operation.article_nb operation.amount -= operation.addcost_amount to_addcost_for_balance += operation.addcost_amount if operationgroup.on_acc.is_cash: @@ -925,38 +1024,37 @@ def kpsul_perform_operations(request): to_articles_stocks[operation.article] -= operation.article_nb else: if operationgroup.on_acc.is_cash: - data['errors']['account'] = 'LIQ' + data["errors"]["account"] = "LIQ" if operation.type != Operation.EDIT: to_checkout_balance += operation.amount operationgroup.amount += operation.amount if operation.type == Operation.DEPOSIT: - required_perms.add('kfet.perform_deposit') + required_perms.add("kfet.perform_deposit") if operation.type == Operation.EDIT: - required_perms.add('kfet.edit_balance_account') + required_perms.add("kfet.edit_balance_account") need_comment = True if operationgroup.on_acc.is_cof: to_addcost_for_balance = to_addcost_for_balance / cof_grant_divisor - (perms, stop) = (operationgroup.on_acc - .perms_to_perform_operation( - amount=operationgroup.amount) - ) + (perms, stop) = operationgroup.on_acc.perms_to_perform_operation( + amount=operationgroup.amount + ) required_perms |= perms if need_comment: operationgroup.comment = operationgroup.comment.strip() if not operationgroup.comment: - data['errors']['need_comment'] = True + data["errors"]["need_comment"] = True - if data['errors']: + if data["errors"]: return JsonResponse(data, status=400) if stop or not request.user.has_perms(required_perms): missing_perms = get_missing_perms(required_perms, request.user) if missing_perms: - data['errors']['missing_perms'] = missing_perms + data["errors"]["missing_perms"] = missing_perms if stop: - data['errors']['negative'] = [operationgroup.on_acc.trigramme] + data["errors"]["negative"] = [operationgroup.on_acc.trigramme] return JsonResponse(data, status=403) # If 1 perm is required, filling who perform the operations @@ -972,9 +1070,9 @@ def kpsul_perform_operations(request): on_acc = operationgroup.on_acc if not on_acc.is_cash: ( - Account.objects - .filter(pk=on_acc.pk) - .update(balance=F('balance') + operationgroup.amount) + Account.objects.filter(pk=on_acc.pk).update( + balance=F("balance") + operationgroup.amount + ) ) on_acc.refresh_from_db() on_acc.update_negative() @@ -982,106 +1080,117 @@ def kpsul_perform_operations(request): # Updating checkout's balance if to_checkout_balance: Checkout.objects.filter(pk=operationgroup.checkout.pk).update( - balance=F('balance') + to_checkout_balance) + balance=F("balance") + to_checkout_balance + ) # Saving addcost_for with new balance if there is one if is_addcost and to_addcost_for_balance: Account.objects.filter(pk=addcost_for.pk).update( - balance=F('balance') + to_addcost_for_balance) + balance=F("balance") + to_addcost_for_balance + ) # Saving operation group operationgroup.save() - data['operationgroup'] = operationgroup.pk + data["operationgroup"] = operationgroup.pk # Filling operationgroup id for each operations and saving for operation in operations: operation.group = operationgroup operation.save() - data['operations'].append(operation.pk) + data["operations"].append(operation.pk) # Updating articles stock for article in to_articles_stocks: Article.objects.filter(pk=article.pk).update( - stock=F('stock') + to_articles_stocks[article]) + stock=F("stock") + to_articles_stocks[article] + ) # Websocket data websocket_data = {} - websocket_data['opegroups'] = [{ - 'add': True, - 'id': operationgroup.pk, - 'amount': operationgroup.amount, - 'checkout__name': operationgroup.checkout.name, - 'at': operationgroup.at, - 'is_cof': operationgroup.is_cof, - 'comment': operationgroup.comment, - 'valid_by__trigramme': (operationgroup.valid_by and - operationgroup.valid_by.trigramme or None), - 'on_acc__trigramme': operationgroup.on_acc.trigramme, - 'opes': [], - }] + websocket_data["opegroups"] = [ + { + "add": True, + "id": operationgroup.pk, + "amount": operationgroup.amount, + "checkout__name": operationgroup.checkout.name, + "at": operationgroup.at, + "is_cof": operationgroup.is_cof, + "comment": operationgroup.comment, + "valid_by__trigramme": ( + operationgroup.valid_by and operationgroup.valid_by.trigramme or None + ), + "on_acc__trigramme": operationgroup.on_acc.trigramme, + "opes": [], + } + ] for operation in operations: ope_data = { - 'id': operation.pk, 'type': operation.type, - 'amount': operation.amount, - 'addcost_amount': operation.addcost_amount, - 'addcost_for__trigramme': ( - operation.addcost_for and addcost_for.trigramme or None), - 'article__name': ( - operation.article and operation.article.name or None), - 'article_nb': operation.article_nb, - 'group_id': operationgroup.pk, - 'canceled_by__trigramme': None, 'canceled_at': None, + "id": operation.pk, + "type": operation.type, + "amount": operation.amount, + "addcost_amount": operation.addcost_amount, + "addcost_for__trigramme": ( + operation.addcost_for and addcost_for.trigramme or None + ), + "article__name": (operation.article and operation.article.name or None), + "article_nb": operation.article_nb, + "group_id": operationgroup.pk, + "canceled_by__trigramme": None, + "canceled_at": None, } - websocket_data['opegroups'][0]['opes'].append(ope_data) + websocket_data["opegroups"][0]["opes"].append(ope_data) # Need refresh from db cause we used update on queryset operationgroup.checkout.refresh_from_db() - websocket_data['checkouts'] = [{ - 'id': operationgroup.checkout.pk, - 'balance': operationgroup.checkout.balance, - }] - websocket_data['articles'] = [] + websocket_data["checkouts"] = [ + {"id": operationgroup.checkout.pk, "balance": operationgroup.checkout.balance} + ] + websocket_data["articles"] = [] # Need refresh from db cause we used update on querysets articles_pk = [article.pk for article in to_articles_stocks] - articles = Article.objects.values('id', 'stock').filter(pk__in=articles_pk) + articles = Article.objects.values("id", "stock").filter(pk__in=articles_pk) for article in articles: - websocket_data['articles'].append({ - 'id': article['id'], - 'stock': article['stock'] - }) - consumers.KPsul.group_send('kfet.kpsul', websocket_data) + websocket_data["articles"].append( + {"id": article["id"], "stock": article["stock"]} + ) + consumers.KPsul.group_send("kfet.kpsul", websocket_data) return JsonResponse(data) @teamkfet_required def kpsul_cancel_operations(request): # Pour la réponse - data = { 'canceled': [], 'warnings': {}, 'errors': {}} + data = {"canceled": [], "warnings": {}, "errors": {}} # Checking if BAD REQUEST (opes_pk not int or not existing) try: # Set pour virer les doublons - opes_post = set(map(int, filter(None, request.POST.getlist('operations[]', [])))) + opes_post = set( + map(int, filter(None, request.POST.getlist("operations[]", []))) + ) except ValueError: return JsonResponse(data, status=400) - opes_all = ( - Operation.objects - .select_related('group', 'group__on_acc', 'group__on_acc__negative') - .filter(pk__in=opes_post)) - opes_pk = [ ope.pk for ope in opes_all ] - opes_notexisting = [ ope for ope in opes_post if ope not in opes_pk ] + opes_all = Operation.objects.select_related( + "group", "group__on_acc", "group__on_acc__negative" + ).filter(pk__in=opes_post) + opes_pk = [ope.pk for ope in opes_all] + opes_notexisting = [ope for ope in opes_post if ope not in opes_pk] if opes_notexisting: - data['errors']['opes_notexisting'] = opes_notexisting + data["errors"]["opes_notexisting"] = opes_notexisting return JsonResponse(data, status=400) - opes_already_canceled = [] # Déjà annulée - opes = [] # Pas déjà annulée + opes_already_canceled = [] # Déjà annulée + opes = [] # Pas déjà annulée required_perms = set() - stop_all = False + stop_all = False cancel_duration = kfet_config.cancel_duration - to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes - to_groups_amounts = defaultdict(lambda:0) # ------ sur les montants des groupes d'opé - to_checkouts_balances = defaultdict(lambda:0) # ------ sur les balances de caisses - to_articles_stocks = defaultdict(lambda:0) # ------ sur les stocks d'articles + to_accounts_balances = defaultdict( + lambda: 0 + ) # Modifs à faire sur les balances des comptes + to_groups_amounts = defaultdict( + lambda: 0 + ) # ------ sur les montants des groupes d'opé + to_checkouts_balances = defaultdict(lambda: 0) # ------ sur les balances de caisses + to_articles_stocks = defaultdict(lambda: 0) # ------ sur les stocks d'articles for ope in opes_all: if ope.canceled_at: # Opération déjà annulée, va pour un warning en Response @@ -1090,7 +1199,7 @@ def kpsul_cancel_operations(request): opes.append(ope.pk) # Si opé il y a plus de CANCEL_DURATION, permission requise if ope.group.at + cancel_duration < timezone.now(): - required_perms.add('kfet.cancel_old_operations') + required_perms.add("kfet.cancel_old_operations") # Calcul de toutes modifs à faire en cas de validation @@ -1112,14 +1221,15 @@ def kpsul_cancel_operations(request): # par `.save()`, amount_error est recalculé automatiquement, # ce qui n'est pas le cas en faisant un update sur queryset # TODO ? : Maj les balance_old de relevés pour modifier l'erreur - last_statement = (CheckoutStatement.objects - .filter(checkout=ope.group.checkout) - .order_by('at') - .last()) + last_statement = ( + CheckoutStatement.objects.filter(checkout=ope.group.checkout) + .order_by("at") + .last() + ) if not last_statement or last_statement.at < ope.group.at: if ope.is_checkout: if ope.group.on_acc.is_cash: - to_checkouts_balances[ope.group.checkout] -= - ope.amount + to_checkouts_balances[ope.group.checkout] -= -ope.amount else: to_checkouts_balances[ope.group.checkout] -= ope.amount @@ -1131,23 +1241,25 @@ def kpsul_cancel_operations(request): # Note : si InventoryArticle est maj par .save(), stock_error # est recalculé automatiquement if ope.article and ope.article_nb: - last_stock = (InventoryArticle.objects - .select_related('inventory') + last_stock = ( + InventoryArticle.objects.select_related("inventory") .filter(article=ope.article) - .order_by('inventory__at') - .last()) + .order_by("inventory__at") + .last() + ) if not last_stock or last_stock.inventory.at < ope.group.at: to_articles_stocks[ope.article] += ope.article_nb if not opes: - data['warnings']['already_canceled'] = opes_already_canceled + data["warnings"]["already_canceled"] = opes_already_canceled return JsonResponse(data) negative_accounts = [] # Checking permissions or stop for account in to_accounts_balances: (perms, stop) = account.perms_to_perform_operation( - amount = to_accounts_balances[account]) + amount=to_accounts_balances[account] + ) required_perms |= perms stop_all = stop_all or stop if stop: @@ -1156,22 +1268,25 @@ def kpsul_cancel_operations(request): if stop_all or not request.user.has_perms(required_perms): missing_perms = get_missing_perms(required_perms, request.user) if missing_perms: - data['errors']['missing_perms'] = missing_perms + data["errors"]["missing_perms"] = missing_perms if stop_all: - data['errors']['negative'] = negative_accounts + data["errors"]["negative"] = negative_accounts return JsonResponse(data, status=403) canceled_by = required_perms and request.user.profile.account_kfet or None canceled_at = timezone.now() with transaction.atomic(): - (Operation.objects.filter(pk__in=opes) - .update(canceled_by=canceled_by, canceled_at=canceled_at)) + ( + Operation.objects.filter(pk__in=opes).update( + canceled_by=canceled_by, canceled_at=canceled_at + ) + ) for account in to_accounts_balances: ( - Account.objects - .filter(pk=account.pk) - .update(balance=F('balance') + to_accounts_balances[account]) + Account.objects.filter(pk=account.pk).update( + balance=F("balance") + to_accounts_balances[account] + ) ) if not account.is_cash: # Should always be true, but we want to be sure @@ -1179,76 +1294,86 @@ def kpsul_cancel_operations(request): account.update_negative() for checkout in to_checkouts_balances: Checkout.objects.filter(pk=checkout.pk).update( - balance = F('balance') + to_checkouts_balances[checkout]) + balance=F("balance") + to_checkouts_balances[checkout] + ) for group in to_groups_amounts: OperationGroup.objects.filter(pk=group.pk).update( - amount = F('amount') + to_groups_amounts[group]) + amount=F("amount") + to_groups_amounts[group] + ) for article in to_articles_stocks: Article.objects.filter(pk=article.pk).update( - stock = F('stock') + to_articles_stocks[article]) + stock=F("stock") + to_articles_stocks[article] + ) # Websocket data - websocket_data = { 'opegroups': [], 'opes': [], 'checkouts': [], 'articles': [] } + websocket_data = {"opegroups": [], "opes": [], "checkouts": [], "articles": []} # Need refresh from db cause we used update on querysets - opegroups_pk = [ opegroup.pk for opegroup in to_groups_amounts ] - opegroups = (OperationGroup.objects - .values('id','amount','is_cof').filter(pk__in=opegroups_pk)) + opegroups_pk = [opegroup.pk for opegroup in to_groups_amounts] + opegroups = OperationGroup.objects.values("id", "amount", "is_cof").filter( + pk__in=opegroups_pk + ) for opegroup in opegroups: - websocket_data['opegroups'].append({ - 'cancellation': True, - 'id': opegroup['id'], - 'amount': opegroup['amount'], - 'is_cof': opegroup['is_cof'], - }) + websocket_data["opegroups"].append( + { + "cancellation": True, + "id": opegroup["id"], + "amount": opegroup["amount"], + "is_cof": opegroup["is_cof"], + } + ) canceled_by__trigramme = canceled_by and canceled_by.trigramme or None for ope in opes: - websocket_data['opes'].append({ - 'cancellation': True, - 'id': ope, - 'canceled_by__trigramme': canceled_by__trigramme, - 'canceled_at': canceled_at, - }) + websocket_data["opes"].append( + { + "cancellation": True, + "id": ope, + "canceled_by__trigramme": canceled_by__trigramme, + "canceled_at": canceled_at, + } + ) # Need refresh from db cause we used update on querysets - checkouts_pk = [ checkout.pk for checkout in to_checkouts_balances] - checkouts = (Checkout.objects - .values('id', 'balance').filter(pk__in=checkouts_pk)) + checkouts_pk = [checkout.pk for checkout in to_checkouts_balances] + checkouts = Checkout.objects.values("id", "balance").filter(pk__in=checkouts_pk) for checkout in checkouts: - websocket_data['checkouts'].append({ - 'id': checkout['id'], - 'balance': checkout['balance']}) + websocket_data["checkouts"].append( + {"id": checkout["id"], "balance": checkout["balance"]} + ) # Need refresh from db cause we used update on querysets - articles_pk = [ article.pk for articles in to_articles_stocks] - articles = Article.objects.values('id', 'stock').filter(pk__in=articles_pk) + articles_pk = [article.pk for articles in to_articles_stocks] + articles = Article.objects.values("id", "stock").filter(pk__in=articles_pk) for article in articles: - websocket_data['articles'].append({ - 'id': article['id'], - 'stock': article['stock']}) - consumers.KPsul.group_send('kfet.kpsul', websocket_data) + websocket_data["articles"].append( + {"id": article["id"], "stock": article["stock"]} + ) + consumers.KPsul.group_send("kfet.kpsul", websocket_data) - data['canceled'] = opes + data["canceled"] = opes if opes_already_canceled: - data['warnings']['already_canceled'] = opes_already_canceled + data["warnings"]["already_canceled"] = opes_already_canceled return JsonResponse(data) + @login_required def history_json(request): # Récupération des paramètres - from_date = request.POST.get('from', None) - to_date = request.POST.get('to', None) - limit = request.POST.get('limit', None); - checkouts = request.POST.getlist('checkouts[]', None) - accounts = request.POST.getlist('accounts[]', None) + from_date = request.POST.get("from", None) + to_date = request.POST.get("to", None) + limit = request.POST.get("limit", None) + checkouts = request.POST.getlist("checkouts[]", None) + accounts = request.POST.getlist("accounts[]", None) # Construction de la requête (sur les opérations) pour le prefetch queryset_prefetch = Operation.objects.select_related( - 'article', 'canceled_by', 'addcost_for') + "article", "canceled_by", "addcost_for" + ) # Construction de la requête principale opegroups = ( - OperationGroup.objects - .prefetch_related(Prefetch('opes', queryset=queryset_prefetch)) - .select_related('on_acc', 'valid_by') - .order_by('at') + OperationGroup.objects.prefetch_related( + Prefetch("opes", queryset=queryset_prefetch) + ) + .select_related("on_acc", "valid_by") + .order_by("at") ) # Application des filtres if from_date: @@ -1260,65 +1385,68 @@ def history_json(request): if accounts: opegroups = opegroups.filter(on_acc_id__in=accounts) # Un non-membre de l'équipe n'a que accès à son historique - if not request.user.has_perm('kfet.is_team'): + if not request.user.has_perm("kfet.is_team"): opegroups = opegroups.filter(on_acc=request.user.profile.account_kfet) if limit: opegroups = opegroups[:limit] - # Construction de la réponse opegroups_list = [] for opegroup in opegroups: opegroup_dict = { - 'id' : opegroup.id, - 'amount' : opegroup.amount, - 'at' : opegroup.at, - 'checkout_id': opegroup.checkout_id, - 'is_cof' : opegroup.is_cof, - 'comment' : opegroup.comment, - 'opes' : [], - 'on_acc__trigramme': - opegroup.on_acc and opegroup.on_acc.trigramme or None, + "id": opegroup.id, + "amount": opegroup.amount, + "at": opegroup.at, + "checkout_id": opegroup.checkout_id, + "is_cof": opegroup.is_cof, + "comment": opegroup.comment, + "opes": [], + "on_acc__trigramme": opegroup.on_acc and opegroup.on_acc.trigramme or None, } - if request.user.has_perm('kfet.is_team'): - opegroup_dict['valid_by__trigramme'] = ( - opegroup.valid_by and opegroup.valid_by.trigramme or None) + if request.user.has_perm("kfet.is_team"): + opegroup_dict["valid_by__trigramme"] = ( + opegroup.valid_by and opegroup.valid_by.trigramme or None + ) for ope in opegroup.opes.all(): ope_dict = { - 'id' : ope.id, - 'type' : ope.type, - 'amount' : ope.amount, - 'article_nb' : ope.article_nb, - 'addcost_amount': ope.addcost_amount, - 'canceled_at' : ope.canceled_at, - 'article__name': - ope.article and ope.article.name or None, - 'addcost_for__trigramme': - ope.addcost_for and ope.addcost_for.trigramme or None, + "id": ope.id, + "type": ope.type, + "amount": ope.amount, + "article_nb": ope.article_nb, + "addcost_amount": ope.addcost_amount, + "canceled_at": ope.canceled_at, + "article__name": ope.article and ope.article.name or None, + "addcost_for__trigramme": ope.addcost_for + and ope.addcost_for.trigramme + or None, } - if request.user.has_perm('kfet.is_team'): - ope_dict['canceled_by__trigramme'] = ( - ope.canceled_by and ope.canceled_by.trigramme or None) - opegroup_dict['opes'].append(ope_dict) + if request.user.has_perm("kfet.is_team"): + ope_dict["canceled_by__trigramme"] = ( + ope.canceled_by and ope.canceled_by.trigramme or None + ) + opegroup_dict["opes"].append(ope_dict) opegroups_list.append(opegroup_dict) - return JsonResponse({ 'opegroups': opegroups_list }) + return JsonResponse({"opegroups": opegroups_list}) + @teamkfet_required def kpsul_articles_data(request): - articles = ( - Article.objects - .values('id', 'name', 'price', 'stock', 'category_id', - 'category__name', 'category__has_addcost') - .filter(is_sold=True)) - return JsonResponse({ 'articles': list(articles) }) + articles = Article.objects.values( + "id", + "name", + "price", + "stock", + "category_id", + "category__name", + "category__has_addcost", + ).filter(is_sold=True) + return JsonResponse({"articles": list(articles)}) @teamkfet_required def history(request): - data = { - 'filter_form': FilterHistoryForm(), - } - return render(request, 'kfet/history.html', data) + data = {"filter_form": FilterHistoryForm()} + return render(request, "kfet/history.html", data) # ----- @@ -1327,77 +1455,73 @@ def history(request): class SettingsList(TemplateView): - template_name = 'kfet/settings.html' + template_name = "kfet/settings.html" -config_list = permission_required('kfet.see_config')(SettingsList.as_view()) +config_list = permission_required("kfet.see_config")(SettingsList.as_view()) class SettingsUpdate(SuccessMessageMixin, FormView): form_class = KFetConfigForm - template_name = 'kfet/settings_update.html' - success_message = 'Paramètres mis à jour' - success_url = reverse_lazy('kfet.settings') + template_name = "kfet/settings_update.html" + success_message = "Paramètres mis à jour" + success_url = reverse_lazy("kfet.settings") def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.change_config'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.change_config"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) form.save() return super().form_valid(form) -config_update = ( - permission_required('kfet.change_config')(SettingsUpdate.as_view()) -) +config_update = permission_required("kfet.change_config")(SettingsUpdate.as_view()) # ----- # Transfer views # ----- + @teamkfet_required def transfers(request): transfers_pre = Prefetch( - 'transfers', - queryset=( - Transfer.objects - .select_related('from_acc', 'to_acc') - ), + "transfers", queryset=(Transfer.objects.select_related("from_acc", "to_acc")) ) transfergroups = ( - TransferGroup.objects - .select_related('valid_by') + TransferGroup.objects.select_related("valid_by") .prefetch_related(transfers_pre) - .order_by('-at') + .order_by("-at") ) - return render(request, 'kfet/transfers.html', { - 'transfergroups': transfergroups, - }) + return render(request, "kfet/transfers.html", {"transfergroups": transfergroups}) @teamkfet_required def transfers_create(request): transfer_formset = TransferFormSet(queryset=Transfer.objects.none()) - return render(request, 'kfet/transfers_create.html', - { 'transfer_formset': transfer_formset }) + return render( + request, "kfet/transfers_create.html", {"transfer_formset": transfer_formset} + ) + @teamkfet_required def perform_transfers(request): - data = { 'errors': {}, 'transfers': [], 'transfergroup': 0 } + data = {"errors": {}, "transfers": [], "transfergroup": 0} # Checking transfer_formset transfer_formset = TransferFormSet(request.POST) if not transfer_formset.is_valid(): - return JsonResponse({ 'errors': list(transfer_formset.errors)}, status=400) + return JsonResponse({"errors": list(transfer_formset.errors)}, status=400) - transfers = transfer_formset.save(commit = False) + transfers = transfer_formset.save(commit=False) # Initializing vars - required_perms = set(['kfet.add_transfer']) # Required perms to perform all transfers - to_accounts_balances = defaultdict(lambda:0) # For balances of accounts + required_perms = set( + ["kfet.add_transfer"] + ) # Required perms to perform all transfers + to_accounts_balances = defaultdict(lambda: 0) # For balances of accounts for transfer in transfers: to_accounts_balances[transfer.from_acc] -= transfer.amount @@ -1409,7 +1533,8 @@ def perform_transfers(request): # Checking if ok on all accounts for account in to_accounts_balances: (perms, stop) = account.perms_to_perform_operation( - amount = to_accounts_balances[account]) + amount=to_accounts_balances[account] + ) required_perms |= perms stop_all = stop_all or stop if stop: @@ -1418,9 +1543,9 @@ def perform_transfers(request): if stop_all or not request.user.has_perms(required_perms): missing_perms = get_missing_perms(required_perms, request.user) if missing_perms: - data['errors']['missing_perms'] = missing_perms + data["errors"]["missing_perms"] = missing_perms if stop_all: - data['errors']['negative'] = negative_accounts + data["errors"]["negative"] = negative_accounts return JsonResponse(data, status=403) # Creating transfer group @@ -1428,69 +1553,72 @@ def perform_transfers(request): if required_perms: transfergroup.valid_by = request.user.profile.account_kfet - comment = request.POST.get('comment', '') + comment = request.POST.get("comment", "") transfergroup.comment = comment.strip() with transaction.atomic(): # Updating balances accounts for account in to_accounts_balances: Account.objects.filter(pk=account.pk).update( - balance = F('balance') + to_accounts_balances[account]) + balance=F("balance") + to_accounts_balances[account] + ) account.refresh_from_db() if account.balance < 0: - if hasattr(account, 'negative'): + if hasattr(account, "negative"): if not account.negative.start: account.negative.start = timezone.now() account.negative.save() else: - negative = AccountNegative( - account = account, start = timezone.now()) + negative = AccountNegative(account=account, start=timezone.now()) negative.save() - elif (hasattr(account, 'negative') - and not account.negative.balance_offset): + elif hasattr(account, "negative") and not account.negative.balance_offset: account.negative.delete() # Saving transfer group transfergroup.save() - data['transfergroup'] = transfergroup.pk + data["transfergroup"] = transfergroup.pk # Saving all transfers with group for transfer in transfers: transfer.group = transfergroup transfer.save() - data['transfers'].append(transfer.pk) + data["transfers"].append(transfer.pk) return JsonResponse(data) + @teamkfet_required def cancel_transfers(request): # Pour la réponse - data = { 'canceled': [], 'warnings': {}, 'errors': {}} + data = {"canceled": [], "warnings": {}, "errors": {}} # Checking if BAD REQUEST (transfers_pk not int or not existing) try: # Set pour virer les doublons - transfers_post = set(map(int, filter(None, request.POST.getlist('transfers[]', [])))) + transfers_post = set( + map(int, filter(None, request.POST.getlist("transfers[]", []))) + ) except ValueError: return JsonResponse(data, status=400) - transfers_all = ( - Transfer.objects - .select_related('group', 'from_acc', 'from_acc__negative', - 'to_acc', 'to_acc__negative') - .filter(pk__in=transfers_post)) - transfers_pk = [ transfer.pk for transfer in transfers_all ] - transfers_notexisting = [ transfer for transfer in transfers_post - if transfer not in transfers_pk ] + transfers_all = Transfer.objects.select_related( + "group", "from_acc", "from_acc__negative", "to_acc", "to_acc__negative" + ).filter(pk__in=transfers_post) + transfers_pk = [transfer.pk for transfer in transfers_all] + transfers_notexisting = [ + transfer for transfer in transfers_post if transfer not in transfers_pk + ] if transfers_notexisting: - data['errors']['transfers_notexisting'] = transfers_notexisting + data["errors"]["transfers_notexisting"] = transfers_notexisting return JsonResponse(data, status=400) - transfers_already_canceled = [] # Déjà annulée - transfers = [] # Pas déjà annulée + transfers_already_canceled = [] # Déjà annulée + transfers = [] # Pas déjà annulée required_perms = set() - stop_all = False + stop_all = False cancel_duration = kfet_config.cancel_duration - to_accounts_balances = defaultdict(lambda:0) # Modifs à faire sur les balances des comptes + to_accounts_balances = defaultdict( + lambda: 0 + ) # Modifs à faire sur les balances des comptes for transfer in transfers_all: if transfer.canceled_at: # Transfert déjà annulé, va pour un warning en Response @@ -1499,7 +1627,7 @@ def cancel_transfers(request): transfers.append(transfer.pk) # Si transfer il y a plus de CANCEL_DURATION, permission requise if transfer.group.at + cancel_duration < timezone.now(): - required_perms.add('kfet.cancel_old_operations') + required_perms.add("kfet.cancel_old_operations") # Calcul de toutes modifs à faire en cas de validation @@ -1508,14 +1636,15 @@ def cancel_transfers(request): to_accounts_balances[transfer.to_acc] += -transfer.amount if not transfers: - data['warnings']['already_canceled'] = transfers_already_canceled + data["warnings"]["already_canceled"] = transfers_already_canceled return JsonResponse(data) negative_accounts = [] # Checking permissions or stop for account in to_accounts_balances: (perms, stop) = account.perms_to_perform_operation( - amount = to_accounts_balances[account]) + amount=to_accounts_balances[account] + ) required_perms |= perms stop_all = stop_all or stop if stop: @@ -1524,76 +1653,79 @@ def cancel_transfers(request): if stop_all or not request.user.has_perms(required_perms): missing_perms = get_missing_perms(required_perms, request.user) if missing_perms: - data['errors']['missing_perms'] = missing_perms + data["errors"]["missing_perms"] = missing_perms if stop_all: - data['errors']['negative'] = negative_accounts + data["errors"]["negative"] = negative_accounts return JsonResponse(data, status=403) canceled_by = required_perms and request.user.profile.account_kfet or None canceled_at = timezone.now() with transaction.atomic(): - (Transfer.objects.filter(pk__in=transfers) - .update(canceled_by=canceled_by, canceled_at=canceled_at)) + ( + Transfer.objects.filter(pk__in=transfers).update( + canceled_by=canceled_by, canceled_at=canceled_at + ) + ) for account in to_accounts_balances: Account.objects.filter(pk=account.pk).update( - balance = F('balance') + to_accounts_balances[account]) + balance=F("balance") + to_accounts_balances[account] + ) account.refresh_from_db() if account.balance < 0: - if hasattr(account, 'negative'): + if hasattr(account, "negative"): if not account.negative.start: account.negative.start = timezone.now() account.negative.save() else: - negative = AccountNegative( - account = account, start = timezone.now()) + negative = AccountNegative(account=account, start=timezone.now()) negative.save() - elif (hasattr(account, 'negative') - and not account.negative.balance_offset): + elif hasattr(account, "negative") and not account.negative.balance_offset: account.negative.delete() - data['canceled'] = transfers + data["canceled"] = transfers if transfers_already_canceled: - data['warnings']['already_canceled'] = transfers_already_canceled + data["warnings"]["already_canceled"] = transfers_already_canceled return JsonResponse(data) + class InventoryList(ListView): - queryset = (Inventory.objects - .select_related('by', 'order') - .annotate(nb_articles=Count('articles')) - .order_by('-at')) - template_name = 'kfet/inventory.html' - context_object_name = 'inventories' + queryset = ( + Inventory.objects.select_related("by", "order") + .annotate(nb_articles=Count("articles")) + .order_by("-at") + ) + template_name = "kfet/inventory.html" + context_object_name = "inventories" + @teamkfet_required def inventory_create(request): - articles = (Article.objects - .select_related('category') - .order_by('category__name', 'name') - ) + articles = Article.objects.select_related("category").order_by( + "category__name", "name" + ) initial = [] for article in articles: - initial.append({ - 'article' : article.pk, - 'stock_old': article.stock, - 'name' : article.name, - 'category' : article.category_id, - 'category__name': article.category.name, - 'box_capacity': article.box_capacity or 0, - }) - - cls_formset = formset_factory( - form = InventoryArticleForm, - extra = 0, + initial.append( + { + "article": article.pk, + "stock_old": article.stock, + "name": article.name, + "category": article.category_id, + "category__name": article.category.name, + "box_capacity": article.box_capacity or 0, + } ) + cls_formset = formset_factory(form=InventoryArticleForm, extra=0) + if request.POST: formset = cls_formset(request.POST, initial=initial) - if not request.user.has_perm('kfet.add_inventory'): - messages.error(request, 'Permission refusée') + if not request.user.has_perm("kfet.add_inventory"): + messages.error(request, "Permission refusée") elif formset.is_valid(): with transaction.atomic(): @@ -1602,60 +1734,63 @@ def inventory_create(request): inventory.by = request.user.profile.account_kfet saved = False for form in formset: - if form.cleaned_data['stock_new'] is not None: + if form.cleaned_data["stock_new"] is not None: if not saved: inventory.save() saved = True - article = articles.get(pk=form.cleaned_data['article'].pk) + article = articles.get(pk=form.cleaned_data["article"].pk) stock_old = article.stock - stock_new = form.cleaned_data['stock_new'] + stock_new = form.cleaned_data["stock_new"] InventoryArticle.objects.create( - inventory = inventory, - article = article, - stock_old = stock_old, - stock_new = stock_new) + inventory=inventory, + article=article, + stock_old=stock_old, + stock_new=stock_new, + ) article.stock = stock_new article.save() if saved: - messages.success(request, 'Inventaire créé') - return redirect('kfet.inventory') - messages.warning(request, 'Bah alors ? On a rien compté ?') + messages.success(request, "Inventaire créé") + return redirect("kfet.inventory") + messages.warning(request, "Bah alors ? On a rien compté ?") else: - messages.error(request, 'Pas marché') + messages.error(request, "Pas marché") else: - formset = cls_formset(initial = initial) + formset = cls_formset(initial=initial) + + return render(request, "kfet/inventory_create.html", {"formset": formset}) - return render(request, 'kfet/inventory_create.html', { - 'formset': formset, - }) class InventoryRead(DetailView): model = Inventory - template_name = 'kfet/inventory_read.html' - context_object_name = 'inventory' + template_name = "kfet/inventory_read.html" + context_object_name = "inventory" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - inventoryarticles = (InventoryArticle.objects - .select_related('article', 'article__category') - .filter(inventory = self.object) - .order_by('article__category__name', 'article__name')) - context['inventoryarts'] = inventoryarticles + inventoryarticles = ( + InventoryArticle.objects.select_related("article", "article__category") + .filter(inventory=self.object) + .order_by("article__category__name", "article__name") + ) + context["inventoryarts"] = inventoryarticles return context + # ----- # Order views # ----- + class OrderList(ListView): - queryset = Order.objects.select_related('supplier', 'inventory') - template_name = 'kfet/order.html' - context_object_name = 'orders' + queryset = Order.objects.select_related("supplier", "inventory") + template_name = "kfet/order.html" + context_object_name = "orders" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['suppliers'] = Supplier.objects.order_by('name') + context["suppliers"] = Supplier.objects.order_by("name") return context @@ -1664,30 +1799,25 @@ def order_create(request, pk): supplier = get_object_or_404(Supplier, pk=pk) articles = ( - Article.objects - .filter(suppliers=supplier.pk) + Article.objects.filter(suppliers=supplier.pk) .distinct() - .select_related('category') - .order_by('category__name', 'name') + .select_related("category") + .order_by("category__name", "name") ) # Force hit to cache articles = list(articles) sales_q = ( - Operation.objects - .select_related('group') + Operation.objects.select_related("group") .filter(article__in=articles, canceled_at=None) - .values('article') - .annotate(nb=Sum('article_nb')) + .values("article") + .annotate(nb=Sum("article_nb")) ) scale = WeekScale(last=True, n_steps=5, std_chunk=False) - chunks = scale.chunkify_qs(sales_q, field='group__at') + chunks = scale.chunkify_qs(sales_q, field="group__at") - sales = [ - {d['article']: d['nb'] for d in chunk} - for chunk in chunks - ] + sales = [{d["article"]: d["nb"] for d in chunk} for chunk in chunks] initial = [] @@ -1716,103 +1846,103 @@ def order_create(request, pk): c_rec = 5 else: c_rec = round(c_rec_temp) - initial.append({ - 'article': article.pk, - 'name': article.name, - 'category': article.category_id, - 'category__name': article.category.name, - 'stock': article.stock, - 'box_capacity': article.box_capacity, - 'v_all': v_all, - 'v_moy': round(v_moy), - 'v_et': round(v_et), - 'v_prev': round(v_prev), - 'c_rec': article.box_capacity and c_rec or round(c_rec_tot), - }) + initial.append( + { + "article": article.pk, + "name": article.name, + "category": article.category_id, + "category__name": article.category.name, + "stock": article.stock, + "box_capacity": article.box_capacity, + "v_all": v_all, + "v_moy": round(v_moy), + "v_et": round(v_et), + "v_prev": round(v_prev), + "c_rec": article.box_capacity and c_rec or round(c_rec_tot), + } + ) - cls_formset = formset_factory( - form=OrderArticleForm, - extra=0, - ) + cls_formset = formset_factory(form=OrderArticleForm, extra=0) if request.POST: formset = cls_formset(request.POST, initial=initial) - if not request.user.has_perm('kfet.add_order'): - messages.error(request, 'Permission refusée') + if not request.user.has_perm("kfet.add_order"): + messages.error(request, "Permission refusée") elif formset.is_valid(): order = Order() order.supplier = supplier saved = False for form in formset: - if form.cleaned_data['quantity_ordered'] is not None: + if form.cleaned_data["quantity_ordered"] is not None: if not saved: order.save() saved = True - article = form.cleaned_data['article'] - q_ordered = form.cleaned_data['quantity_ordered'] + article = form.cleaned_data["article"] + q_ordered = form.cleaned_data["quantity_ordered"] if article.box_capacity: q_ordered *= article.box_capacity OrderArticle.objects.create( - order=order, - article=article, - quantity_ordered=q_ordered, + order=order, article=article, quantity_ordered=q_ordered ) if saved: - messages.success(request, 'Commande créée') - return redirect('kfet.order.read', order.pk) - messages.warning(request, 'Rien commandé => Pas de commande') + messages.success(request, "Commande créée") + return redirect("kfet.order.read", order.pk) + messages.warning(request, "Rien commandé => Pas de commande") else: - messages.error(request, 'Corrigez les erreurs') + messages.error(request, "Corrigez les erreurs") else: formset = cls_formset(initial=initial) scale.label_fmt = "S-{rev_i}" - return render(request, 'kfet/order_create.html', { - 'supplier': supplier, - 'formset': formset, - 'scale': scale, - }) + return render( + request, + "kfet/order_create.html", + {"supplier": supplier, "formset": formset, "scale": scale}, + ) class OrderRead(DetailView): model = Order - template_name = 'kfet/order_read.html' - context_object_name = 'order' + template_name = "kfet/order_read.html" + context_object_name = "order" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - orderarticles = (OrderArticle.objects - .select_related('article', 'article__category') - .filter(order=self.object) - .order_by('article__category__name', 'article__name') - ) - context['orderarts'] = orderarticles - mail = ("Bonjour,\n\nNous voudrions pour le ##DATE## à la K-Fêt de " - "l'ENS Ulm :") + orderarticles = ( + OrderArticle.objects.select_related("article", "article__category") + .filter(order=self.object) + .order_by("article__category__name", "article__name") + ) + context["orderarts"] = orderarticles + mail = ( + "Bonjour,\n\nNous voudrions pour le ##DATE## à la K-Fêt de " "l'ENS Ulm :" + ) category = 0 for orderarticle in orderarticles: if category != orderarticle.article.category: category = orderarticle.article.category - mail += '\n' + mail += "\n" nb = orderarticle.quantity_ordered - box = '' + box = "" if orderarticle.article.box_capacity: nb /= orderarticle.article.box_capacity if nb >= 2: - box = ' %ss de' % orderarticle.article.box_type + box = " %ss de" % orderarticle.article.box_type else: - box = ' %s de' % orderarticle.article.box_type + box = " %s de" % orderarticle.article.box_type name = orderarticle.article.name.capitalize() mail += "\n- %s%s %s" % (round(nb), box, name) - mail += ("\n\nMerci d'appeler le numéro suivant lorsque les livreurs " - "sont là : ##TELEPHONE##\nCordialement,\n##PRENOM## ##NOM## " - ", pour la K-Fêt de l'ENS Ulm") + mail += ( + "\n\nMerci d'appeler le numéro suivant lorsque les livreurs " + "sont là : ##TELEPHONE##\nCordialement,\n##PRENOM## ##NOM## " + ", pour la K-Fêt de l'ENS Ulm" + ) - context['mail'] = mail + context["mail"] = mail return context @@ -1820,69 +1950,70 @@ class OrderRead(DetailView): def order_to_inventory(request, pk): order = get_object_or_404(Order, pk=pk) - if hasattr(order, 'inventory'): + if hasattr(order, "inventory"): raise Http404 supplier_prefetch = Prefetch( - 'article__supplierarticle_set', + "article__supplierarticle_set", queryset=( - SupplierArticle.objects - .filter(supplier=order.supplier) - .order_by('-at') + SupplierArticle.objects.filter(supplier=order.supplier).order_by("-at") ), - to_attr='supplier', + to_attr="supplier", ) order_articles = ( - OrderArticle.objects - .filter(order=order.pk) - .select_related('article', 'article__category') - .prefetch_related( - supplier_prefetch, - ) - .order_by('article__category__name', 'article__name') + OrderArticle.objects.filter(order=order.pk) + .select_related("article", "article__category") + .prefetch_related(supplier_prefetch) + .order_by("article__category__name", "article__name") ) initial = [] for order_article in order_articles: article = order_article.article - initial.append({ - 'article': article.pk, - 'name': article.name, - 'category': article.category_id, - 'category__name': article.category.name, - 'quantity_ordered': order_article.quantity_ordered, - 'quantity_received': order_article.quantity_ordered, - 'price_HT': article.supplier[0].price_HT, - 'TVA': article.supplier[0].TVA, - 'rights': article.supplier[0].rights, - }) + initial.append( + { + "article": article.pk, + "name": article.name, + "category": article.category_id, + "category__name": article.category.name, + "quantity_ordered": order_article.quantity_ordered, + "quantity_received": order_article.quantity_ordered, + "price_HT": article.supplier[0].price_HT, + "TVA": article.supplier[0].TVA, + "rights": article.supplier[0].rights, + } + ) cls_formset = formset_factory(OrderArticleToInventoryForm, extra=0) - if request.method == 'POST': + if request.method == "POST": formset = cls_formset(request.POST, initial=initial) - if not request.user.has_perm('kfet.order_to_inventory'): - messages.error(request, 'Permission refusée') + if not request.user.has_perm("kfet.order_to_inventory"): + messages.error(request, "Permission refusée") elif formset.is_valid(): with transaction.atomic(): inventory = Inventory.objects.create( - order=order, by=request.user.profile.account_kfet, + order=order, by=request.user.profile.account_kfet ) new_supplierarticle = [] new_inventoryarticle = [] for form in formset: - q_received = form.cleaned_data['quantity_received'] - article = form.cleaned_data['article'] + q_received = form.cleaned_data["quantity_received"] + article = form.cleaned_data["article"] - price_HT = form.cleaned_data['price_HT'] - TVA = form.cleaned_data['TVA'] - rights = form.cleaned_data['rights'] + price_HT = form.cleaned_data["price_HT"] + TVA = form.cleaned_data["TVA"] + rights = form.cleaned_data["rights"] - if any((form.initial['price_HT'] != price_HT, - form.initial['TVA'] != TVA, - form.initial['rights'] != rights)): + if any( + ( + form.initial["price_HT"] != price_HT, + form.initial["TVA"] != TVA, + form.initial["rights"] != rights, + ) + ): new_supplierarticle.append( SupplierArticle( supplier=order.supplier, @@ -1893,9 +2024,9 @@ def order_to_inventory(request, pk): ) ) ( - OrderArticle.objects - .filter(order=order, article=article) - .update(quantity_received=q_received) + OrderArticle.objects.filter( + order=order, article=article + ).update(quantity_received=q_received) ) new_inventoryarticle.append( InventoryArticle( @@ -1912,29 +2043,29 @@ def order_to_inventory(request, pk): SupplierArticle.objects.bulk_create(new_supplierarticle) InventoryArticle.objects.bulk_create(new_inventoryarticle) messages.success(request, "C'est tout bon !") - return redirect('kfet.order') + return redirect("kfet.order") else: messages.error(request, "Corrigez les erreurs") else: formset = cls_formset(initial=initial) - return render(request, 'kfet/order_to_inventory.html', { - 'formset': formset, - 'order': order, - }) + return render( + request, "kfet/order_to_inventory.html", {"formset": formset, "order": order} + ) + class SupplierUpdate(SuccessMessageMixin, UpdateView): - model = Supplier - template_name = 'kfet/supplier_form.html' - fields = ['name', 'address', 'email', 'phone', 'comment'] - success_url = reverse_lazy('kfet.order') - sucess_message = 'Données fournisseur mis à jour' + model = Supplier + template_name = "kfet/supplier_form.html" + fields = ["name", "address", "email", "phone", "comment"] + success_url = reverse_lazy("kfet.order") + sucess_message = "Données fournisseur mis à jour" # Surcharge de la validation def form_valid(self, form): # Checking permission - if not self.request.user.has_perm('kfet.change_supplier'): - form.add_error(None, 'Permission refusée') + if not self.request.user.has_perm("kfet.change_supplier"): + form.add_error(None, "Permission refusée") return self.form_invalid(form) # Updating return super().form_valid(form) @@ -1952,14 +2083,12 @@ class JSONResponseMixin(object): """ A mixin that can be used to render a JSON response. """ + def render_to_json_response(self, context, **response_kwargs): """ Returns a JSON response, transforming 'context' to make the payload. """ - return JsonResponse( - self.get_data(context), - **response_kwargs - ) + return JsonResponse(self.get_data(context), **response_kwargs) def get_data(self, context): """ @@ -1980,7 +2109,6 @@ class JSONDetailView(JSONResponseMixin, BaseDetailView): class PkUrlMixin(object): - def get_object(self, *args, **kwargs): get_by = self.kwargs.get(self.pk_url_kwarg) return get_object_or_404(self.model, **{self.pk_url_kwarg: get_by}) @@ -1993,7 +2121,8 @@ class SingleResumeStat(JSONDetailView): url to retrieve data, label, ... """ - id_prefix = '' + + id_prefix = "" nb_default = 0 stats = [] @@ -2004,27 +2133,28 @@ class SingleResumeStat(JSONDetailView): object_id = self.object.id context = {} stats = [] - prefix = '{}_{}'.format(self.id_prefix, object_id) + prefix = "{}_{}".format(self.id_prefix, object_id) for i, stat_def in enumerate(self.stats): url_pk = getattr(self.object, self.pk_url_kwarg) - url_params_d = stat_def.get('url_params', {}) + url_params_d = stat_def.get("url_params", {}) if len(url_params_d) > 0: - url_params = '?{}'.format(urlencode(url_params_d)) + url_params = "?{}".format(urlencode(url_params_d)) else: - url_params = '' - stats.append({ - 'label': stat_def['label'], - 'btn': 'btn_{}_{}'.format(prefix, i), - 'url': '{url}{params}'.format( - url=reverse(self.url_stat, args=[url_pk]), - params=url_params, - ), - }) - context['id_prefix'] = prefix - context['content_id'] = "content_%s" % prefix - context['stats'] = stats - context['default_stat'] = self.nb_default - context['object_id'] = object_id + url_params = "" + stats.append( + { + "label": stat_def["label"], + "btn": "btn_{}_{}".format(prefix, i), + "url": "{url}{params}".format( + url=reverse(self.url_stat, args=[url_pk]), params=url_params + ), + } + ) + context["id_prefix"] = prefix + context["content_id"] = "content_%s" % prefix + context["stats"] = stats + context["default_stat"] = self.nb_default + context["object_id"] = object_id return context @@ -2036,31 +2166,18 @@ ID_PREFIX_ACC_BALANCE = "balance_acc" class AccountStatBalanceList(PkUrlMixin, SingleResumeStat): """Manifest for balance stats of an account.""" + model = Account - context_object_name = 'account' - pk_url_kwarg = 'trigramme' - url_stat = 'kfet.account.stat.balance' + context_object_name = "account" + pk_url_kwarg = "trigramme" + url_stat = "kfet.account.stat.balance" id_prefix = ID_PREFIX_ACC_BALANCE stats = [ - { - 'label': 'Tout le temps', - }, - { - 'label': '1 an', - 'url_params': {'last_days': 365}, - }, - { - 'label': '6 mois', - 'url_params': {'last_days': 183}, - }, - { - 'label': '3 mois', - 'url_params': {'last_days': 90}, - }, - { - 'label': '30 jours', - 'url_params': {'last_days': 30}, - }, + {"label": "Tout le temps"}, + {"label": "1 an", "url_params": {"last_days": 365}}, + {"label": "6 mois", "url_params": {"last_days": 183}}, + {"label": "3 mois", "url_params": {"last_days": 90}}, + {"label": "30 jours", "url_params": {"last_days": 30}}, ] nb_default = 0 @@ -2081,9 +2198,10 @@ class AccountStatBalance(PkUrlMixin, JSONDetailView): Operations and Transfers are taken into account. """ + model = Account - pk_url_kwarg = 'trigramme' - context_object_name = 'account' + pk_url_kwarg = "trigramme" + context_object_name = "account" def get_changes_list(self, last_days=None, begin_date=None, end_date=None): account = self.object @@ -2096,11 +2214,7 @@ class AccountStatBalance(PkUrlMixin, JSONDetailView): # prepare querysets # TODO: retirer les opgroup dont tous les op sont annulées opegroups = OperationGroup.objects.filter(on_acc=account) - transfers = ( - Transfer.objects - .filter(canceled_at=None) - .select_related('group') - ) + transfers = Transfer.objects.filter(canceled_at=None).select_related("group") recv_transfers = transfers.filter(to_acc=account) sent_transfers = transfers.filter(from_acc=account) @@ -2127,69 +2241,62 @@ class AccountStatBalance(PkUrlMixin, JSONDetailView): actions = [] - actions.append({ - 'at': (begin_date or account.created_at).isoformat(), - 'amount': 0, - 'balance': 0, - }) - actions.append({ - 'at': (end_date or timezone.now()).isoformat(), - 'amount': 0, - 'balance': 0, - }) + actions.append( + { + "at": (begin_date or account.created_at).isoformat(), + "amount": 0, + "balance": 0, + } + ) + actions.append( + {"at": (end_date or timezone.now()).isoformat(), "amount": 0, "balance": 0} + ) - actions += [ - { - 'at': ope_grp.at.isoformat(), - 'amount': ope_grp.amount, - 'balance': 0, - } for ope_grp in opegroups - ] + [ - { - 'at': tr.group.at.isoformat(), - 'amount': tr.amount, - 'balance': 0, - } for tr in recv_transfers - ] + [ - { - 'at': tr.group.at.isoformat(), - 'amount': -tr.amount, - 'balance': 0, - } for tr in sent_transfers - ] + actions += ( + [ + {"at": ope_grp.at.isoformat(), "amount": ope_grp.amount, "balance": 0} + for ope_grp in opegroups + ] + + [ + {"at": tr.group.at.isoformat(), "amount": tr.amount, "balance": 0} + for tr in recv_transfers + ] + + [ + {"at": tr.group.at.isoformat(), "amount": -tr.amount, "balance": 0} + for tr in sent_transfers + ] + ) # Maintenant on trie la liste des actions par ordre du plus récent # an plus ancien et on met à jour la balance if len(actions) > 1: - actions = sorted(actions, key=lambda k: k['at'], reverse=True) - actions[0]['balance'] = account.balance - for i in range(len(actions)-1): - actions[i+1]['balance'] = \ - actions[i]['balance'] - actions[i+1]['amount'] + actions = sorted(actions, key=lambda k: k["at"], reverse=True) + actions[0]["balance"] = account.balance + for i in range(len(actions) - 1): + actions[i + 1]["balance"] = ( + actions[i]["balance"] - actions[i + 1]["amount"] + ) return actions def get_context_data(self, *args, **kwargs): context = {} - last_days = self.request.GET.get('last_days', None) + last_days = self.request.GET.get("last_days", None) if last_days is not None: last_days = int(last_days) - begin_date = self.request.GET.get('begin_date', None) - end_date = self.request.GET.get('end_date', None) + begin_date = self.request.GET.get("begin_date", None) + end_date = self.request.GET.get("end_date", None) changes = self.get_changes_list( - last_days=last_days, - begin_date=begin_date, end_date=end_date, + last_days=last_days, begin_date=begin_date, end_date=end_date ) - context['charts'] = [{ - "color": "rgb(200, 20, 60)", - "label": "Balance", - "values": changes, - }] - context['is_time_chart'] = True + context["charts"] = [ + {"color": "rgb(200, 20, 60)", "label": "Balance", "values": changes} + ] + context["is_time_chart"] = True if len(changes) > 0: - context['min_date'] = changes[-1]['at'] - context['max_date'] = changes[0]['at'] + context["min_date"] = changes[-1]["at"] + context["max_date"] = changes[0]["at"] # TODO: offset return context @@ -2215,13 +2322,14 @@ ID_PREFIX_ACC_LAST_MONTHS = "last_months_acc" class AccountStatOperationList(PkUrlMixin, SingleResumeStat): """Manifest for operations stats of an account.""" + model = Account - context_object_name = 'account' - pk_url_kwarg = 'trigramme' + context_object_name = "account" + pk_url_kwarg = "trigramme" id_prefix = ID_PREFIX_ACC_LAST nb_default = 2 stats = last_stats_manifest(types=[Operation.PURCHASE]) - url_stat = 'kfet.account.stat.operation' + url_stat = "kfet.account.stat.operation" def get_object(self, *args, **kwargs): obj = super().get_object(*args, **kwargs) @@ -2236,9 +2344,10 @@ class AccountStatOperationList(PkUrlMixin, SingleResumeStat): class AccountStatOperation(ScaleMixin, PkUrlMixin, JSONDetailView): """Datasets of operations of an account.""" + model = Account - pk_url_kwarg = 'trigramme' - context_object_name = 'account' + pk_url_kwarg = "trigramme" + context_object_name = "account" id_prefix = "" def get_operations(self, scale, types=None): @@ -2247,26 +2356,25 @@ class AccountStatOperation(ScaleMixin, PkUrlMixin, JSONDetailView): # puis on choisi pour chaques intervalle les opérations # effectuées dans ces intervalles de temps all_operations = ( - Operation.objects - .filter(group__on_acc=self.object, - canceled_at=None) - .values('article_nb', 'group__at') - .order_by('group__at') + Operation.objects.filter(group__on_acc=self.object, canceled_at=None) + .values("article_nb", "group__at") + .order_by("group__at") ) if types is not None: all_operations = all_operations.filter(type__in=types) chunks = scale.get_by_chunks( - all_operations, field_db='group__at', - field_callback=(lambda d: d['group__at']), + all_operations, + field_db="group__at", + field_callback=(lambda d: d["group__at"]), ) return chunks def get_context_data(self, *args, **kwargs): old_ctx = super().get_context_data(*args, **kwargs) - context = {'labels': old_ctx['labels']} + context = {"labels": old_ctx["labels"]} scale = self.scale - types = self.request.GET.get('types', None) + types = self.request.GET.get("types", None) if types is not None: types = ast.literal_eval(types) @@ -2274,12 +2382,16 @@ class AccountStatOperation(ScaleMixin, PkUrlMixin, JSONDetailView): # On compte les opérations nb_ventes = [] for chunk in operations: - ventes = sum(ope['article_nb'] for ope in chunk) + ventes = sum(ope["article_nb"] for ope in chunk) nb_ventes.append(ventes) - context['charts'] = [{"color": "rgb(200, 20, 60)", - "label": "NB items achetés", - "values": nb_ventes}] + context["charts"] = [ + { + "color": "rgb(200, 20, 60)", + "label": "NB items achetés", + "values": nb_ventes, + } + ] return context def get_object(self, *args, **kwargs): @@ -2304,11 +2416,12 @@ ID_PREFIX_ART_LAST_MONTHS = "last_months_art" class ArticleStatSalesList(SingleResumeStat): """Manifest for sales stats of an article.""" + model = Article - context_object_name = 'article' + context_object_name = "article" id_prefix = ID_PREFIX_ART_LAST nb_default = 2 - url_stat = 'kfet.article.stat.sales' + url_stat = "kfet.article.stat.sales" stats = last_stats_manifest() @method_decorator(teamkfet_required) @@ -2318,34 +2431,30 @@ class ArticleStatSalesList(SingleResumeStat): class ArticleStatSales(ScaleMixin, JSONDetailView): """Datasets of sales of an article.""" + model = Article - context_object_name = 'article' + context_object_name = "article" def get_context_data(self, *args, **kwargs): old_ctx = super().get_context_data(*args, **kwargs) - context = {'labels': old_ctx['labels']} + context = {"labels": old_ctx["labels"]} scale = self.scale all_purchases = ( - Operation.objects - .filter( - type=Operation.PURCHASE, - article=self.object, - canceled_at=None, + Operation.objects.filter( + type=Operation.PURCHASE, article=self.object, canceled_at=None ) - .values('group__at', 'article_nb') - .order_by('group__at') + .values("group__at", "article_nb") + .order_by("group__at") ) - liq_only = all_purchases.filter(group__on_acc__trigramme='LIQ') - liq_exclude = all_purchases.exclude(group__on_acc__trigramme='LIQ') + liq_only = all_purchases.filter(group__on_acc__trigramme="LIQ") + liq_exclude = all_purchases.exclude(group__on_acc__trigramme="LIQ") chunks_liq = scale.get_by_chunks( - liq_only, field_db='group__at', - field_callback=lambda d: d['group__at'], + liq_only, field_db="group__at", field_callback=lambda d: d["group__at"] ) chunks_no_liq = scale.get_by_chunks( - liq_exclude, field_db='group__at', - field_callback=lambda d: d['group__at'], + liq_exclude, field_db="group__at", field_callback=lambda d: d["group__at"] ) # On compte les opérations @@ -2353,21 +2462,25 @@ class ArticleStatSales(ScaleMixin, JSONDetailView): nb_accounts = [] nb_liq = [] for chunk_liq, chunk_no_liq in zip(chunks_liq, chunks_no_liq): - sum_accounts = sum(ope['article_nb'] for ope in chunk_no_liq) - sum_liq = sum(ope['article_nb'] for ope in chunk_liq) + sum_accounts = sum(ope["article_nb"] for ope in chunk_no_liq) + sum_liq = sum(ope["article_nb"] for ope in chunk_liq) nb_ventes.append(sum_accounts + sum_liq) nb_accounts.append(sum_accounts) nb_liq.append(sum_liq) - context['charts'] = [{"color": "rgb(200, 20, 60)", - "label": "Toutes consommations", - "values": nb_ventes}, - {"color": "rgb(54, 162, 235)", - "label": "LIQ", - "values": nb_liq}, - {"color": "rgb(255, 205, 86)", - "label": "Comptes K-Fêt", - "values": nb_accounts}] + context["charts"] = [ + { + "color": "rgb(200, 20, 60)", + "label": "Toutes consommations", + "values": nb_ventes, + }, + {"color": "rgb(54, 162, 235)", "label": "LIQ", "values": nb_liq}, + { + "color": "rgb(255, 205, 86)", + "label": "Comptes K-Fêt", + "values": nb_accounts, + }, + ] return context @method_decorator(teamkfet_required) diff --git a/shared/tests/testcases.py b/shared/tests/testcases.py index 91bd9d38..19122322 100644 --- a/shared/tests/testcases.py +++ b/shared/tests/testcases.py @@ -2,6 +2,7 @@ import csv from unittest import mock from urllib.parse import parse_qs, urlparse +import icalendar from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse from django.http import QueryDict @@ -9,13 +10,10 @@ from django.test import Client from django.utils import timezone from django.utils.functional import cached_property -import icalendar - User = get_user_model() class TestCaseMixin: - def assertForbidden(self, response): """ Test that the response (retrieved with a Client) is a denial of access. @@ -40,31 +38,30 @@ class TestCaseMixin: full_path = request.get_full_path() querystring = QueryDict(mutable=True) - querystring['next'] = full_path + querystring["next"] = full_path - login_url = '{}?{}'.format( - reverse('cof-login'), querystring.urlencode(safe='/')) + login_url = "{}?{}".format( + reverse("cof-login"), querystring.urlencode(safe="/") + ) # We don't focus on what the login view does. # So don't fetch the redirect. - self.assertRedirects( - response, login_url, - fetch_redirect_response=False, - ) + self.assertRedirects(response, login_url, fetch_redirect_response=False) except AssertionError: raise AssertionError( "%(http_method)s request at %(path)s should be forbidden for " "%(username)s user.\n" "Response isn't 403, nor a redirect to login view. Instead, " - "response code is %(code)d." % { - 'http_method': request.method, - 'path': request.get_full_path(), - 'username': ( + "response code is %(code)d." + % { + "http_method": request.method, + "path": request.get_full_path(), + "username": ( "'{}'".format(request.user) if request.user.is_authenticated() - else 'anonymous' + else "anonymous" ), - 'code': response.status_code, + "code": response.status_code, } ) @@ -85,10 +82,9 @@ class TestCaseMixin: if type(expected) == dict: parsed = urlparse(actual) for part, expected_part in expected.items(): - if part == 'query': + if part == "query": self.assertDictEqual( - parse_qs(parsed.query), - expected.get('query', {}), + parse_qs(parsed.query), expected.get("query", {}) ) else: self.assertEqual(getattr(parsed, part), expected_part) @@ -105,22 +101,17 @@ class TestCaseMixin: for k, v in kwargs.items(): setattr(self, k, Elt(v)) - results_as_ldap = [ - Entry(uid=uid, cn=name) for uid, name in results - ] + results_as_ldap = [Entry(uid=uid, cn=name) for uid, name in results] mock_connection = mock.MagicMock() mock_connection.entries = results_as_ldap # Connection is used as a context manager. mock_context_manager = mock.MagicMock() - mock_context_manager.return_value.__enter__.return_value = ( - mock_connection - ) + mock_context_manager.return_value.__enter__.return_value = mock_connection patcher = mock.patch( - 'gestioncof.autocomplete.Connection', - new=mock_context_manager, + "gestioncof.autocomplete.Connection", new=mock_context_manager ) patcher.start() self.addCleanup(patcher.stop) @@ -128,8 +119,8 @@ class TestCaseMixin: return mock_connection def load_from_csv_response(self, r): - decoded = r.content.decode('utf-8') - return list(csv.reader(decoded.split('\n')[:-1])) + decoded = r.content.decode("utf-8") + return list(csv.reader(decoded.split("\n")[:-1])) def _test_event_equal(self, event, exp): for k, v_desc in exp.items(): @@ -155,7 +146,7 @@ class TestCaseMixin: cal = icalendar.Calendar.from_ical(ical_content) - for ev in cal.walk('vevent'): + for ev in cal.walk("vevent"): found, i_found = self._find_event(ev, remaining) if found: remaining.pop(i_found) @@ -235,10 +226,11 @@ class ViewTestCaseMixin(TestCaseMixin): can be given by defining an attribute '_data'. """ + url_name = None url_expected = None - http_methods = ['GET'] + http_methods = ["GET"] auth_user = None auth_forbidden = [] @@ -250,7 +242,7 @@ class ViewTestCaseMixin(TestCaseMixin): # Signals handlers on login/logout send messages. # Due to the way the Django' test Client performs login, this raise an # error. As workaround, we mock the Django' messages module. - patcher_messages = mock.patch('gestioncof.signals.messages') + patcher_messages = mock.patch("gestioncof.signals.messages") patcher_messages.start() self.addCleanup(patcher_messages.stop) @@ -268,10 +260,7 @@ class ViewTestCaseMixin(TestCaseMixin): if self.auth_user: # The wrapper is a sanity check. self.assertTrue( - self.client.login( - username=self.auth_user, - password=self.auth_user, - ) + self.client.login(username=self.auth_user, password=self.auth_user) ) def tearDown(self): @@ -289,8 +278,8 @@ class ViewTestCaseMixin(TestCaseMixin): """ return { - 'user': User.objects.create_user('user', '', 'user'), - 'root': User.objects.create_superuser('root', '', 'root'), + "user": User.objects.create_user("user", "", "user"), + "root": User.objects.create_superuser("root", "", "root"), } @cached_property @@ -323,22 +312,25 @@ class ViewTestCaseMixin(TestCaseMixin): @property def urls_conf(self): - return [{ - 'name': self.url_name, - 'args': getattr(self, 'url_args', []), - 'kwargs': getattr(self, 'url_kwargs', {}), - 'expected': self.url_expected, - }] + return [ + { + "name": self.url_name, + "args": getattr(self, "url_args", []), + "kwargs": getattr(self, "url_kwargs", {}), + "expected": self.url_expected, + } + ] @property def t_urls(self): return [ reverse( - url_conf['name'], - args=url_conf.get('args', []), - kwargs=url_conf.get('kwargs', {}), + url_conf["name"], + args=url_conf.get("args", []), + kwargs=url_conf.get("kwargs", {}), ) - for url_conf in self.urls_conf] + for url_conf in self.urls_conf + ] @property def url(self): @@ -346,7 +338,7 @@ class ViewTestCaseMixin(TestCaseMixin): def test_urls(self): for url, conf in zip(self.t_urls, self.urls_conf): - self.assertEqual(url, conf['expected']) + self.assertEqual(url, conf["expected"]) def test_forbidden(self): for method in self.http_methods: @@ -361,7 +353,7 @@ class ViewTestCaseMixin(TestCaseMixin): client.login(username=user, password=user) send_request = getattr(client, method) - data = getattr(self, '{}_data'.format(method), {}) + data = getattr(self, "{}_data".format(method), {}) r = send_request(url, data) self.assertForbidden(r) diff --git a/utils/views/autocomplete.py b/utils/views/autocomplete.py index ca50c63b..c5d51343 100644 --- a/utils/views/autocomplete.py +++ b/utils/views/autocomplete.py @@ -1,6 +1,5 @@ -from django.db.models import Q - from dal import autocomplete +from django.db.models import Q class Select2QuerySetView(autocomplete.Select2QuerySetView): @@ -18,7 +17,7 @@ class Select2QuerySetView(autocomplete.Select2QuerySetView): for word in words: for field in self.search_fields: - filter_q |= Q(**{'{}__icontains'.format(field): word}) + filter_q |= Q(**{"{}__icontains".format(field): word}) return filter_q