forms.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. # pylint: disable=C,R,W
  2. """Contains the logic to create cohesive forms on the explore view"""
  3. from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
  4. from flask_appbuilder.forms import DynamicForm
  5. from flask_babel import lazy_gettext as _
  6. from flask_wtf.file import FileAllowed, FileField, FileRequired
  7. from wtforms import (
  8. BooleanField, Field, IntegerField, SelectField, StringField)
  9. from wtforms.ext.sqlalchemy.fields import QuerySelectField
  10. from wtforms.validators import DataRequired, NumberRange, Optional
  11. from superset import app, db, security_manager
  12. from superset.models import core as models
  13. config = app.config
  14. class CommaSeparatedListField(Field):
  15. widget = BS3TextFieldWidget()
  16. def _value(self):
  17. if self.data:
  18. return u', '.join(self.data)
  19. else:
  20. return u''
  21. def process_formdata(self, valuelist):
  22. if valuelist:
  23. self.data = [x.strip() for x in valuelist[0].split(',')]
  24. else:
  25. self.data = []
  26. def filter_not_empty_values(value):
  27. """Returns a list of non empty values or None"""
  28. if not value:
  29. return None
  30. data = [x for x in value if x]
  31. if not data:
  32. return None
  33. return data
  34. class CsvToDatabaseForm(DynamicForm):
  35. # pylint: disable=E0211
  36. def csv_allowed_dbs():
  37. csv_allowed_dbs = []
  38. csv_enabled_dbs = db.session.query(
  39. models.Database).filter_by(
  40. allow_csv_upload=True).all()
  41. for csv_enabled_db in csv_enabled_dbs:
  42. if CsvToDatabaseForm.at_least_one_schema_is_allowed(csv_enabled_db):
  43. csv_allowed_dbs.append(csv_enabled_db)
  44. return csv_allowed_dbs
  45. @staticmethod
  46. def at_least_one_schema_is_allowed(database):
  47. """
  48. If the user has access to the database or all datasource
  49. 1. if schemas_allowed_for_csv_upload is empty
  50. a) if database does not support schema
  51. user is able to upload csv without specifying schema name
  52. b) if database supports schema
  53. user is able to upload csv to any schema
  54. 2. if schemas_allowed_for_csv_upload is not empty
  55. a) if database does not support schema
  56. This situation is impossible and upload will fail
  57. b) if database supports schema
  58. user is able to upload to schema in schemas_allowed_for_csv_upload
  59. elif the user does not access to the database or all datasource
  60. 1. if schemas_allowed_for_csv_upload is empty
  61. a) if database does not support schema
  62. user is unable to upload csv
  63. b) if database supports schema
  64. user is unable to upload csv
  65. 2. if schemas_allowed_for_csv_upload is not empty
  66. a) if database does not support schema
  67. This situation is impossible and user is unable to upload csv
  68. b) if database supports schema
  69. user is able to upload to schema in schemas_allowed_for_csv_upload
  70. """
  71. if (security_manager.database_access(database) or
  72. security_manager.all_datasource_access()):
  73. return True
  74. schemas = database.get_schema_access_for_csv_upload()
  75. if (schemas and
  76. security_manager.schemas_accessible_by_user(
  77. database, schemas, False)):
  78. return True
  79. return False
  80. name = StringField(
  81. _('Table Name'),
  82. description=_('Name of table to be created from csv data.'),
  83. validators=[DataRequired()],
  84. widget=BS3TextFieldWidget())
  85. csv_file = FileField(
  86. _('CSV File'),
  87. description=_('Select a CSV file to be uploaded to a database.'),
  88. validators=[
  89. FileRequired(), FileAllowed(['csv'], _('CSV Files Only!'))])
  90. con = QuerySelectField(
  91. _('Database'),
  92. query_factory=csv_allowed_dbs,
  93. get_pk=lambda a: a.id, get_label=lambda a: a.database_name)
  94. schema = StringField(
  95. _('Schema'),
  96. description=_('Specify a schema (if database flavor supports this).'),
  97. validators=[Optional()],
  98. widget=BS3TextFieldWidget(),
  99. filters=[lambda x: x or None])
  100. sep = StringField(
  101. _('Delimiter'),
  102. description=_('Delimiter used by CSV file (for whitespace use \s+).'),
  103. validators=[DataRequired()],
  104. widget=BS3TextFieldWidget())
  105. if_exists = SelectField(
  106. _('Table Exists'),
  107. description=_(
  108. 'If table exists do one of the following: '
  109. 'Fail (do nothing), Replace (drop and recreate table) '
  110. 'or Append (insert data).'),
  111. choices=[
  112. ('fail', _('Fail')), ('replace', _('Replace')),
  113. ('append', _('Append'))],
  114. validators=[DataRequired()])
  115. header = IntegerField(
  116. _('Header Row'),
  117. description=_(
  118. 'Row containing the headers to use as '
  119. 'column names (0 is first line of data). '
  120. 'Leave empty if there is no header row.'),
  121. validators=[Optional()],
  122. widget=BS3TextFieldWidget(),
  123. filters=[lambda x: x or None])
  124. index_col = IntegerField(
  125. _('Index Column'),
  126. description=_(
  127. 'Column to use as the row labels of the '
  128. 'dataframe. Leave empty if no index column.'),
  129. validators=[Optional(), NumberRange(0, 1E+20)],
  130. widget=BS3TextFieldWidget(),
  131. filters=[lambda x: x or None])
  132. mangle_dupe_cols = BooleanField(
  133. _('Mangle Duplicate Columns'),
  134. description=_('Specify duplicate columns as "X.0, X.1".'))
  135. skipinitialspace = BooleanField(
  136. _('Skip Initial Space'),
  137. description=_('Skip spaces after delimiter.'))
  138. skiprows = IntegerField(
  139. _('Skip Rows'),
  140. description=_('Number of rows to skip at start of file.'),
  141. validators=[Optional(), NumberRange(0, 1E+20)],
  142. widget=BS3TextFieldWidget(),
  143. filters=[lambda x: x or None])
  144. nrows = IntegerField(
  145. _('Rows to Read'),
  146. description=_('Number of rows of file to read.'),
  147. validators=[Optional(), NumberRange(0, 1E+20)],
  148. widget=BS3TextFieldWidget(),
  149. filters=[lambda x: x or None])
  150. skip_blank_lines = BooleanField(
  151. _('Skip Blank Lines'),
  152. description=_(
  153. 'Skip blank lines rather than interpreting them '
  154. 'as NaN values.'))
  155. parse_dates = CommaSeparatedListField(
  156. _('Parse Dates'),
  157. description=_(
  158. 'A comma separated list of columns that should be '
  159. 'parsed as dates.'),
  160. filters=[filter_not_empty_values])
  161. infer_datetime_format = BooleanField(
  162. _('Infer Datetime Format'),
  163. description=_(
  164. 'Use Pandas to interpret the datetime format '
  165. 'automatically.'))
  166. decimal = StringField(
  167. _('Decimal Character'),
  168. description=_('Character to interpret as decimal point.'),
  169. validators=[Optional()],
  170. widget=BS3TextFieldWidget(),
  171. filters=[lambda x: x or '.'])
  172. index = BooleanField(
  173. _('Dataframe Index'),
  174. description=_('Write dataframe index as a column.'))
  175. index_label = StringField(
  176. _('Column Label(s)'),
  177. description=_(
  178. 'Column label for index column(s). If None is given '
  179. 'and Dataframe Index is True, Index Names are used.'),
  180. validators=[Optional()],
  181. widget=BS3TextFieldWidget(),
  182. filters=[lambda x: x or None])