Add task scheduling to election/admin UI

This commit is contained in:
RunasSudo 2017-12-12 20:49:02 +10:30
parent 788c5c006c
commit 6ba9b1369a
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
5 changed files with 82 additions and 9 deletions

View File

@ -93,6 +93,15 @@ class WorkflowTask(EmbeddedObject):
self.fire_event('exit') self.fire_event('exit')
self.on_exit() self.on_exit()
def get_entry_task(self):
election = self.recurse_parents('eos.base.election.Election')
for task in WorkflowTaskEntryTask.get_all():
if task.election_id == election._id and task.workflow_task == self._name:
return task
return None
class Workflow(EmbeddedObject): class Workflow(EmbeddedObject):
tasks = EmbeddedObjectListField() tasks = EmbeddedObjectListField()
meta = { meta = {

View File

@ -66,7 +66,7 @@ class TaskScheduler:
tasks = Task.get_all() tasks = Task.get_all()
for task in tasks: for task in tasks:
if task.status == Task.Status.READY and task.run_at and task.run_at < DateTimeField.now(): if task.status == Task.Status.READY:
pending_tasks.append(task) pending_tasks.append(task)
return pending_tasks return pending_tasks
@ -100,4 +100,5 @@ class TaskScheduler:
@staticmethod @staticmethod
def tick(): def tick():
for task in TaskScheduler.pending_tasks(): for task in TaskScheduler.pending_tasks():
task.run() if task.run_at and task.run_at < DateTimeField.now():
task.run()

View File

@ -229,11 +229,33 @@ def election_admin_enter_task(election):
if workflow_task.status != WorkflowTask.Status.READY: if workflow_task.status != WorkflowTask.Status.READY:
return flask.Response('Task is not yet ready or has already exited', 409) return flask.Response('Task is not yet ready or has already exited', 409)
task = WorkflowTaskEntryTask(election_id=election._id, workflow_task=workflow_task._name, status=Task.Status.READY, run_strategy=EosObject.lookup(app.config['TASK_RUN_STRATEGY'])()) task = WorkflowTaskEntryTask(
election_id=election._id,
workflow_task=workflow_task._name,
status=Task.Status.READY,
run_strategy=EosObject.lookup(app.config['TASK_RUN_STRATEGY'])()
)
task.run() task.run()
return flask.redirect(flask.url_for('election_admin_summary', election_id=election._id)) return flask.redirect(flask.url_for('election_admin_summary', election_id=election._id))
@app.route('/election/<election_id>/admin/schedule_task', methods=['POST'])
@using_election
@election_admin
def election_admin_schedule_task(election):
workflow_task = election.workflow.get_task(flask.request.form['task_name'])
task = WorkflowTaskEntryTask(
election_id=election._id,
workflow_task=workflow_task._name,
run_at=DateTimeField().deserialise(flask.request.form['datetime']),
status=Task.Status.READY,
run_strategy=EosObject.lookup(app.config['TASK_RUN_STRATEGY'])()
)
task.save()
return flask.redirect(flask.url_for('election_admin_summary', election_id=election._id))
@app.route('/election/<election_id>/cast_ballot', methods=['POST']) @app.route('/election/<election_id>/cast_ballot', methods=['POST'])
@using_election @using_election
def election_api_cast_vote(election): def election_api_cast_vote(election):

View File

@ -28,4 +28,22 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
<h2>Schedule a task</h2>
<form class="ui form" action="{{ url_for('election_admin_schedule_task', election_id=election._id) }}" method="post">
<div class="field">
<label>Task</label>
<select class="ui dropdown" name="task_name">
{% for task in election.workflow.tasks %}
<option value="{{ task._name }}">{{ task.label }}</option>
{% endfor %}
</select>
</div>
<div class="field">
<label>UTC date and time (ISO 8601)</label>
<input name="datetime" value="{{ eos.core.objects.DateTimeField().serialise(eos.core.objects.DateTimeField.now()) }}" type="text">
</div>
<input type="submit" class="ui primary button" value="Schedule task">
</form>
{% endblock %} {% endblock %}

View File

@ -37,17 +37,40 @@
<p> <p>
Voting in this {{ election.kind }} Voting in this {{ election.kind }}
{% if election.workflow.get_task('eos.base.workflow.TaskOpenVoting').status == Status.EXITED %} {% if election.workflow.get_task('eos.base.workflow.TaskOpenVoting').status == Status.EXITED %}
opened at {{ election.workflow.get_task('eos.base.workflow.TaskOpenVoting').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC opened at
{% if election.workflow.get_task('eos.base.workflow.TaskOpenVoting').get_entry_task().run_at %}
{{ election.workflow.get_task('eos.base.workflow.TaskOpenVoting').get_entry_task().run_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC
according to schedule,
{% else %}
{{ election.workflow.get_task('eos.base.workflow.TaskOpenVoting').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC
at the administrator's discretion,
{% endif %}
{% else %} {% else %}
is scheduled to open is scheduled to open at
{% if election.workflow.get_task('eos.base.workflow.TaskOpenVoting').get_entry_task().run_at %}
{{ election.workflow.get_task('eos.base.workflow.TaskOpenVoting').get_entry_task().run_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC,
{% else %}
the administrator's discretion,
{% endif %}
{% endif %} {% endif %}
at the administrators' discretion, and and
{% if election.workflow.get_task('eos.base.workflow.TaskCloseVoting').status == Status.EXITED %} {% if election.workflow.get_task('eos.base.workflow.TaskCloseVoting').status == Status.EXITED %}
closed at {{ election.workflow.get_task('eos.base.workflow.TaskCloseVoting').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC closed at
{% if election.workflow.get_task('eos.base.workflow.TaskCloseVoting').get_entry_task().run_at %}
{{ election.workflow.get_task('eos.base.workflow.TaskCloseVoting').get_entry_task().run_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC
according to schedule
{% else %}
{{ election.workflow.get_task('eos.base.workflow.TaskCloseVoting').exited_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC
at the administrator's discretion
{% endif %}
{% else %} {% else %}
is scheduled to close is scheduled to close at
{% if election.workflow.get_task('eos.base.workflow.TaskCloseVoting').get_entry_task().run_at %}
{{ election.workflow.get_task('eos.base.workflow.TaskCloseVoting').get_entry_task().run_at.strftime('%Y-%m-%d %H:%M:%S') }} UTC.
{% else %}
the administrator's discretion.
{% endif %}
{% endif %} {% endif %}
at the administrators' discretion.
</p> </p>
{% else %} {% else %}
<p><button class="ui huge button">This {{ election.kind }} is not yet ready for voting</button></p> <p><button class="ui huge button">This {{ election.kind }} is not yet ready for voting</button></p>