Skip to content
Open
6 changes: 5 additions & 1 deletion apollo/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def on_failure(self, exc, task_id, args, kwargs, einfo):
'progress': self.task_info,
'description': TASK_DESCRIPTIONS.get(self.request.task, _('Task')),
'quit': True,
'name': self.request.task.split('.')[-1],
}

if channel is not None:
Expand All @@ -207,6 +208,7 @@ def on_success(self, retval, task_id, args, kwargs):
'progress': self.task_info,
'description': TASK_DESCRIPTIONS.get(self.request.task, _('Task')),
'quit': True,
'name': self.request.task.split('.')[-1],
}

if channel is not None:
Expand All @@ -227,7 +229,9 @@ def update_task_info(self, **kwargs):
'id': request.id,
'status': _('RUNNING'),
'progress': task_metadata.get('result'),
'description': TASK_DESCRIPTIONS.get(self.request.task, _('Task'))
'description': TASK_DESCRIPTIONS.get(self.request.task, _('Task')),
'name': self.request.task.split('.')[-1],
'quit': False,
}

if channel is not None:
Expand Down
20 changes: 0 additions & 20 deletions apollo/formsframework/tasks.py

This file was deleted.

1 change: 1 addition & 0 deletions apollo/formsframework/views_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def forms_list(view):
'breadcrumbs': breadcrumbs,
'init_form': checklist_init_form,
'form_import_form': form_import_form,
'channel': session.get('_id'),
}

return view.render(template_name, **context)
Expand Down
1 change: 1 addition & 0 deletions apollo/locations/views_locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def locations_list(view, location_set_id):
else:
ctx = dict(
args=args,
channel=session.get('_id'),
extra_fields=extra_fields,
filter_form=queryset_filter.form,
breadcrumbs=breadcrumbs,
Expand Down
3 changes: 2 additions & 1 deletion apollo/participants/views_participants.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@ def participant_list(participant_set_id=0, view=None):
location_types=helpers.displayable_location_types(
is_administrative=True, location_set_id=location_set_id),
participants=queryset_filterset.qs.paginate(
page=page, per_page=current_app.config.get('PAGE_SIZE'))
page=page, per_page=current_app.config.get('PAGE_SIZE')),
channel=session.get('_id'),
)

if view:
Expand Down
122 changes: 122 additions & 0 deletions apollo/static/js/task-toasts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
function setupTaskToasts(appSelector, taskNames, taskListUrl, eventStreamUrl) {
var roundTo = function(num) {
return Math.round((num + 0.00001) * 100) / 100;
};

Vue.component('task-toast', {
updated: function() {
// clear completed tasks
if (this.taskInfo.quit == true)
this.clear();
},
template: '#task-toast',
props: {
taskInfo: {
id: String,
status: String,
quit: Boolean,
progress: Object,
description: String,
name: String
}
},
methods: {
clear: function() {
var self = this;
setTimeout(function() {
$(self.$el).toast('hide');
}, 5000);
}
},
computed: {
processedCount: function() {
return this.taskInfo.progress.processed_records;
},
warningCount: function() {
return this.taskInfo.progress.warning_records;
},
errorCount: function() {
return this.taskInfo.progress.error_records;
},
totalCount: function() {
return this.taskInfo.progress.total_records;
},
processedStyle: function() {
return {width: roundTo(100 * this.taskInfo.progress.processed_records / this.taskInfo.progress.total_records) + '%'};
},
warningStyle: function() {
return {width: roundTo(100 * this.taskInfo.progress.warning_records / this.taskInfo.progress.total_records) + '%'};
},
errorStyle: function() {
return {width: roundTo(100 * this.taskInfo.progress.error_records / this.taskInfo.progress.total_records) + '%'};
}
},
mounted: function() {
$(this.$el).toast('show');
var self = this;
if (this.taskInfo.quit == true) {
self.clear();
}
},
destroyed: function() {
$(this.el).toast('dispose');
}
});

var vm = new Vue({
el: appSelector,
data: {
tasks: []
},
methods: {
addTask: function(taskInfo) {
// skip unregistered tasks
if (taskNames.indexOf(taskInfo.name) === -1)
return;

var self = this;
var index = self.tasks.findIndex(function(task) {
return task.id == taskInfo.id;
});
if (index == -1)
self.tasks.unshift(taskInfo);
else
self.tasks.splice(index, 1, taskInfo);
},
loadCompletedTasks: function() {
var self = this;
fetch(taskListUrl)
.then(function(response) {
return response.json();
}).then(function(data) {
if (data.results.length > 0) {
data.results.forEach(function(taskInfo) {
self.addTask(taskInfo);
});
}

}).catch(function(err) {
console.error(err);
});
},
loadRunningTasks: function() {
var self = this;
if (window.EventSource) {
var es = new EventSource(eventStreamUrl);
es.onmessage = function (ev) {
let data = JSON.parse(ev.data);
if (data.id !== undefined) {
self.addTask(data);
}
};
}
}
},
mounted: function() {
this.loadCompletedTasks();
this.loadRunningTasks();
}
});

return vm;
}
1 change: 0 additions & 1 deletion apollo/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

