Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Automated Allow/Launch Tally #363

Merged
merged 6 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions iam/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,18 @@ class ScheduledEventsSchema(Schema):
load_default=None,
dump_default=None
)
allow_tally = marshmallow_fields.Nested(
ScheduledEventSchema,
allow_none=True,
load_default=None,
dump_default=None
)
start_tally = marshmallow_fields.Nested(
ScheduledEventSchema,
allow_none=True,
load_default=None,
dump_default=None
)


class OIDCPPublicInfoSchema(Schema):
Expand Down Expand Up @@ -925,11 +937,15 @@ def update_scheduled_events(sender, instance, **kwargs):
default_events = dict(
start_voting=None,
end_voting=None,
allow_tally=None,
start_tally=None,
)

alt_status = dict(
start_voting='start',
end_voting='stop',
allow_tally='allow-tally',
start_tally='tally',
)

events = (instance.scheduled_events
Expand Down Expand Up @@ -1168,6 +1184,10 @@ def create_user_data(sender, instance, created, *args, **kwargs):
('authevent:start_voting:revoked', 'authevent:start_voting:revoked'),
('authevent:end_voting:scheduled', 'authevent:end_voting:scheduled'),
('authevent:end_voting:revoked', 'authevent:end_voting:revoked'),
('authevent:allow_tally:scheduled', 'authevent:allow_tally:scheduled'),
('authevent:allow_tally:revoked', 'authevent:allow_tally:revoked'),
('authevent:start_tally:scheduled', 'authevent:start_tally:scheduled'),
('authevent:start_tally:revoked', 'authevent:start_tally:revoked'),
)

class Action(models.Model):
Expand Down
47 changes: 39 additions & 8 deletions iam/api/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import plugins
from authmethods.sms_provider import SMSProvider
from utils import send_codes, generate_access_token_hmac, reproducible_json_dumps
from api import views
from .models import Action, AuthEvent, BallotBox, TallySheet

logger = get_task_logger(__name__)
Expand Down Expand Up @@ -1031,6 +1032,28 @@ def set_public_candidates_task(
)
action.save()

def run_start_tally(
user_id,
auth_event_id,
):
'''
Launches the a ballot box action call in a task. If the election has
children, also launches the call for those.
'''
logger.info(
f'\n\nrun_start_tally_task(user_id={user_id}, auth_event_id={auth_event_id})'
)
user = get_object_or_404(User, pk=user_id)
auth_event = get_object_or_404(AuthEvent, pk=auth_event_id)
elements = auth_event.children_election_info['natural_order'] if auth_event.children_election_info else []
req = {
"children_election_ids": elements,
"force_tally": "force-all",
"mode": "all"
}

views.TallyStatusView.tally_status_post(auth_event_id, req, user)

def run_ballot_box_action(
action_name,
user_id,
Expand Down Expand Up @@ -1173,13 +1196,21 @@ def set_status_task(status, user_id, auth_event_id, parent_auth_event_id=None):
'resume': 'resumed',
}
def set_status_inner(auth_event):
if status in ['allow-tally', 'tally']:
return
auth_event.status = alt_status[status]
auth_event.save()

run_ballot_box_action(
action_name=status,
user_id=user_id,
auth_event_id=auth_event_id,
auth_event_callback_func=set_status_inner,
apply_callback=(status in ['start', 'stop', 'suspend', 'resume'])
)

if 'tally' == status:
run_start_tally(
user_id=user_id,
auth_event_id=auth_event_id,
)
else:
run_ballot_box_action(
action_name=status,
user_id=user_id,
auth_event_id=auth_event_id,
auth_event_callback_func=set_status_inner,
apply_callback=(status in ['start', 'stop', 'suspend', 'resume', 'allow-tally', 'tally'])
)
30 changes: 20 additions & 10 deletions iam/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2903,22 +2903,14 @@ def delete(self, request, pk, ballot_box_pk, tally_sheet_pk):

class TallyStatusView(View):

def post(self, request, pk):
def tally_status_post(pk, req, user):
'''
Launches the tallly in a celery background task. If the
election has children, also launches the tally for them.
'''
# check permissions
permission_required(
request.user,
'AuthEvent',
['edit', 'tally'],
pk
)

# get AuthEvent and parse request json
auth_event = get_object_or_404(AuthEvent, pk=pk)
req = parse_json_request(request)

# cannot launch tally on an election whose voting period is still open
# or has not even started.
Expand Down Expand Up @@ -3016,7 +3008,7 @@ def post(self, request, pk):

# log the action
action = Action(
executer=request.user,
executer=user,
receiver=None,
action_name='authevent:tally',
event=auth_event,
Expand All @@ -3035,6 +3027,24 @@ def post(self, request, pk):
# celery task
return json_response()

def post(self, request, pk):
'''
Launches the tallly in a celery background task. If the
election has children, also launches the tally for them.
'''
# check permissions
permission_required(
request.user,
'AuthEvent',
['edit', 'tally'],
pk
)

# get AuthEvent and parse request json
req = parse_json_request(request)
self.tally_status_post(pk, req, request.user)


def get(self, request, pk):
'''
Returns the tally status of an election and its children
Expand Down
Loading