Add agent management webinterface

fixes #3
This commit is contained in:
Adrian Moennich 2014-11-17 17:33:00 +01:00
parent 2b6439272c
commit 6ce5d7e223
12 changed files with 310 additions and 2 deletions

View File

@ -1 +1,3 @@
graft indico_livesync/migrations
graft indico_livesync/templates
graft indico_livesync/static

View File

@ -16,10 +16,11 @@
from __future__ import unicode_literals
__all__ = ('LiveSyncPluginBase', 'LiveSyncAgentBase', 'SimpleChange', 'process_records', 'MARCXMLGenerator',
'Uploader', 'MARCXMLUploader')
__all__ = ('LiveSyncPluginBase', 'LiveSyncAgentBase', 'AgentForm', 'SimpleChange', 'process_records',
'MARCXMLGenerator', 'Uploader', 'MARCXMLUploader')
from .base import LiveSyncPluginBase, LiveSyncAgentBase
from .forms import AgentForm
from .simplify import SimpleChange, process_records
from .marcxml import MARCXMLGenerator
from .uploader import Uploader, MARCXMLUploader

View File

@ -22,6 +22,7 @@ from indico.core.plugins import IndicoPlugin
from indico.util.decorators import classproperty
from MaKaC.conference import CategoryManager
from indico_livesync.forms import AgentForm
from indico_livesync.models.queue import LiveSyncQueueEntry
from indico_livesync.plugin import LiveSyncPlugin
@ -48,6 +49,8 @@ class LiveSyncAgentBase(object):
plugin = None # set automatically when the agent is registered
#: the Uploader to use. only needed if run and run_initial_export are not overridden
uploader = None
#: the form used when creating/editing the agent
form = AgentForm
@classproperty
@classmethod

View File

@ -0,0 +1,27 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from indico.core.plugins import IndicoPluginBlueprint
from indico_livesync.controllers import RHAddAgent, RHEditAgent, RHDeleteAgent
blueprint = IndicoPluginBlueprint('livesync', 'indico_livesync', url_prefix='/admin/plugins/livesync')
blueprint.add_url_rule('/agents/create/<backend>', 'add_agent', RHAddAgent, methods=('GET', 'POST'))
blueprint.add_url_rule('/agents/<int:agent_id>/', 'edit_agent', RHEditAgent, methods=('GET', 'POST'))
blueprint.add_url_rule('/agents/<int:agent_id>/delete', 'delete_agent', RHDeleteAgent, methods=('POST',))

View File

@ -0,0 +1,88 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from flask import request, redirect, flash
from flask_pluginengine import render_plugin_template, current_plugin
from indico.core.db import db
from indico.util.i18n import _
from indico.web.flask.util import url_for
from indico.web.forms.base import FormDefaults
from MaKaC.webinterface.rh.admins import RHAdminBase
from indico_livesync.models.agents import LiveSyncAgent
from indico_livesync.views import WPLiveSync
def extend_plugin_details():
agents = LiveSyncAgent.find().order_by(LiveSyncAgent.name, LiveSyncAgent.id).all()
return render_plugin_template('plugin_details_extra.html', agents=agents, backends=current_plugin.agent_classes)
class RHDeleteAgent(RHAdminBase):
"""Deletes a LiveSync agent"""
def _checkParams(self):
self.agent = LiveSyncAgent.find_one(id=request.view_args['agent_id'])
def _process(self):
db.session.delete(self.agent)
flash(_('Agent deleted'), 'success')
return redirect(url_for('plugins.details', plugin='livesync'))
class RHAddAgent(RHAdminBase):
"""Adds a LiveSync agent"""
def _checkParams(self):
self.backend_name = request.view_args['backend']
self.backend = current_plugin.agent_classes[self.backend_name]
def _process(self):
form = self.backend.form(obj=FormDefaults(name=self.backend.title))
if form.validate_on_submit():
data = form.data
name = data.pop('name')
agent = LiveSyncAgent(name=name, backend_name=self.backend_name, settings=data)
db.session.add(agent)
flash(_('Agent added'), 'success')
flash(_("Don't forget to run the initial export!"), 'highlight')
return redirect(url_for('plugins.details', plugin='livesync'))
return WPLiveSync.render_template('edit_agent.html', form=form, backend=self.backend)
class RHEditAgent(RHAdminBase):
"""Edits a LiveSync agent"""
def _checkParams(self):
self.agent = LiveSyncAgent.find_one(id=request.view_args['agent_id'])
if self.agent.backend is None:
flash(_('Cannot edit an agent that is not loaded'), 'error')
return redirect(url_for('plugins.details', plugin='livesync'))
def _process(self):
form = self.agent.backend.form(obj=FormDefaults(self.agent, {'name'}, **self.agent.settings))
if form.validate_on_submit():
data = form.data
self.agent.name = data.pop('name')
self.agent.settings = data
flash(_('Agent updated'), 'success')
return redirect(url_for('plugins.details', plugin='livesync'))
return WPLiveSync.render_template('edit_agent.html', form=form, backend=self.agent.backend, agent=self.agent)

View File

@ -0,0 +1,29 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from wtforms.fields.core import StringField
from wtforms.validators import DataRequired
from indico.util.i18n import _
from indico.util.string import strip_whitespace
from indico.web.forms.base import IndicoForm
class AgentForm(IndicoForm):
name = StringField(_('Name'), [DataRequired()], filters=[strip_whitespace],
description=_("The name of the agent. Only used in the administration interface."))

View File

@ -58,6 +58,10 @@ class LiveSyncAgent(db.Model):
default={}
)
@property
def locator(self):
return {'agent_id': self.id}
@property
def backend(self):
"""Returns the backend class"""

