Projet

Général

Profil

0001-datasource-import-export-13722.patch

Lauréline Guérin, 26 juin 2020 14:40

Télécharger (8,45 ko)

Voir les différences:

Subject: [PATCH] datasource: import/export (#13722)

 tests/test_admin_pages.py                     | 57 ++++++++++++++
 tests/test_datasource.py                      |  1 +
 wcs/admin/data_sources.py                     | 74 ++++++++++++++++++-
 wcs/data_sources.py                           |  1 +
 .../wcs/backoffice/data-sources.html          |  1 +
 5 files changed, 130 insertions(+), 4 deletions(-)
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
-