Loading hybrid/admin.py +87 −2 Changes for hybrid/admin.py: 87 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -14,7 +14,8 @@ from django.utils.decorators import method_decorator from elections.models import Election from voters.models import Voter from vote.models import Participation from .models import HybridElectionVoter from .models import HybridElectionVoter, EnterHybridVotesElection from .forms import LocalVotesForm class ElectionContextFilter(admin.SimpleListFilter): title = 'wybory' Loading Loading @@ -86,7 +87,6 @@ class HybridElectionVoterAdmin(admin.ModelAdmin): return None card_issuer = queryset.first().card_issued_by return f'{card_issuer.first_name} {card_issuer.last_name} ({card_issuer.username})' return 'zad' def get_urls(self): urls = super().get_urls() Loading Loading @@ -135,4 +135,89 @@ class HybridElectionVoterAdmin(admin.ModelAdmin): def has_delete_permission(self, request, obj = None): return False class EnterHybridVotesElectionAdmin(admin.ModelAdmin): list_display = ['name', 'start_date', 'end_date', 'local_votes_action'] ordering = ['-created_at', '-updated_at'] exclude = ['invalid_votes', 'local_votes_entered'] @admin.display(description='Głosy stacjonarne') def local_votes_action(self, obj): if not obj.is_hybrid or obj.is_active: return None if obj.local_votes_entered: return 'WPROWADZONE' url = reverse('admin:enter_local_votes', args=[obj.id]) return format_html('<a href="{}">WPROWADŹ</a>', url) def changelist_view(self, request, extra_context = None): extra_context = extra_context or {} extra_context['title'] = 'Wprowadzanie głosów stacjonarnych' return super().changelist_view(request, extra_context) def get_urls(self): urls = super().get_urls() custom_urls = [ path('enter-local-votes/<int:election_id>/', self.admin_site.admin_view(self.local_votes_view), name='enter_local_votes'), ] return custom_urls + urls def local_votes_view(self, request, election_id): election = get_object_or_404(Election, pk=election_id) options_queryset = election.option_set.all() expected_votes_count = Participation.objects.filter(election=election, is_remote=False).count() if not election.is_hybrid or election.is_active: messages.error(request, 'Głosy stacjonarne można wprowadzić tylko dla zakończonych wyborów hybrydowych') return HttpResponseRedirect(reverse('admin:hybrid_enterhybridvoteselection_changelist')) if election.local_votes_entered: messages.error(request, 'Głosy stacjonarne zostały już wprowadzone dla tego głosowania') return HttpResponseRedirect(reverse('admin:hybrid_enterhybridvoteselection_changelist')) if request.POST.get('post') == 'yes': form = LocalVotesForm(request.POST, options_queryset=options_queryset, expected_votes_count=expected_votes_count) if form.is_valid(): for option in options_queryset: field_name = f'option_{option.id}' votes = form.cleaned_data.get(field_name) option.votes += votes option.save() election.local_votes_entered = True election.save() messages.success(request, 'Pomyślnie wprowadzono głosy stacjonarne') return HttpResponseRedirect(reverse('admin:hybrid_enterhybridvoteselection_changelist')) else: form = LocalVotesForm(options_queryset=options_queryset, expected_votes_count=expected_votes_count) context = admin.site.each_context(request) context.update({ 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME, 'title' : 'Wprowadzenie głosów stacjonarnych', 'election' : election, 'form' : form }) return render(request, 'admin/enter_local_votes.html', context) def has_add_permission(self, request): return False def has_change_permission(self, request, obj = None): return False def has_delete_permission(self, request, obj = None): return False admin.site.register(HybridElectionVoter, HybridElectionVoterAdmin) admin.site.register(EnterHybridVotesElection, EnterHybridVotesElectionAdmin) No newline at end of file hybrid/forms.py 0 → 100644 +29 −0 Changes for hybrid/forms.py: 29 added lines, 0 removed lines. Original line number Diff line number Diff line from django import forms from django.core.exceptions import ValidationError class LocalVotesForm(forms.Form): def __init__(self, *args, **kwargs): self.options_queryset = kwargs.pop('options_queryset', []) self.expected_votes_count = kwargs.pop('expected_votes_count', 0) super().__init__(*args, **kwargs) for option in self.options_queryset: field_name = f'option_{option.id}' self.fields[field_name] = forms.IntegerField(label=option.name, initial=0, min_value=0, required=True, widget=forms.NumberInput(attrs={'class': 'form-control'})) def clean(self): cleaned_data = super().clean() entered_votes_count = 0 for option in self.options_queryset: field_name = f'option_{option.id}' value = cleaned_data.get(field_name) if value is not None: entered_votes_count += value if entered_votes_count != self.expected_votes_count: raise ValidationError(f'Otrzymano {entered_votes_count} głosów, a oczekiwano {self.expected_votes_count}') return cleaned_data No newline at end of file hybrid/models.py +7 −0 Changes for hybrid/models.py: 7 added lines, 0 removed lines. Original line number Diff line number Diff line from django.db import models from voters.models import Voter from elections.models import Election class HybridElectionVoter(Voter): class Meta: proxy = True verbose_name = 'Wydaj karty' verbose_name_plural = 'Wydawanie kart' class EnterHybridVotesElection(Election): class Meta: proxy = True verbose_name = 'Wprowadź głosy stacjonarne' verbose_name_plural = 'Wprowadź głosy stacjonarne' No newline at end of file hybrid/templates/admin/enter_local_votes.html 0 → 100644 +23 −0 Changes for hybrid/templates/admin/enter_local_votes.html: 23 added lines, 0 removed lines. Original line number Diff line number Diff line {% extends 'admin/base_site.html' %} {% load admin_urls %} {% block title %} {{ title }} | {{ site_title }}{% endblock %} {% block content %} <div class="delete-confirmation delete-selected-confirmation"> <p>Czy na pewno chcesz wprowadzić głosy stacjonarne w wyborach <b>{{ election.name }}</b>? Tą akcję można wykonać tylko raz i nie da się jej cofnąć.</p> <form method="post"> {% csrf_token %} <fieldset class="module aligned"> {{ form.as_p }} </fieldset> <div class="submit-row"> <input type="hidden" name="post" value="yes"> <input type="submit" value="Zatwierdź głosy stacjonarne"> </div> </form> </div> {% endblock %} No newline at end of file pyproject.toml +1 −0 Changes for pyproject.toml: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ dependencies = [ "django>=6.0.3", "django-environ>=0.13.0", "django-rq>=4.0.1", "django-smartbase-admin", "django-tasks-rq>=0.12.0", "honcho>=2.0.0", "redis>=7.3.0", Loading Loading
hybrid/admin.py +87 −2 Changes for hybrid/admin.py: 87 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -14,7 +14,8 @@ from django.utils.decorators import method_decorator from elections.models import Election from voters.models import Voter from vote.models import Participation from .models import HybridElectionVoter from .models import HybridElectionVoter, EnterHybridVotesElection from .forms import LocalVotesForm class ElectionContextFilter(admin.SimpleListFilter): title = 'wybory' Loading Loading @@ -86,7 +87,6 @@ class HybridElectionVoterAdmin(admin.ModelAdmin): return None card_issuer = queryset.first().card_issued_by return f'{card_issuer.first_name} {card_issuer.last_name} ({card_issuer.username})' return 'zad' def get_urls(self): urls = super().get_urls() Loading Loading @@ -135,4 +135,89 @@ class HybridElectionVoterAdmin(admin.ModelAdmin): def has_delete_permission(self, request, obj = None): return False class EnterHybridVotesElectionAdmin(admin.ModelAdmin): list_display = ['name', 'start_date', 'end_date', 'local_votes_action'] ordering = ['-created_at', '-updated_at'] exclude = ['invalid_votes', 'local_votes_entered'] @admin.display(description='Głosy stacjonarne') def local_votes_action(self, obj): if not obj.is_hybrid or obj.is_active: return None if obj.local_votes_entered: return 'WPROWADZONE' url = reverse('admin:enter_local_votes', args=[obj.id]) return format_html('<a href="{}">WPROWADŹ</a>', url) def changelist_view(self, request, extra_context = None): extra_context = extra_context or {} extra_context['title'] = 'Wprowadzanie głosów stacjonarnych' return super().changelist_view(request, extra_context) def get_urls(self): urls = super().get_urls() custom_urls = [ path('enter-local-votes/<int:election_id>/', self.admin_site.admin_view(self.local_votes_view), name='enter_local_votes'), ] return custom_urls + urls def local_votes_view(self, request, election_id): election = get_object_or_404(Election, pk=election_id) options_queryset = election.option_set.all() expected_votes_count = Participation.objects.filter(election=election, is_remote=False).count() if not election.is_hybrid or election.is_active: messages.error(request, 'Głosy stacjonarne można wprowadzić tylko dla zakończonych wyborów hybrydowych') return HttpResponseRedirect(reverse('admin:hybrid_enterhybridvoteselection_changelist')) if election.local_votes_entered: messages.error(request, 'Głosy stacjonarne zostały już wprowadzone dla tego głosowania') return HttpResponseRedirect(reverse('admin:hybrid_enterhybridvoteselection_changelist')) if request.POST.get('post') == 'yes': form = LocalVotesForm(request.POST, options_queryset=options_queryset, expected_votes_count=expected_votes_count) if form.is_valid(): for option in options_queryset: field_name = f'option_{option.id}' votes = form.cleaned_data.get(field_name) option.votes += votes option.save() election.local_votes_entered = True election.save() messages.success(request, 'Pomyślnie wprowadzono głosy stacjonarne') return HttpResponseRedirect(reverse('admin:hybrid_enterhybridvoteselection_changelist')) else: form = LocalVotesForm(options_queryset=options_queryset, expected_votes_count=expected_votes_count) context = admin.site.each_context(request) context.update({ 'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME, 'title' : 'Wprowadzenie głosów stacjonarnych', 'election' : election, 'form' : form }) return render(request, 'admin/enter_local_votes.html', context) def has_add_permission(self, request): return False def has_change_permission(self, request, obj = None): return False def has_delete_permission(self, request, obj = None): return False admin.site.register(HybridElectionVoter, HybridElectionVoterAdmin) admin.site.register(EnterHybridVotesElection, EnterHybridVotesElectionAdmin) No newline at end of file
hybrid/forms.py 0 → 100644 +29 −0 Changes for hybrid/forms.py: 29 added lines, 0 removed lines. Original line number Diff line number Diff line from django import forms from django.core.exceptions import ValidationError class LocalVotesForm(forms.Form): def __init__(self, *args, **kwargs): self.options_queryset = kwargs.pop('options_queryset', []) self.expected_votes_count = kwargs.pop('expected_votes_count', 0) super().__init__(*args, **kwargs) for option in self.options_queryset: field_name = f'option_{option.id}' self.fields[field_name] = forms.IntegerField(label=option.name, initial=0, min_value=0, required=True, widget=forms.NumberInput(attrs={'class': 'form-control'})) def clean(self): cleaned_data = super().clean() entered_votes_count = 0 for option in self.options_queryset: field_name = f'option_{option.id}' value = cleaned_data.get(field_name) if value is not None: entered_votes_count += value if entered_votes_count != self.expected_votes_count: raise ValidationError(f'Otrzymano {entered_votes_count} głosów, a oczekiwano {self.expected_votes_count}') return cleaned_data No newline at end of file
hybrid/models.py +7 −0 Changes for hybrid/models.py: 7 added lines, 0 removed lines. Original line number Diff line number Diff line from django.db import models from voters.models import Voter from elections.models import Election class HybridElectionVoter(Voter): class Meta: proxy = True verbose_name = 'Wydaj karty' verbose_name_plural = 'Wydawanie kart' class EnterHybridVotesElection(Election): class Meta: proxy = True verbose_name = 'Wprowadź głosy stacjonarne' verbose_name_plural = 'Wprowadź głosy stacjonarne' No newline at end of file
hybrid/templates/admin/enter_local_votes.html 0 → 100644 +23 −0 Changes for hybrid/templates/admin/enter_local_votes.html: 23 added lines, 0 removed lines. Original line number Diff line number Diff line {% extends 'admin/base_site.html' %} {% load admin_urls %} {% block title %} {{ title }} | {{ site_title }}{% endblock %} {% block content %} <div class="delete-confirmation delete-selected-confirmation"> <p>Czy na pewno chcesz wprowadzić głosy stacjonarne w wyborach <b>{{ election.name }}</b>? Tą akcję można wykonać tylko raz i nie da się jej cofnąć.</p> <form method="post"> {% csrf_token %} <fieldset class="module aligned"> {{ form.as_p }} </fieldset> <div class="submit-row"> <input type="hidden" name="post" value="yes"> <input type="submit" value="Zatwierdź głosy stacjonarne"> </div> </form> </div> {% endblock %} No newline at end of file
pyproject.toml +1 −0 Changes for pyproject.toml: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ dependencies = [ "django>=6.0.3", "django-environ>=0.13.0", "django-rq>=4.0.1", "django-smartbase-admin", "django-tasks-rq>=0.12.0", "honcho>=2.0.0", "redis>=7.3.0", Loading