View File

@ -16,12 +16,17 @@
from __future__ import unicode_literals
from flask import request
from indico.core.plugins import IndicoPlugin, wrap_cli_manager
from indico.core.plugins.views import WPPlugins
from indico.util.i18n import _
from indico.web.forms.base import IndicoForm
from indico.web.forms.fields import MultipleItemsField
from indico_livesync.blueprint import blueprint
from indico_livesync.cli import cli_manager
from indico_livesync.controllers import extend_plugin_details
from indico_livesync.handler import connect_signals
@ -45,6 +50,15 @@ class LiveSyncPlugin(IndicoPlugin):
super(LiveSyncPlugin, self).init()
self.agent_classes = {}
connect_signals(self)
self.template_hook('plugin-details', self._extend_plugin_details)
self.inject_js('livesync_admin_js', WPPlugins, subclasses=False,
condition=lambda: request.view_args.get('plugin') == self.name)
def get_blueprints(self):
return blueprint
def register_assets(self):
self.register_js_bundle('livesync_admin_js', 'js/livesync_admin.js')
def add_cli_command(self, manager):
manager.add_command('livesync', wrap_cli_manager(cli_manager, self))
@ -53,3 +67,7 @@ class LiveSyncPlugin(IndicoPlugin):
if name in self.agent_classes:
raise RuntimeError('Duplicate livesync agent: {}'.format(name))
self.agent_classes[name] = agent_class
def _extend_plugin_details(self, plugin, **kwargs):
if plugin == self:
return extend_plugin_details()

View File

@ -0,0 +1,19 @@
(function(global) {
global.liveSyncPluginPage = function liveSyncPluginPage() {
$('.js-delete-agent').on('click', function(e) {
e.preventDefault();
var $this = $(this);
var msg = $T('Do you really want to delete this agent and all its queue entries?');
new ConfirmPopup($T('Delete this agent?'), msg, function(confirmed) {
if (!confirmed) {
return;
}
$('<form>', {
action: $this.data('href'),
method: 'post'
}).appendTo('body').submit();
}).open();
});
};
})(window);

View File

@ -0,0 +1,23 @@
{% from 'forms/form_widget.html' import form_header, form_row, form_footer %}
<div class="plugin-header">
<h1>LiveSync</h1>
<h2>
{%- if agent -%}
{% trans %}Edit Agent{% endtrans %}
{%- else -%}
{% trans %}Add{% endtrans %} {{ backend.title }}
{%- endif -%}
</h2>
</div>
<p class="plugin-description">{{ backend.description }}</p>
{{ form_header() }}
{% for field in form.visible_fields %}
{{ form_row(field) }}
{% endfor %}
{% call form_footer() %}
<input class="i-button big highlight" type="submit" value="{% trans %}Save{% endtrans %}">
<a href="{{ url_for('plugins.details', plugin='livesync') }}" class="i-button big">{% trans %}Cancel{% endtrans %}</a>
{% endcall %}

View File

@ -0,0 +1,70 @@
<h2>{% trans %}LiveSync Agents{% endtrans %}</h2>
<div class="i-form" style="max-width: 800px;">
<table class="i-table-widget">
<thead>
<tr>
<th class="small-column">{% trans %}ID{% endtrans %}</th>
<th>{% trans %}Name{% endtrans %}</th>
<th>{% trans %}Backend{% endtrans %}</th>
<th class="small-column">{% trans %}Initial Export{% endtrans %}</th>
<th class="small-column">{% trans %}Queue{% endtrans %}</th>
<th class="action-column">{% trans %}Actions{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for agent in agents %}
<tr>
<td class="text-right">{{ agent.id }}</td>
<td>{{ agent.name }}</td>
<td>
{% if agent.backend %}
{{ agent.backend.title }}
{% else %}
<em class="text-error">{% trans name=agent.backend_name %}Not loaded: {{ name }}{% endtrans %}</em>
{% endif %}
</td>
<td>
{% if agent.initial_data_exported %}
{% trans %}Done{% endtrans %}
{% else %}
<strong>{% trans %}Pending{% endtrans %}</strong>
{% endif %}
</td>
<td class="text-right">{{ agent.queue.count() }}</td>
<td>
<a href="#" class="action-icon icon-remove js-delete-agent"
data-href="{{ url_for_plugin('livesync.delete_agent', agent) }}"></a>
{%- if agent.backend -%}
<a class="action-icon icon-edit" href="{{ url_for_plugin('livesync.edit_agent', agent) }}"></a>
{%- endif -%}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% for name, backend in backends.items() | sort(attribute='1.title') %}
<a class="i-button icon-plus i-form-button" href="{{ url_for_plugin('livesync.add_agent', backend=name) }}">{% trans %}Add{% endtrans %} {{ backend.title }}</a>
{% endfor %}
{% set missing_initial_export = agents|rejectattr('initial_data_exported')|list %}
{% if missing_initial_export %}
<p>
{% trans -%}
You still need to run the initial export for some agents by executing the commands below in a shell.<br>
Please note that this may take a very long time if there are many events in Indico!
{%- endtrans %}
</p>
<pre class="code"><code>
{#- Don't "fix" the indentation of these lines! -#}
{%- for agent in missing_initial_export -%}
indico livesync initial_export {{ agent.id }}
{% endfor -%}
</code></pre>
{% endif %}
</div>
<script>
liveSyncPluginPage();
</script>

View File

@ -0,0 +1,24 @@
# This file is part of Indico.
# Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from indico.core.plugins import WPJinjaMixinPlugin
from indico.core.plugins.views import WPPlugins
class WPLiveSync(WPJinjaMixinPlugin, WPPlugins):
pass