0001-datasource-import-export-13722.patch
tests/test_admin_pages.py | ||
---|---|---|
5450 | 5450 |
assert 'delete-button' in resp.text |
5451 | 5451 | |
5452 | 5452 | |
5453 |
def test_data_sources_export(pub): |
|
5454 |
create_superuser(pub) |
|
5455 |
create_role() |
|
5456 | ||
5457 |
NamedDataSource.wipe() |
|
5458 |
data_source = NamedDataSource(name='foobar') |
|
5459 |
data_source.data_source = {'type': 'formula', 'value': '[]'} |
|
5460 |
data_source.store() |
|
5461 | ||
5462 |
app = login(get_app(pub)) |
|
5463 |
resp = app.get('/backoffice/settings/data-sources/1/') |
|
5464 | ||
5465 |
resp = resp.click(href='export') |
|
5466 |
xml_export = resp.text |
|
5467 | ||
5468 |
ds = StringIO(xml_export) |
|
5469 |
data_source2 = NamedDataSource.import_from_xml(ds) |
|
5470 |
assert data_source2.name == 'foobar' |
|
5471 | ||
5472 | ||
5473 |
def test_data_sources_import(pub): |
|
5474 |
create_superuser(pub) |
|
5475 |
create_role() |
|
5476 | ||
5477 |
NamedDataSource.wipe() |
|
5478 |
data_source = NamedDataSource(name='foobar') |
|
5479 |
data_source.data_source = {'type': 'formula', 'value': '[]'} |
|
5480 |
data_source.store() |
|
5481 |
data_source_xml = ET.tostring(data_source.export_to_xml(include_id=True)) |
|
5482 | ||
5483 |
NamedDataSource.wipe() |
|
5484 |
assert NamedDataSource.count() == 0 |
|
5485 | ||
5486 |
app = login(get_app(pub)) |
|
5487 |
resp = app.get('/backoffice/settings/data-sources/') |
|
5488 |
resp = resp.click(href='import') |
|
5489 |
resp.forms[0]['file'] = Upload('datasource.wcs', data_source_xml) |
|
5490 |
resp = resp.forms[0].submit() |
|
5491 |
assert NamedDataSource.count() == 1 |
|
5492 | ||
5493 |
# import the same datasource a second time, make sure slug is not reused |
|
5494 |
resp = app.get('/backoffice/settings/data-sources/') |
|
5495 |
resp = resp.click(href='import') |
|
5496 |
resp.forms[0]['file'] = Upload('datasource.wcs', data_source_xml) |
|
5497 |
resp = resp.forms[0].submit() |
|
5498 |
assert NamedDataSource.count() == 2 |
|
5499 |
assert NamedDataSource.get(1).slug == 'foobar' |
|
5500 |
assert NamedDataSource.get(2).slug == 'foobar-1' |
|
5501 | ||
5502 |
# import an invalid file |
|
5503 |
resp = app.get('/backoffice/settings/data-sources/') |
|
5504 |
resp = resp.click(href='import') |
|
5505 |
resp.form['file'] = Upload('datasource.wcs', b'garbage') |
|
5506 |
resp = resp.form.submit() |
|
5507 |
assert 'Invalid File' in resp.text |
|
5508 | ||
5509 | ||
5453 | 5510 |
def test_data_sources_edit_slug(pub): |
5454 | 5511 |
create_superuser(pub) |
5455 | 5512 |
NamedDataSource.wipe() |
tests/test_datasource.py | ||
---|---|---|
564 | 564 | |
565 | 565 |
def test_named_datasource_in_formdef(): |
566 | 566 |
from wcs.formdef import FormDef |
567 |
NamedDataSource.wipe() |
|
567 | 568 |
datasource = NamedDataSource(name='foobar') |
568 | 569 |
datasource.data_source = {'type': 'json', 'value': 'http://whatever/'} |
569 | 570 |
datasource.store() |
wcs/admin/data_sources.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import xml.etree.ElementTree as ET |
|
18 | ||
17 | 19 |
from quixote import redirect |
18 | 20 |
from quixote.directory import Directory |
19 | 21 |
from quixote.html import TemplateIO, htmltext |
20 | 22 | |
21 |
from wcs.qommon import _ |
|
23 |
from wcs.qommon import _, force_str
|
|
22 | 24 |
from wcs.qommon import errors, template |
23 | 25 |
from wcs.qommon.form import * |
24 |
from wcs.qommon.misc import json_response |
|
26 |
from wcs.qommon.form import FileWidget |
|
27 |
from wcs.qommon.form import Form |
|
28 |
from wcs.qommon.form import UrlWidget |
|
29 |
from wcs.qommon.form import get_response |
|
30 |
from wcs.qommon.form import get_session |
|
31 |
from wcs.qommon import misc |
|
25 | 32 |
from wcs.qommon.backoffice.menu import html_top |
26 | 33 |
from wcs.data_sources import (NamedDataSource, DataSourceSelectionWidget, |
27 | 34 |
get_structured_items) |
... | ... | |
123 | 130 | |
124 | 131 | |
125 | 132 |
class NamedDataSourcePage(Directory): |
126 |
_q_exports = ['', 'edit', 'delete'] |
|
133 |
_q_exports = ['', 'edit', 'delete', 'export']
|
|
127 | 134 |
do_not_call_in_templates = True |
128 | 135 | |
129 | 136 |
def __init__(self, component): |
... | ... | |
222 | 229 |
self.datasource.remove_self() |
223 | 230 |
return redirect('..') |
224 | 231 | |
232 |
def export(self): |
|
233 |
x = self.datasource.export_to_xml(include_id=True) |
|
234 |
misc.indent_xml(x) |
|
235 |
response = get_response() |
|
236 |
response.set_content_type('application/x-wcs-datasource') |
|
237 |
response.set_header( |
|
238 |
'content-disposition', |
|
239 |
'attachment; filename=datasource-%s.wcs' % self.datasource.slug) |
|
240 |
return '<?xml version="1.0"?>\n' + force_str(ET.tostring(x)) |
|
241 | ||
225 | 242 | |
226 | 243 |
class NamedDataSourcesDirectory(Directory): |
227 |
_q_exports = ['', 'new'] |
|
244 |
_q_exports = ['', 'new', ('import', 'p_import')]
|
|
228 | 245 | |
229 | 246 |
def _q_traverse(self, path): |
230 | 247 |
get_response().breadcrumb.append( ('data-sources/', _('Data Sources')) ) |
... | ... | |
239 | 256 |
r += htmltext('<div id="appbar">') |
240 | 257 |
r += htmltext('<h2>%s</h2>') % _('Data Sources') |
241 | 258 |
r += htmltext('<span class="actions">') |
259 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
|
242 | 260 |
r += htmltext('<a class="new-item" href="new">%s</a>') % _('New Data Source') |
243 | 261 |
r += htmltext('</span>') |
244 | 262 |
r += htmltext('</div>') |
... | ... | |
275 | 293 | |
276 | 294 |
def _q_lookup(self, component): |
277 | 295 |
return NamedDataSourcePage(component) |
296 | ||
297 |
def p_import(self): |
|
298 |
form = Form(enctype='multipart/form-data') |
|
299 |
import_title = _('Import Data Source') |
|
300 | ||
301 |
form.add(FileWidget, 'file', title=_('File'), required=True) |
|
302 |
form.add_submit('submit', import_title) |
|
303 |
form.add_submit('cancel', _('Cancel')) |
|
304 | ||
305 |
if form.get_submit() == 'cancel': |
|
306 |
return redirect('.') |
|
307 | ||
308 |
if form.is_submitted() and not form.has_errors(): |
|
309 |
try: |
|
310 |
return self.import_submit(form) |
|
311 |
except ValueError: |
|
312 |
pass |
|
313 | ||
314 |
get_response().breadcrumb.append(('import', _('Import'))) |
|
315 |
html_top('datasources', title=import_title) |
|
316 |
r = TemplateIO(html=True) |
|
317 |
r += htmltext('<h2>%s</h2>') % import_title |
|
318 |
r += htmltext('<p>%s</p>') % _( |
|
319 |
'You can install a new data source by uploading a file ' |
|
320 |
'or by pointing to the data source URL.') |
|
321 |
r += form.render() |
|
322 |
return r.getvalue() |
|
323 | ||
324 |
def import_submit(self, form): |
|
325 |
self.imported_datasource = None |
|
326 |
fp = form.get_widget('file').parse().fp |
|
327 | ||
328 |
error = False |
|
329 |
try: |
|
330 |
datasource = NamedDataSource.import_from_xml(fp) |
|
331 |
get_session().message = ( |
|
332 |
'info', _('This datasource has been successfully imported.')) |
|
333 |
except ValueError: |
|
334 |
error = True |
|
335 | ||
336 |
if error: |
|
337 |
form.set_error('file', _('Invalid File')) |
|
338 |
raise ValueError() |
|
339 | ||
340 |
self.imported_datasource = datasource |
|
341 |
datasource.slug = None # a new one will be set in .store() |
|
342 |
datasource.store() |
|
343 |
return redirect('%s/' % datasource.id) |
wcs/data_sources.py | ||
---|---|---|
264 | 264 | |
265 | 265 |
class NamedDataSource(XmlStorableObject): |
266 | 266 |
_names = 'datasources' |
267 |
_indexes = ['slug'] |
|
267 | 268 |
_xml_tagname = 'datasources' |
268 | 269 | |
269 | 270 |
name = None |
wcs/templates/wcs/backoffice/data-sources.html | ||
---|---|---|
4 | 4 |
<div id="appbar"> |
5 | 5 |
<h2>{% trans "Data Source" %} - {{ datasource.name }}</h2> |
6 | 6 |
<span class="actions"> |
7 |
<a href="export">{% trans "Export" %}</a> |
|
7 | 8 |
<a href="delete" rel="popup">{% trans "Delete" %}</a> |
8 | 9 |
<a href="edit">{% trans "Edit" %}</a> |
9 | 10 |
</span> |
10 |
- |