celery = create_celery_app()

from apollo.formsframework.tasks import update_submissions
from apollo.messaging.tasks import send_messages, send_email
from apollo.participants.tasks import import_participants
from apollo.locations.tasks import import_locations
Expand Down
18 changes: 17 additions & 1 deletion apollo/templates/admin/form_list.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{% extends "admin/base.html" %}
{%- from 'admin/task_toast.html' import render_toast_parent, render_toast_template -%}
{%- set toast_app_id = 'toast_app' -%}

{%- block body %}
<div class="d-flex justify-content-between align-items-md-center flex-row mb-3">
Expand Down Expand Up @@ -172,11 +174,25 @@ <h5 id="formImportModalLabel" class="modal-title">{{ _('Import Form') }}</h5>
</div>
</form>
{% endblock %}

{% block footer %}
{{ render_toast_parent(toast_app_id) }}
{% endblock footer %}
{%- block tail_js %}
{{ super() }}
{{ render_toast_template() }}
<script src="{{ url_for('static', filename='js/vue.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/EventSource.js') }}"></script>
<script src="{{ url_for('static', filename='js/promise-polyfill.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/fetch.umd.js') }}"></script>
<script src="{{ url_for('static', filename='js/task-toasts.js') }}"></script>
<script>
var taskAppSelector = '#{{ toast_app_id }}';
var taskNames = ['init_submissions', 'init_survey_submissions'];
var taskListUrl = "{{ url_for('users.task_list') }}";
var eventStreamUrl = "{{ url_for('sse.stream', channel=channel) }}";

var toastVm = setupTaskToasts(taskAppSelector, taskNames, taskListUrl, eventStreamUrl);

