forms.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. # Licensed to the Apache Software Foundation (ASF) under one
  2. # or more contributor license agreements. See the NOTICE file
  3. # distributed with this work for additional information
  4. # regarding copyright ownership. The ASF licenses this file
  5. # to you under the Apache License, Version 2.0 (the
  6. # "License"); you may not use this file except in compliance
  7. # with the License. You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing,
  12. # software distributed under the License is distributed on an
  13. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. # KIND, either express or implied. See the License for the
  15. # specific language governing permissions and limitations
  16. # under the License.
  17. """Contains the logic to create cohesive forms on the explore view"""
  18. from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
  19. from flask_appbuilder.forms import DynamicForm
  20. from flask_babel import lazy_gettext as _
  21. from flask_wtf.file import FileAllowed, FileField, FileRequired
  22. from wtforms import BooleanField, IntegerField, SelectField, StringField
  23. from wtforms.ext.sqlalchemy.fields import QuerySelectField
  24. from wtforms.validators import DataRequired, Length, NumberRange, Optional
  25. from superset import app, db, security_manager
  26. from superset.forms import CommaSeparatedListField, filter_not_empty_values
  27. from superset.models import core as models
  28. config = app.config
  29. class CsvToDatabaseForm(DynamicForm):
  30. # pylint: disable=E0211
  31. def csv_allowed_dbs(): # type: ignore
  32. csv_allowed_dbs = []
  33. csv_enabled_dbs = (
  34. db.session.query(models.Database).filter_by(allow_csv_upload=True).all()
  35. )
  36. for csv_enabled_db in csv_enabled_dbs:
  37. if CsvToDatabaseForm.at_least_one_schema_is_allowed(csv_enabled_db):
  38. csv_allowed_dbs.append(csv_enabled_db)
  39. return csv_allowed_dbs
  40. @staticmethod
  41. def at_least_one_schema_is_allowed(database):
  42. """
  43. If the user has access to the database or all datasource
  44. 1. if schemas_allowed_for_csv_upload is empty
  45. a) if database does not support schema
  46. user is able to upload csv without specifying schema name
  47. b) if database supports schema
  48. user is able to upload csv to any schema
  49. 2. if schemas_allowed_for_csv_upload is not empty
  50. a) if database does not support schema
  51. This situation is impossible and upload will fail
  52. b) if database supports schema
  53. user is able to upload to schema in schemas_allowed_for_csv_upload
  54. elif the user does not access to the database or all datasource
  55. 1. if schemas_allowed_for_csv_upload is empty
  56. a) if database does not support schema
  57. user is unable to upload csv
  58. b) if database supports schema
  59. user is unable to upload csv
  60. 2. if schemas_allowed_for_csv_upload is not empty
  61. a) if database does not support schema
  62. This situation is impossible and user is unable to upload csv
  63. b) if database supports schema
  64. user is able to upload to schema in schemas_allowed_for_csv_upload
  65. """
  66. if (
  67. security_manager.database_access(database)
  68. or security_manager.all_datasource_access()
  69. ):
  70. return True
  71. schemas = database.get_schema_access_for_csv_upload()
  72. if schemas and security_manager.schemas_accessible_by_user(
  73. database, schemas, False
  74. ):
  75. return True
  76. return False
  77. name = StringField(
  78. _("Table Name"),
  79. description=_("Name of table to be created from csv data."),
  80. validators=[DataRequired()],
  81. widget=BS3TextFieldWidget(),
  82. )
  83. csv_file = FileField(
  84. _("CSV File"),
  85. description=_("Select a CSV file to be uploaded to a database."),
  86. validators=[
  87. FileRequired(),
  88. FileAllowed(
  89. config["ALLOWED_EXTENSIONS"],
  90. _(
  91. "Only the following file extensions are allowed: "
  92. "%(allowed_extensions)s",
  93. allowed_extensions=", ".join(config["ALLOWED_EXTENSIONS"]),
  94. ),
  95. ),
  96. ],
  97. )
  98. con = QuerySelectField(
  99. _("Database"),
  100. query_factory=csv_allowed_dbs,
  101. get_pk=lambda a: a.id,
  102. get_label=lambda a: a.database_name,
  103. )
  104. schema = StringField(
  105. _("Schema"),
  106. description=_("Specify a schema (if database flavor supports this)."),
  107. validators=[Optional()],
  108. widget=BS3TextFieldWidget(),
  109. )
  110. sep = StringField(
  111. _("Delimiter"),
  112. description=_("Delimiter used by CSV file (for whitespace use \\s+)."),
  113. validators=[DataRequired()],
  114. widget=BS3TextFieldWidget(),
  115. )
  116. if_exists = SelectField(
  117. _("Table Exists"),
  118. description=_(
  119. "If table exists do one of the following: "
  120. "Fail (do nothing), Replace (drop and recreate table) "
  121. "or Append (insert data)."
  122. ),
  123. choices=[
  124. ("fail", _("Fail")),
  125. ("replace", _("Replace")),
  126. ("append", _("Append")),
  127. ],
  128. validators=[DataRequired()],
  129. )
  130. header = IntegerField(
  131. _("Header Row"),
  132. description=_(
  133. "Row containing the headers to use as "
  134. "column names (0 is first line of data). "
  135. "Leave empty if there is no header row."
  136. ),
  137. validators=[Optional(), NumberRange(min=0)],
  138. widget=BS3TextFieldWidget(),
  139. )
  140. index_col = IntegerField(
  141. _("Index Column"),
  142. description=_(
  143. "Column to use as the row labels of the "
  144. "dataframe. Leave empty if no index column."
  145. ),
  146. validators=[Optional(), NumberRange(min=0)],
  147. widget=BS3TextFieldWidget(),
  148. )
  149. mangle_dupe_cols = BooleanField(
  150. _("Mangle Duplicate Columns"),
  151. description=_('Specify duplicate columns as "X.0, X.1".'),
  152. )
  153. skipinitialspace = BooleanField(
  154. _("Skip Initial Space"), description=_("Skip spaces after delimiter.")
  155. )
  156. skiprows = IntegerField(
  157. _("Skip Rows"),
  158. description=_("Number of rows to skip at start of file."),
  159. validators=[Optional(), NumberRange(min=0)],
  160. widget=BS3TextFieldWidget(),
  161. )
  162. nrows = IntegerField(
  163. _("Rows to Read"),
  164. description=_("Number of rows of file to read."),
  165. validators=[Optional(), NumberRange(min=0)],
  166. widget=BS3TextFieldWidget(),
  167. )
  168. skip_blank_lines = BooleanField(
  169. _("Skip Blank Lines"),
  170. description=_(
  171. "Skip blank lines rather than interpreting them " "as NaN values."
  172. ),
  173. )
  174. parse_dates = CommaSeparatedListField(
  175. _("Parse Dates"),
  176. description=_(
  177. "A comma separated list of columns that should be " "parsed as dates."
  178. ),
  179. filters=[filter_not_empty_values],
  180. )
  181. infer_datetime_format = BooleanField(
  182. _("Infer Datetime Format"),
  183. description=_("Use Pandas to interpret the datetime format " "automatically."),
  184. )
  185. decimal = StringField(
  186. _("Decimal Character"),
  187. default=".",
  188. description=_("Character to interpret as decimal point."),
  189. validators=[Optional(), Length(min=1, max=1)],
  190. widget=BS3TextFieldWidget(),
  191. )
  192. index = BooleanField(
  193. _("Dataframe Index"), description=_("Write dataframe index as a column.")
  194. )
  195. index_label = StringField(
  196. _("Column Label(s)"),
  197. description=_(
  198. "Column label for index column(s). If None is given "
  199. "and Dataframe Index is True, Index Names are used."
  200. ),
  201. validators=[Optional()],
  202. widget=BS3TextFieldWidget(),
  203. )