Implement batch operations on elections #11

This commit is contained in:
RunasSudo 2019-03-06 13:43:54 +11:00
parent fd6f6bc4b1
commit 1bb0197b46
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 106 additions and 61 deletions

View File

@ -1,5 +1,5 @@
# Eos - Verifiable elections
# Copyright © 2017-18 RunasSudo (Yingtong Li)
# Copyright © 2017-2019 RunasSudo (Yingtong Li)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@ -120,50 +120,6 @@ def run_tests(prefix, lang):
def sessdb():
app.session_interface.db.create_all()
# TODO: Will remove this once we have a web UI
@app.cli.command('drop_db_and_setup')
def setup_test_election():
# DANGER!
dbinfo.provider.reset_db()
# Set up election
election = PSRElection()
election.workflow = PSRWorkflow()
# Set election details
election.name = 'Test Election'
from eos.redditauth.election import RedditUser
election.voters.append(UserVoter(user=EmailUser(name='Alice', email='alice@localhost')))
election.voters.append(UserVoter(user=EmailUser(name='Bob', email='bob@localhost')))
election.voters.append(UserVoter(user=EmailUser(name='Carol', email='carol@localhost')))
election.voters.append(UserVoter(user=RedditUser(username='RunasSudo')))
for voter in election.voters:
if isinstance(voter, UserVoter):
if isinstance(voter.user, EmailUser):
emails.voter_email_password(election, voter)
election.mixing_trustees.append(InternalMixingTrustee(name='Eos Voting'))
election.mixing_trustees.append(InternalMixingTrustee(name='Eos Voting'))
election.sk = EGPrivateKey.generate()
election.public_key = election.sk.public_key
question = PreferentialQuestion(prompt='President', choices=[
Ticket(name='ACME Party', choices=[
Choice(name='John Smith'),
Choice(name='Joe Bloggs', party='Independent ACME')
]),
Choice(name='John Q. Public')
], min_choices=0, max_choices=3, randomise_choices=True)
election.questions.append(question)
question = ApprovalQuestion(prompt='Chairman', choices=[Choice(name='John Doe'), Choice(name='Andrew Citizen')], min_choices=0, max_choices=1)
election.questions.append(question)
election.save()
@app.cli.command('verify_election')
@click.option('--electionid', default=None)
def verify_election(electionid):
@ -227,6 +183,22 @@ def tick_scheduler():
# === Views ===
def using_election(func):
@functools.wraps(func)
def wrapped(election_id, **kwargs):
election = Election.get_by_id(election_id)
return func(election, **kwargs)
return wrapped
def election_admin(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
if 'user' in flask.session and flask.session['user'].is_admin():
return func(*args, **kwargs)
else:
return flask.Response('Administrator credentials required', 403)
return wrapped
@app.route('/')
def index():
elections = Election.get_all()
@ -250,21 +222,34 @@ def elections():
return flask.render_template('elections.html', elections=elections)
def using_election(func):
@functools.wraps(func)
def wrapped(election_id, **kwargs):
election = Election.get_by_id(election_id)
return func(election, **kwargs)
return wrapped
def election_admin(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
if 'user' in flask.session and flask.session['user'].is_admin():
return func(*args, **kwargs)
else:
return flask.Response('Administrator credentials required', 403)
return wrapped
@app.route('/elections/batch', methods=['GET', 'POST'])
@election_admin
def elections_batch():
if flask.request.method == 'POST':
# Execute
for k, v in flask.request.form.items():
if k.startswith('election_') and v:
election_id = k[9:]
election = Election.get_by_id(election_id)
for workflow_task in election.workflow.tasks:
if workflow_task.status == eos.base.workflow.WorkflowTaskStatus.READY:
task = WorkflowTaskEntryWebTask(
election_id=election._id,
workflow_task=workflow_task._name,
status=TaskStatus.READY,
run_strategy=EosObject.lookup(app.config['TASK_RUN_STRATEGY'])()
)
task.run()
break
elections = []
for election in Election.get_all():
if any(workflow_task.status == eos.base.workflow.WorkflowTaskStatus.READY for workflow_task in election.workflow.tasks):
elections.append(election)
elections.sort(key=lambda e: e.name)
return flask.render_template('elections_batch.html', elections=elections)
@app.route('/election/<election_id>/')
@using_election

View File

@ -2,7 +2,7 @@
{#
Eos - Verifiable elections
Copyright © 2017-18 RunasSudo (Yingtong Li)
Copyright © 2017-2019 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@ -23,6 +23,10 @@
{% block content %}
<h1>All elections: Eos Voting for {{ eosweb.app.config['ORG_NAME'] }}</h1>
{% if session.user and session.user.is_admin() %}
<div style="text-align: right; font-size: small;"><a href="{{ url_for('elections_batch') }}">Batch operations</a></div>
{% endif %}
<p>Please choose an election from the list below:</p>
<ul>

View File

@ -0,0 +1,56 @@
{% extends 'base.html' %}
{#
Eos - Verifiable elections
Copyright © 2017-2019 RunasSudo (Yingtong Li)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{% block title %}Perform batch operations{% endblock %}
{% block content %}
<h1>Perform batch operations</h1>
<form method="POST">
<table class="ui selectable celled table">
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Next stage</th>
</tr>
</thead>
<tbody>
{% for election in elections %}
<tr>
<td><input type="checkbox" name="election_{{ election._id }}" id="election_{{ election._id }}"></td>
<td>{{ election.name }}</td>
<td>
<ul style="padding-left: 1em; margin: 0;">
{% for task in election.workflow.tasks %}
{% if task.status == eos.base.workflow.WorkflowTaskStatus.READY %}
<li>{{ task.label }}</li>
{% endif %}
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<input class="ui primary button" type="submit" value="Execute">
</form>
{% endblock %}