Vue.component('create-checklists-modal-body', {
template: `
<div>
Expand Down
20 changes: 19 additions & 1 deletion apollo/templates/admin/locations_list.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{%- extends 'admin/master.html' -%}
{%- from 'admin/locations_list_filter.html' import render_filter_form -%}
{%- from 'admin/task_toast.html' import render_toast_parent, render_toast_template -%}
{%- from 'frontend/macros/pagination.html' import render_pager, render_pager_counter -%}

{%- set toast_app_id = 'app' -%}

{% block head_css %}
{{ super() }}
Expand Down Expand Up @@ -148,13 +149,29 @@ <h5 class="modal-title" id="finalizeModalLabel">{{ _('Import Locations') }}</h5>
</div>
{% endblock %}

{% block footer %}
{{ render_toast_parent(toast_app_id) }}
{% endblock footer %}

{%- block tail_js -%}
{{ super() }}
{{ render_toast_template() }}
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap-wizard.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.form.js') }}"></script>
<script src="{{ url_for('static', filename='js/vue.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/EventSource.js') }}"></script>
<script src="{{ url_for('static', filename='js/promise-polyfill.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/fetch.umd.js') }}"></script>
<script src="{{ url_for('static', filename='js/task-toasts.js') }}"></script>
<script type="text/javascript">
$(function(){
var taskAppSelector = '#{{ toast_app_id }}';
var taskNames = ['import_locations'];
var taskListUrl = "{{ url_for('users.task_list') }}";
var eventStreamUrl = "{{ url_for('sse.stream', channel=channel) }}";

var vm = setupTaskToasts(taskAppSelector, taskNames, taskListUrl, eventStreamUrl);

$('#filter_reset').on('click', function() {
var $form = $(this).parents('form').first();
$form.find(':input').not('button').each(function() { $(this).val(''); })
Expand Down Expand Up @@ -238,6 +255,7 @@ <h5 class="modal-title" id="finalizeModalLabel">{{ _('Import Locations') }}</h5>
wizard.submitSuccess();
wizard.updateProgressBar(0);
wizard.close();
vm.loadRunningTasks();
}).fail(function (data) {
wizard.submitFailure();
wizard.showButtons();
Expand Down
40 changes: 30 additions & 10 deletions apollo/templates/admin/participant_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
{%- from 'frontend/macros/participant_list_filter.html' import render_filter_form -%}
{%- from 'frontend/macros/pagination.html' import render_pager -%}
{%- from 'frontend/macros/send_message.html' import send_message_modal -%}
{%- from 'admin/task_toast.html' import render_toast_parent, render_toast_template -%}
{%- set toast_app_id = 'app' -%}

{% block head_css %}
{{ super() }}
Expand Down Expand Up @@ -214,29 +216,29 @@
<div class="wizard-card" data-cardname="uploadFile">
<h3>{{ _('Upload File') }}</h3>
<div class="wizard-input-section">
<form enctype="multipart/form-data" method="POST" id="upload-form" action="{{ url_for('participantset.participants_import', participant_set_id=participant_set_id) }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<div class="custom-file">
<input type="file" class="custom-file-input upload" id="uploadFile" name="spreadsheet">
<label for="uploadFile" class="custom-file-label" data-browse="{{ _('Browse') }}">{{ _('Choose file') }}</label>
<small class="form-text text-muted" id="uploadFileHelptext">{{ _('Only .csv, .xls and .xlsx files') }}</small>
</div>
</form>
<form enctype="multipart/form-data" method="POST" id="upload-form" action="{{ url_for('participantset.participants_import', participant_set_id=participant_set_id) }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<div class="custom-file">
<input type="file" class="custom-file-input upload" id="uploadFile" name="spreadsheet">
<label for="uploadFile" class="custom-file-label" data-browse="{{ _('Browse') }}">{{ _('Choose file') }}</label>
<small class="form-text text-muted" id="uploadFileHelptext">{{ _('Only .csv, .xls and .xlsx files') }}</small>
</div>
</form>
</div>
</div>

<div class="wizard-card pr-0" data-cardname="mapFields">
<h3>{{ _('Map Fields') }}</h3>
<div class="wizard-input-section overflow-auto"></div>
</div>

<div class="wizard-card" data-cardname="finalize">
<h3>{{ _('Finalize') }}</h3>
<div class="wizard-input-section">
<div class="alert alert-info">
<span class="create-server-name">{% trans %}Click the <strong>Submit</strong> button to begin the import process.{% endtrans %}</span>
</div>
</div>

<div class="wizard-failure">
<div class="alert alert-danger">
{% trans %}There was a problem submitting the form. Please try again in a minute.{% endtrans %}
Expand All @@ -246,12 +248,29 @@ <h3>{{ _('Finalize') }}</h3>
</div>
{%- endblock -%}

{% block footer %}
{{ render_toast_parent(toast_app_id) }}
{% endblock footer %}

{%- block tail_js -%}
{{ super() }}
{{ render_toast_template() }}
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap-wizard.min.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery.form.js') }}"></script>
<script src="{{ url_for('static', filename='js/vue.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/EventSource.js') }}"></script>
<script src="{{ url_for('static', filename='js/promise-polyfill.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/fetch.umd.js') }}"></script>
<script src="{{ url_for('static', filename='js/task-toasts.js') }}"></script>
<script type="text/javascript">
$(function(){
var taskAppSelector = '#{{ toast_app_id }}';
var taskNames = ['import_participants'];
var taskListUrl = "{{ url_for('users.task_list') }}";
var eventStreamUrl = "{{ url_for('sse.stream', channel=channel) }}";

var vm = setupTaskToasts(taskAppSelector, taskNames, taskListUrl, eventStreamUrl);

$('#filter_reset').on('click', function() {
var $form = $(this).parents('form').first();
$form.find(':input').not('button').each(function() { $(this).val(''); })
Expand Down Expand Up @@ -333,6 +352,7 @@ <h3>{{ _('Finalize') }}</h3>
wizard.submitSuccess();
wizard.updateProgressBar(0);
wizard.close();
vm.loadRunningTasks();
}).fail(function () {
wizard.submitFailure();
wizard.showButtons();
Expand Down
Loading