From d9c2e57c29adea313bf7d60370ad79a19237e4c3 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 23 Mar 2018 22:16:02 +0100 Subject: [PATCH] forms: make csv import parse dates accepts a list of columns (#4639) Instead of a boolean which has way less chances to work. While at it add a proper label for the "con" field. Fixes #4637 --- superset/forms.py | 36 +++++++++++++++++++++++++++++++++--- superset/views/core.py | 1 - tests/form_tests.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 tests/form_tests.py diff --git a/superset/forms.py b/superset/forms.py index eb8aba889a086..e846d57ca9991 100644 --- a/superset/forms.py +++ b/superset/forms.py @@ -10,7 +10,7 @@ from flask_babel import lazy_gettext as _ from flask_wtf.file import FileAllowed, FileField, FileRequired from wtforms import ( - BooleanField, IntegerField, SelectField, StringField) + BooleanField, Field, IntegerField, SelectField, StringField) from wtforms.ext.sqlalchemy.fields import QuerySelectField from wtforms.validators import DataRequired, NumberRange, Optional @@ -20,6 +20,32 @@ config = app.config +class CommaSeparatedListField(Field): + widget = BS3TextFieldWidget() + + def _value(self): + if self.data: + return u', '.join(self.data) + else: + return u'' + + def process_formdata(self, valuelist): + if valuelist: + self.data = [x.strip() for x in valuelist[0].split(',')] + else: + self.data = [] + + +def filter_not_empty_values(value): + """Returns a list of non empty values or None""" + if not value: + return None + data = [x for x in value if x] + if not data: + return None + return data + + class CsvToDatabaseForm(DynamicForm): # pylint: disable=E0211 def all_db_items(): @@ -36,6 +62,7 @@ def all_db_items(): validators=[ FileRequired(), FileAllowed(['csv'], _('CSV Files Only!'))]) con = QuerySelectField( + _('Database'), query_factory=all_db_items, get_pk=lambda a: a.id, get_label=lambda a: a.database_name) sep = StringField( @@ -99,9 +126,12 @@ def all_db_items(): description=_( 'Skip blank lines rather than interpreting them ' 'as NaN values.')) - parse_dates = BooleanField( + parse_dates = CommaSeparatedListField( _('Parse Dates'), - description=_('Parse date values.')) + description=_( + 'A comma separated list of columns that should be ' + 'parsed as dates.'), + filters=[filter_not_empty_values]) infer_datetime_format = BooleanField( _('Infer Datetime Format'), description=_( diff --git a/superset/views/core.py b/superset/views/core.py index 723e8ccfb7a75..a080a4b8ebe3a 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -339,7 +339,6 @@ def form_get(self, form): form.mangle_dupe_cols.data = True form.skipinitialspace.data = False form.skip_blank_lines.data = True - form.parse_dates.data = True form.infer_datetime_format.data = True form.decimal.data = '.' form.if_exists.data = 'append' diff --git a/tests/form_tests.py b/tests/form_tests.py new file mode 100644 index 0000000000000..82178a213eb09 --- /dev/null +++ b/tests/form_tests.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from tests.base_tests import SupersetTestCase +from wtforms.form import Form + +from superset.forms import ( + CommaSeparatedListField, filter_not_empty_values) + + +class FormTestCase(SupersetTestCase): + + def test_comma_separated_list_field(self): + field = CommaSeparatedListField().bind(Form(), 'foo') + field.process_formdata([u'']) + self.assertEqual(field.data, [u'']) + + field.process_formdata(['a,comma,separated,list']) + self.assertEqual(field.data, [u'a', u'comma', u'separated', u'list']) + + def test_filter_not_empty_values(self): + self.assertEqual(filter_not_empty_values(None), None) + self.assertEqual(filter_not_empty_values([]), None) + self.assertEqual(filter_not_empty_values(['']), None) + self.assertEqual(filter_not_empty_values(['hi']), ['hi'])