diff --git a/bda/admin.py b/bda/admin.py index fc10c326..0cc66d43 100644 --- a/bda/admin.py +++ b/bda/admin.py @@ -13,32 +13,76 @@ from bda.models import Spectacle, Salle, Participant, ChoixSpectacle,\ Attribution, Tirage, Quote, CategorieSpectacle, SpectacleRevente +class ReadOnlyMixin(object): + readonly_fields_update = () + + def get_readonly_fields(self, request, obj=None): + readonly_fields = super().get_readonly_fields(request, obj) + if obj is None: + return readonly_fields + else: + return readonly_fields + self.readonly_fields_update + + class ChoixSpectacleInline(admin.TabularInline): model = ChoixSpectacle sortable_field_name = "priority" +class AttributionTabularAdminForm(forms.ModelForm): + listing = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + spectacles = Spectacle.objects.select_related('location') + if self.listing is not None: + spectacles = spectacles.filter(listing=self.listing) + self.fields['spectacle'].queryset = spectacles + + +class WithoutListingAttributionTabularAdminForm(AttributionTabularAdminForm): + listing = False + + +class WithListingAttributionTabularAdminForm(AttributionTabularAdminForm): + listing = True + + class AttributionInline(admin.TabularInline): model = Attribution extra = 0 + listing = None def get_queryset(self, request): - qs = super(AttributionInline, self).get_queryset(request) - return qs.filter(spectacle__listing=False) + qs = super().get_queryset(request) + if self.listing is not None: + qs.filter(spectacle__listing=self.listing) + return qs -class AttributionInlineListing(admin.TabularInline): - model = Attribution +class WithListingAttributionInline(AttributionInline): + form = WithListingAttributionTabularAdminForm + listing = True + + +class WithoutListingAttributionInline(AttributionInline): exclude = ('given', ) - extra = 0 - - def get_queryset(self, request): - qs = super(AttributionInlineListing, self).get_queryset(request) - return qs.filter(spectacle__listing=True) + form = WithoutListingAttributionTabularAdminForm + listing = False -class ParticipantAdmin(admin.ModelAdmin): - inlines = [AttributionInline, AttributionInlineListing] +class ParticipantAdminForm(forms.ModelForm): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['choicesrevente'].queryset = ( + Spectacle.objects + .select_related('location') + ) + + +class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin): + inlines = [WithListingAttributionInline, WithoutListingAttributionInline] def get_queryset(self, request): return Participant.objects.annotate(nb_places=Count('attributions'), @@ -65,6 +109,8 @@ class ParticipantAdmin(admin.ModelAdmin): actions_on_bottom = True list_per_page = 400 readonly_fields = ("total",) + readonly_fields_update = ('user', 'tirage') + form = ParticipantAdminForm def send_attribs(self, request, queryset): datatuple = [] @@ -94,6 +140,20 @@ class ParticipantAdmin(admin.ModelAdmin): 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 'participant' in self.fields: + self.fields['participant'].queryset = ( + Participant.objects + .select_related('user', 'tirage') + ) + def clean(self): cleaned_data = super(AttributionAdminForm, self).clean() participant = cleaned_data.get("participant") @@ -106,7 +166,7 @@ class AttributionAdminForm(forms.ModelForm): return cleaned_data -class AttributionAdmin(admin.ModelAdmin): +class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin): def paid(self, obj): return obj.participant.paid paid.short_description = 'A payé' @@ -116,6 +176,7 @@ class AttributionAdmin(admin.ModelAdmin): 'participant__user__first_name', 'participant__user__last_name') form = AttributionAdminForm + readonly_fields_update = ('spectacle', 'participant') class ChoixSpectacleAdmin(admin.ModelAdmin): @@ -160,6 +221,24 @@ class SalleAdmin(admin.ModelAdmin): search_fields = ('name', 'address') +class SpectacleReventeAdminForm(forms.ModelForm): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['answered_mail'].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') + ) + + class SpectacleReventeAdmin(admin.ModelAdmin): """ Administration des reventes de spectacles @@ -182,6 +261,7 @@ class SpectacleReventeAdmin(admin.ModelAdmin): actions = ['transfer', 'reinit'] actions_on_bottom = True + form = SpectacleReventeAdminForm def transfer(self, request, queryset): """ diff --git a/bda/algorithm.py b/bda/algorithm.py index bf9b690f..7f18ce18 100644 --- a/bda/algorithm.py +++ b/bda/algorithm.py @@ -22,8 +22,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 * choices.aggregate(Max('priority'))['priority__max'] + self.max_group = 2*max(choice.priority for choice in choices) self.shows = [] showdict = {} for show in shows: diff --git a/bda/forms.py b/bda/forms.py index 2029645b..c0417d1e 100644 --- a/bda/forms.py +++ b/bda/forms.py @@ -1,35 +1,40 @@ # -*- coding: utf-8 -*- -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - from django import forms from django.forms.models import BaseInlineFormSet from django.utils import timezone + from bda.models import Attribution, Spectacle -class BaseBdaFormSet(BaseInlineFormSet): - def clean(self): - """Checks that no two articles have the same title.""" - super(BaseBdaFormSet, self).clean() - if any(self.errors): - # Don't bother validating the formset unless each form is valid on - # its own - return - spectacles = [] - for i in range(0, self.total_form_count()): - form = self.forms[i] - if not form.cleaned_data: - continue - spectacle = form.cleaned_data['spectacle'] - delete = form.cleaned_data['DELETE'] - if not delete and spectacle in spectacles: - raise forms.ValidationError( - "Vous ne pouvez pas vous inscrire deux fois pour le " - "même spectacle.") - spectacles.append(spectacle) +class InscriptionInlineFormSet(BaseInlineFormSet): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # self.instance is a Participant object + tirage = self.instance.tirage + + # 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') + choices = [(sp.pk, str(sp)) for sp in spectacles] + self.force_choices('spectacle', choices) + + def force_choices(self, name, choices): + """Set choices of a field. + + As ModelChoiceIterator (default use to get choices of a + ModelChoiceField), it appends an empty selection if requested. + + """ + for form in self.forms: + field = form.fields[name] + if field.empty_label is not None: + field.choices = [('', field.empty_label)] + choices + else: + field.choices = choices class TokenForm(forms.Form): @@ -38,7 +43,7 @@ class TokenForm(forms.Form): class AttributionModelMultipleChoiceField(forms.ModelMultipleChoiceField): def label_from_instance(self, obj): - return "%s" % obj.spectacle + return "%s" % str(obj.spectacle) class ResellForm(forms.Form): @@ -50,9 +55,13 @@ class ResellForm(forms.Form): def __init__(self, participant, *args, **kwargs): super(ResellForm, self).__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') + ) class AnnulForm(forms.Form): @@ -64,11 +73,15 @@ class AnnulForm(forms.Form): def __init__(self, participant, *args, **kwargs): super(AnnulForm, self).__init__(*args, **kwargs) - self.fields['attributions'].queryset = participant.attribution_set\ + self.fields['attributions'].queryset = ( + participant.attribution_set .filter(spectacle__date__gte=timezone.now(), revente__isnull=False, revente__notif_sent=False, revente__soldTo__isnull=True) + .select_related('spectacle', 'spectacle__location', + 'participant__user') + ) class InscriptionReventeForm(forms.Form): @@ -79,8 +92,11 @@ class InscriptionReventeForm(forms.Form): def __init__(self, tirage, *args, **kwargs): super(InscriptionReventeForm, self).__init__(*args, **kwargs) - self.fields['spectacles'].queryset = tirage.spectacle_set.filter( - date__gte=timezone.now()) + self.fields['spectacles'].queryset = ( + tirage.spectacle_set + .select_related('location') + .filter(date__gte=timezone.now()) + ) class SoldForm(forms.Form): @@ -93,7 +109,9 @@ class SoldForm(forms.Form): super(SoldForm, self).__init__(*args, **kwargs) self.fields['attributions'].queryset = ( participant.attribution_set - .filter(revente__isnull=False, - revente__soldTo__isnull=False) - .exclude(revente__soldTo=participant) + .filter(revente__isnull=False, + revente__soldTo__isnull=False) + .exclude(revente__soldTo=participant) + .select_related('spectacle', 'spectacle__location', + 'participant__user') ) diff --git a/bda/templates/bda/reventes.html b/bda/templates/bda/reventes.html index e61e7c8d..0912babb 100644 --- a/bda/templates/bda/reventes.html +++ b/bda/templates/bda/reventes.html @@ -4,6 +4,8 @@ {% block realcontent %}