datasource: import/export (#13722)

This commit is contained in:
Lauréline Guérin 2020-06-12 14:41:31 +02:00
parent 106787acef
commit 7fe3a4b473
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
5 changed files with 130 additions and 4 deletions

View File

@ -5450,6 +5450,63 @@ def test_data_sources_in_use_delete(pub):
assert 'delete-button' in resp.text
def test_data_sources_export(pub):
create_superuser(pub)
create_role()
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'formula', 'value': '[]'}
data_source.store()
app = login(get_app(pub))
resp = app.get('/backoffice/settings/data-sources/1/')
resp = resp.click(href='export')
xml_export = resp.text
ds = StringIO(xml_export)
data_source2 = NamedDataSource.import_from_xml(ds)
assert data_source2.name == 'foobar'
def test_data_sources_import(pub):
create_superuser(pub)
create_role()
NamedDataSource.wipe()
data_source = NamedDataSource(name='foobar')
data_source.data_source = {'type': 'formula', 'value': '[]'}
data_source.store()
data_source_xml = ET.tostring(data_source.export_to_xml(include_id=True))
NamedDataSource.wipe()
assert NamedDataSource.count() == 0
app = login(get_app(pub))
resp = app.get('/backoffice/settings/data-sources/')
resp = resp.click(href='import')
resp.forms[0]['file'] = Upload('datasource.wcs', data_source_xml)
resp = resp.forms[0].submit()
assert NamedDataSource.count() == 1
# import the same datasource a second time, make sure slug is not reused
resp = app.get('/backoffice/settings/data-sources/')
resp = resp.click(href='import')
resp.forms[0]['file'] = Upload('datasource.wcs', data_source_xml)
resp = resp.forms[0].submit()
assert NamedDataSource.count() == 2
assert NamedDataSource.get(1).slug == 'foobar'
assert NamedDataSource.get(2).slug == 'foobar-1'
# import an invalid file
resp = app.get('/backoffice/settings/data-sources/')
resp = resp.click(href='import')
resp.form['file'] = Upload('datasource.wcs', b'garbage')
resp = resp.form.submit()
assert 'Invalid File' in resp.text
def test_data_sources_edit_slug(pub):
create_superuser(pub)
NamedDataSource.wipe()

View File

@ -564,6 +564,7 @@ def test_named_datasource_id_parameter(requests_pub):
def test_named_datasource_in_formdef():
from wcs.formdef import FormDef
NamedDataSource.wipe()
datasource = NamedDataSource(name='foobar')
datasource.data_source = {'type': 'json', 'value': 'http://whatever/'}
datasource.store()

View File

@ -14,14 +14,21 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import xml.etree.ElementTree as ET
from quixote import redirect
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
from wcs.qommon import _
from wcs.qommon import _, force_str
from wcs.qommon import errors, template
from wcs.qommon.form import *
from wcs.qommon.misc import json_response
from wcs.qommon.form import FileWidget
from wcs.qommon.form import Form
from wcs.qommon.form import UrlWidget
from wcs.qommon.form import get_response
from wcs.qommon.form import get_session
from wcs.qommon import misc
from wcs.qommon.backoffice.menu import html_top
from wcs.data_sources import (NamedDataSource, DataSourceSelectionWidget,
get_structured_items)
@ -123,7 +130,7 @@ class NamedDataSourceUI(object):
class NamedDataSourcePage(Directory):
_q_exports = ['', 'edit', 'delete']
_q_exports = ['', 'edit', 'delete', 'export']
do_not_call_in_templates = True
def __init__(self, component):
@ -222,9 +229,19 @@ class NamedDataSourcePage(Directory):
self.datasource.remove_self()
return redirect('..')
def export(self):
x = self.datasource.export_to_xml(include_id=True)
misc.indent_xml(x)
response = get_response()
response.set_content_type('application/x-wcs-datasource')
response.set_header(
'content-disposition',
'attachment; filename=datasource-%s.wcs' % self.datasource.slug)
return '<?xml version="1.0"?>\n' + force_str(ET.tostring(x))
class NamedDataSourcesDirectory(Directory):
_q_exports = ['', 'new']
_q_exports = ['', 'new', ('import', 'p_import')]
def _q_traverse(self, path):
get_response().breadcrumb.append( ('data-sources/', _('Data Sources')) )
@ -239,6 +256,7 @@ class NamedDataSourcesDirectory(Directory):
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % _('Data Sources')
r += htmltext('<span class="actions">')
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
r += htmltext('<a class="new-item" href="new">%s</a>') % _('New Data Source')
r += htmltext('</span>')
r += htmltext('</div>')
@ -275,3 +293,51 @@ class NamedDataSourcesDirectory(Directory):
def _q_lookup(self, component):
return NamedDataSourcePage(component)
def p_import(self):
form = Form(enctype='multipart/form-data')
import_title = _('Import Data Source')
form.add(FileWidget, 'file', title=_('File'), required=True)
form.add_submit('submit', import_title)
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
return redirect('.')
if form.is_submitted() and not form.has_errors():
try:
return self.import_submit(form)
except ValueError:
pass
get_response().breadcrumb.append(('import', _('Import')))
html_top('datasources', title=import_title)
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % import_title
r += htmltext('<p>%s</p>') % _(
'You can install a new data source by uploading a file '
'or by pointing to the data source URL.')
r += form.render()
return r.getvalue()
def import_submit(self, form):
self.imported_datasource = None
fp = form.get_widget('file').parse().fp
error = False
try:
datasource = NamedDataSource.import_from_xml(fp)
get_session().message = (
'info', _('This datasource has been successfully imported.'))
except ValueError:
error = True
if error:
form.set_error('file', _('Invalid File'))
raise ValueError()
self.imported_datasource = datasource
datasource.slug = None # a new one will be set in .store()
datasource.store()
return redirect('%s/' % datasource.id)

View File

@ -264,6 +264,7 @@ def get_object(data_source):
class NamedDataSource(XmlStorableObject):
_names = 'datasources'
_indexes = ['slug']
_xml_tagname = 'datasources'
name = None

View File

@ -4,6 +4,7 @@
<div id="appbar">
<h2>{% trans "Data Source" %} - {{ datasource.name }}</h2>
<span class="actions">
<a href="export">{% trans "Export" %}</a>
<a href="delete" rel="popup">{% trans "Delete" %}</a>
<a href="edit">{% trans "Edit" %}</a>
</span>