Skip to content

Commit

Permalink
✨ Election turnout overview / graph for election managers (#334)
Browse files Browse the repository at this point in the history
Parent issue: sequentech/meta#206
  • Loading branch information
Findeton committed Apr 12, 2024
1 parent 393ed61 commit acbea0c
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 1 deletion.
39 changes: 38 additions & 1 deletion iam/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
from django.core.validators import MaxValueValidator, MinValueValidator
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
from django.db.models.functions import TruncHour

from django.contrib.postgres import fields
from jsonfield import JSONField

from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save
from django.db.models import Q
from django.db.models import Q, Count, DateTimeField
from django.conf import settings
from django.utils import timezone

Expand Down Expand Up @@ -762,6 +763,42 @@ def get_num_votes_query(self):
)\
.order_by('user_id', '-created')\
.distinct('user_id')

def get_votes_per_hour(self):
'''
Returns the number of votes per hour in this election and in
children elections (if any).
'''
if self.children_election_info:
parents2 = self.children_election_info['natural_order']
else:
parents2 = []

q_base = SuccessfulLogin.objects\
.filter(
Q(auth_event_id=self.pk) |
Q(auth_event__parent_id=self.pk) |
Q(auth_event__parent_id__in=parents2)
)
subquery_distinct = q_base\
.order_by('user_id', '-created')\
.distinct('user_id')

q = q_base\
.annotate(hour=TruncHour('created'))\
.values('hour')\
.annotate(votes=Count('user_id'))\
.order_by('hour')\
.filter(id__in=subquery_distinct)

return [
dict(
hour=str(obj['hour']),
votes=obj['votes']
)
for obj in q
]


def get_num_votes(self):
'''
Expand Down
1 change: 1 addition & 0 deletions iam/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
url(r'^auth-event/(?P<pk>\d+)/census/send_auth/$', views.census_send_auth, name='census_send_auth'),
url(r'^auth-event/(?P<pk>\d+)/census/reset-voter/$', views.census_reset_voter, name='census_reset_voter'),
url(r'^auth-event/(?P<pk>\d+)/(?P<status>(notstarted|started|stopped|suspended|resumed))/$', views.ae_status, name='ae_status'),
url(r'^auth-event/(?P<pk>\d+)/turnout/$', views.turnout, name='turnout'),
url(r'^auth-event/module/$', views.authevent_module, name='authevent_module'),
url(r'^auth-event/module/(?P<name>[-\w]+)/$', views.authevent_module, name='authevent_module'),

Expand Down
22 changes: 22 additions & 0 deletions iam/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import mimetypes
from datetime import datetime
from django import forms
from django.core import serializers
from django.conf import settings
from django.http import Http404
from django.db.models import Q, IntegerField
Expand Down Expand Up @@ -2351,6 +2352,27 @@ def post(self, request, pk):

census_reset_voter = login_required(CensusResetVoter.as_view())

class Turnout(View):
def get(self, request, pk):
permission_required(request.user, 'AuthEvent', 'view', pk)
ae = get_object_or_404(AuthEvent, pk=pk)

ids = [pk]
data = {}
if ae.children_election_info:
ids.extend(ae.children_election_info['natural_order'])

for id in ids:
id_ae = get_object_or_404(AuthEvent, pk=pk)
data[id] = {
'users': id_ae.len_census(),
'total_votes': id_ae.get_num_votes(),
'votes_per_hour': id_ae.get_votes_per_hour()
}
return json_response(data)

turnout = login_required(Turnout.as_view())

class GetImage(View):
def get(self, request, pk, uid):
permission_required(request.user, 'AuthEvent', 'edit', pk)
Expand Down

0 comments on commit acbea0c

Please sign in to comment.