Loading api/serializers.py +1 −1 Changes for api/serializers.py: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ class ElectionListSerializer(serializers.ModelSerializer): class OptionSerializer(serializers.ModelSerializer): class Meta: model = Option fields = ['name'] fields = ['id', 'name'] class ElectionSerializer(serializers.ModelSerializer): options = OptionSerializer(many=True, read_only=True) Loading api/urls.py +3 −2 Changes for api/urls.py: 3 added lines, 2 removed lines. Original line number Diff line number Diff line from django.urls import path from api.views import ElectionListView, ElectionView, VoterView from api.views import ElectionListView, ElectionView, VoterView, VoteAPIView urlpatterns = [ path("elections/", ElectionListView.as_view(), name="election-list-api"), path("election/<str:alias>/", ElectionView.as_view(), name="election-api"), path("voter/<uuid:token>/", VoterView.as_view(), name="voter-api") path("voter/<uuid:token>/", VoterView.as_view(), name="voter-api"), path("vote/<str:alias>/<str:option_id>", VoteAPIView.as_view(), name="vote-api") ] No newline at end of file api/views.py +51 −1 Changes for api/views.py: 51 added lines, 1 removed line. Original line number Diff line number Diff line from rest_framework.generics import ListAPIView, RetrieveAPIView from rest_framework.views import APIView from rest_framework.exceptions import PermissionDenied, ValidationError from rest_framework import status from rest_framework.response import Response from django.utils import timezone from django.db.models import Q, OuterRef, Exists from django.shortcuts import get_object_or_404 from django.db import transaction from django.http import HttpResponse from elections.models import Election from elections.models import Election, Option from voters.models import Voter from vote.models import Participation Loading Loading @@ -70,3 +76,47 @@ class VoterView(RetrieveAPIView): voter = super().get_object() return voter class VoteAPIView(APIView): def post(self, request, alias, option_id): token = self.request.query_params.get("token") if token is None: raise PermissionDenied("Brak tokena uwierzytelniającego!") try: voter = Voter.objects.all().filter(token=token).first() except: voter = None if voter is None: raise PermissionDenied("Nieprawidłowy token uwierzytelniający!") if not voter.is_verified: raise PermissionDenied("Konto niezweryfikowane!") election = get_object_or_404(Election, alias=alias) if Participation.objects.filter(voter=voter, election=election).exists(): raise ValidationError("Już oddałeś głos w tym głosowaniu!") if not election.is_active: raise ValidationError("Głosowanie nie jest aktywne") if option_id == "invalid": if not election.allow_invalid: raise ValidationError("Ustawienia głosowania nie zezwalają na oddanie głosu nieważnego.") else: try: option_id = int(option_id) option = Option.objects.filter(pk=option_id, election=election).first() if option is None: raise ValidationError("Nieprawidłowa opcja.") except: raise ValidationError("Nieprawidłowy identyfikator opcji.") election.cast_vote(voter, option_id) return Response( {"detail": "Głos pomyślnie zapisany."}, status = status.HTTP_201_CREATED ) No newline at end of file elections/models.py +14 −1 Changes for elections/models.py: 14 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -58,6 +58,19 @@ class Election(models.Model): return super().save(*args, **kwargs) def cast_vote(self, voter, choice): if choice == 'invalid': self.invalid_votes += 1 self.save() else: option = Option.objects.get(pk=choice) option.votes += 1 option.save() from vote.models import Participation participation = Participation(voter=voter, election=self) participation.save() class Meta: verbose_name = 'głosowanie' verbose_name_plural = 'głosowania' Loading @@ -74,4 +87,4 @@ class Option(models.Model): class Meta: verbose_name = 'opcja' verbose_name_plural = 'opcje' unique_together = [("name", "election")] hybrid/admin.py +2 −2 Changes for hybrid/admin.py: 2 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ class IssueCardAdmin(admin.ModelAdmin): if not queryset.exists(): return None card_issuer = queryset.first().card_issued_by return f'{card_issuer.name} ({card_issuer.email})' return f'{card_issuer.first_name} {card_issuer.last_name} ({card_issuer.username})' def get_urls(self): urls = super().get_urls() Loading Loading @@ -170,7 +170,7 @@ class EnterVotesAdmin(admin.ModelAdmin): def local_votes_view(self, request, election_id): election = get_object_or_404(Election, pk=election_id) options_queryset = election.option_set.all() options_queryset = election.options.all() expected_votes_count = Participation.objects.filter(election=election, is_remote=False).count() if not election.is_hybrid or election.is_active: Loading Loading
api/serializers.py +1 −1 Changes for api/serializers.py: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -10,7 +10,7 @@ class ElectionListSerializer(serializers.ModelSerializer): class OptionSerializer(serializers.ModelSerializer): class Meta: model = Option fields = ['name'] fields = ['id', 'name'] class ElectionSerializer(serializers.ModelSerializer): options = OptionSerializer(many=True, read_only=True) Loading
api/urls.py +3 −2 Changes for api/urls.py: 3 added lines, 2 removed lines. Original line number Diff line number Diff line from django.urls import path from api.views import ElectionListView, ElectionView, VoterView from api.views import ElectionListView, ElectionView, VoterView, VoteAPIView urlpatterns = [ path("elections/", ElectionListView.as_view(), name="election-list-api"), path("election/<str:alias>/", ElectionView.as_view(), name="election-api"), path("voter/<uuid:token>/", VoterView.as_view(), name="voter-api") path("voter/<uuid:token>/", VoterView.as_view(), name="voter-api"), path("vote/<str:alias>/<str:option_id>", VoteAPIView.as_view(), name="vote-api") ] No newline at end of file
api/views.py +51 −1 Changes for api/views.py: 51 added lines, 1 removed line. Original line number Diff line number Diff line from rest_framework.generics import ListAPIView, RetrieveAPIView from rest_framework.views import APIView from rest_framework.exceptions import PermissionDenied, ValidationError from rest_framework import status from rest_framework.response import Response from django.utils import timezone from django.db.models import Q, OuterRef, Exists from django.shortcuts import get_object_or_404 from django.db import transaction from django.http import HttpResponse from elections.models import Election from elections.models import Election, Option from voters.models import Voter from vote.models import Participation Loading Loading @@ -70,3 +76,47 @@ class VoterView(RetrieveAPIView): voter = super().get_object() return voter class VoteAPIView(APIView): def post(self, request, alias, option_id): token = self.request.query_params.get("token") if token is None: raise PermissionDenied("Brak tokena uwierzytelniającego!") try: voter = Voter.objects.all().filter(token=token).first() except: voter = None if voter is None: raise PermissionDenied("Nieprawidłowy token uwierzytelniający!") if not voter.is_verified: raise PermissionDenied("Konto niezweryfikowane!") election = get_object_or_404(Election, alias=alias) if Participation.objects.filter(voter=voter, election=election).exists(): raise ValidationError("Już oddałeś głos w tym głosowaniu!") if not election.is_active: raise ValidationError("Głosowanie nie jest aktywne") if option_id == "invalid": if not election.allow_invalid: raise ValidationError("Ustawienia głosowania nie zezwalają na oddanie głosu nieważnego.") else: try: option_id = int(option_id) option = Option.objects.filter(pk=option_id, election=election).first() if option is None: raise ValidationError("Nieprawidłowa opcja.") except: raise ValidationError("Nieprawidłowy identyfikator opcji.") election.cast_vote(voter, option_id) return Response( {"detail": "Głos pomyślnie zapisany."}, status = status.HTTP_201_CREATED ) No newline at end of file
elections/models.py +14 −1 Changes for elections/models.py: 14 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -58,6 +58,19 @@ class Election(models.Model): return super().save(*args, **kwargs) def cast_vote(self, voter, choice): if choice == 'invalid': self.invalid_votes += 1 self.save() else: option = Option.objects.get(pk=choice) option.votes += 1 option.save() from vote.models import Participation participation = Participation(voter=voter, election=self) participation.save() class Meta: verbose_name = 'głosowanie' verbose_name_plural = 'głosowania' Loading @@ -74,4 +87,4 @@ class Option(models.Model): class Meta: verbose_name = 'opcja' verbose_name_plural = 'opcje' unique_together = [("name", "election")]
hybrid/admin.py +2 −2 Changes for hybrid/admin.py: 2 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ class IssueCardAdmin(admin.ModelAdmin): if not queryset.exists(): return None card_issuer = queryset.first().card_issued_by return f'{card_issuer.name} ({card_issuer.email})' return f'{card_issuer.first_name} {card_issuer.last_name} ({card_issuer.username})' def get_urls(self): urls = super().get_urls() Loading Loading @@ -170,7 +170,7 @@ class EnterVotesAdmin(admin.ModelAdmin): def local_votes_view(self, request, election_id): election = get_object_or_404(Election, pk=election_id) options_queryset = election.option_set.all() options_queryset = election.options.all() expected_votes_count = Participation.objects.filter(election=election, is_remote=False).count() if not election.is_hybrid or election.is_active: Loading