flask-appbuilder 3.2.1rc1__py3-none-any.whl → 5.0.2rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. flask_appbuilder/__init__.py +2 -3
  2. flask_appbuilder/_compat.py +0 -1
  3. flask_appbuilder/actions.py +14 -14
  4. flask_appbuilder/api/__init__.py +741 -527
  5. flask_appbuilder/api/convert.py +104 -98
  6. flask_appbuilder/api/manager.py +14 -8
  7. flask_appbuilder/api/schemas.py +12 -1
  8. flask_appbuilder/babel/manager.py +12 -16
  9. flask_appbuilder/base.py +353 -280
  10. flask_appbuilder/basemanager.py +1 -1
  11. flask_appbuilder/baseviews.py +241 -164
  12. flask_appbuilder/charts/jsontools.py +10 -10
  13. flask_appbuilder/charts/views.py +56 -60
  14. flask_appbuilder/cli.py +115 -70
  15. flask_appbuilder/const.py +52 -52
  16. flask_appbuilder/exceptions.py +67 -5
  17. flask_appbuilder/fields.py +32 -23
  18. flask_appbuilder/fieldwidgets.py +34 -27
  19. flask_appbuilder/filemanager.py +33 -45
  20. flask_appbuilder/filters.py +11 -13
  21. flask_appbuilder/forms.py +31 -35
  22. flask_appbuilder/hooks.py +90 -0
  23. flask_appbuilder/menu.py +35 -10
  24. flask_appbuilder/models/base.py +47 -57
  25. flask_appbuilder/models/decorators.py +13 -13
  26. flask_appbuilder/models/filters.py +42 -38
  27. flask_appbuilder/models/generic/__init__.py +29 -29
  28. flask_appbuilder/models/generic/filters.py +11 -3
  29. flask_appbuilder/models/generic/interface.py +1 -3
  30. flask_appbuilder/models/group.py +37 -39
  31. flask_appbuilder/models/mixins.py +22 -18
  32. flask_appbuilder/models/sqla/__init__.py +19 -72
  33. flask_appbuilder/models/sqla/base.py +24 -0
  34. flask_appbuilder/models/sqla/base_legacy.py +132 -0
  35. flask_appbuilder/models/sqla/filters.py +132 -19
  36. flask_appbuilder/models/sqla/interface.py +390 -276
  37. flask_appbuilder/security/api.py +31 -35
  38. flask_appbuilder/security/decorators.py +181 -83
  39. flask_appbuilder/security/forms.py +20 -31
  40. flask_appbuilder/security/manager.py +715 -489
  41. flask_appbuilder/security/registerviews.py +29 -112
  42. flask_appbuilder/security/schemas.py +43 -0
  43. flask_appbuilder/security/sqla/apis/__init__.py +8 -0
  44. flask_appbuilder/security/sqla/apis/group/__init__.py +1 -0
  45. flask_appbuilder/security/sqla/apis/group/api.py +227 -0
  46. flask_appbuilder/security/sqla/apis/group/schema.py +73 -0
  47. flask_appbuilder/security/sqla/apis/permission/__init__.py +1 -0
  48. flask_appbuilder/security/sqla/apis/permission/api.py +19 -0
  49. flask_appbuilder/security/sqla/apis/permission_view_menu/__init__.py +1 -0
  50. flask_appbuilder/security/sqla/apis/permission_view_menu/api.py +16 -0
  51. flask_appbuilder/security/sqla/apis/role/__init__.py +1 -0
  52. flask_appbuilder/security/sqla/apis/role/api.py +306 -0
  53. flask_appbuilder/security/sqla/apis/role/schema.py +27 -0
  54. flask_appbuilder/security/sqla/apis/user/__init__.py +1 -0
  55. flask_appbuilder/security/sqla/apis/user/api.py +292 -0
  56. flask_appbuilder/security/sqla/apis/user/schema.py +97 -0
  57. flask_appbuilder/security/sqla/apis/user/validator.py +27 -0
  58. flask_appbuilder/security/sqla/apis/view_menu/__init__.py +1 -0
  59. flask_appbuilder/security/sqla/apis/view_menu/api.py +18 -0
  60. flask_appbuilder/security/sqla/manager.py +421 -203
  61. flask_appbuilder/security/sqla/models.py +192 -57
  62. flask_appbuilder/security/utils.py +9 -0
  63. flask_appbuilder/security/views.py +232 -229
  64. flask_appbuilder/static/.DS_Store +0 -0
  65. flask_appbuilder/static/appbuilder/css/ab.css +20 -12
  66. flask_appbuilder/static/appbuilder/css/bootstrap-datepicker/bootstrap-datepicker3.min.css +7 -0
  67. flask_appbuilder/static/appbuilder/css/bootstrap.min.css.map +1 -0
  68. flask_appbuilder/static/appbuilder/css/flags/flags16.css +249 -245
  69. flask_appbuilder/static/appbuilder/css/fontawesome/all.min.css +6 -0
  70. flask_appbuilder/static/appbuilder/css/fontawesome/brands.min.css +6 -0
  71. flask_appbuilder/static/appbuilder/css/fontawesome/fontawesome.min.css +6 -0
  72. flask_appbuilder/static/appbuilder/css/fontawesome/regular.min.css +6 -0
  73. flask_appbuilder/static/appbuilder/css/fontawesome/solid.min.css +6 -0
  74. flask_appbuilder/static/appbuilder/css/fontawesome/svg-with-js.min.css +6 -0
  75. flask_appbuilder/static/appbuilder/css/fontawesome/v4-font-face.min.css +6 -0
  76. flask_appbuilder/static/appbuilder/css/fontawesome/v4-shims.min.css +6 -0
  77. flask_appbuilder/static/appbuilder/css/fontawesome/v5-font-face.min.css +6 -0
  78. flask_appbuilder/static/appbuilder/css/images/flags16.png +0 -0
  79. flask_appbuilder/static/appbuilder/css/select2/select2-bootstrap.min.css +7 -0
  80. flask_appbuilder/static/appbuilder/css/select2/select2.min.css +1 -0
  81. flask_appbuilder/static/appbuilder/css/swagger/swagger-ui.css +3 -0
  82. flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.ttf +0 -0
  83. flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.woff2 +0 -0
  84. flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.ttf +0 -0
  85. flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.woff2 +0 -0
  86. flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.ttf +0 -0
  87. flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.woff2 +0 -0
  88. flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.ttf +0 -0
  89. flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.woff2 +0 -0
  90. flask_appbuilder/static/appbuilder/js/ab.js +33 -23
  91. flask_appbuilder/static/appbuilder/js/ab_filters.js +91 -84
  92. flask_appbuilder/static/appbuilder/js/bootstrap-datepicker/bootstrap-datepicker.min.js +8 -0
  93. flask_appbuilder/static/appbuilder/js/jquery-latest.js +2 -2
  94. flask_appbuilder/static/appbuilder/js/select2/select2.min.js +2 -0
  95. flask_appbuilder/static/appbuilder/js/swagger-ui-bundle.js +3 -0
  96. flask_appbuilder/templates/appbuilder/baselib.html +9 -3
  97. flask_appbuilder/templates/appbuilder/general/lib.html +60 -34
  98. flask_appbuilder/templates/appbuilder/general/model/edit.html +1 -1
  99. flask_appbuilder/templates/appbuilder/general/model/edit_cascade.html +1 -1
  100. flask_appbuilder/templates/appbuilder/general/model/search.html +3 -2
  101. flask_appbuilder/templates/appbuilder/general/model/show.html +1 -1
  102. flask_appbuilder/templates/appbuilder/general/model/show_cascade.html +1 -1
  103. flask_appbuilder/templates/appbuilder/general/security/login_db.html +7 -7
  104. flask_appbuilder/templates/appbuilder/general/security/login_ldap.html +5 -5
  105. flask_appbuilder/templates/appbuilder/general/security/login_oauth.html +24 -49
  106. flask_appbuilder/templates/appbuilder/general/widgets/base_list.html +2 -1
  107. flask_appbuilder/templates/appbuilder/general/widgets/chart.html +4 -2
  108. flask_appbuilder/templates/appbuilder/general/widgets/direct_chart.html +4 -3
  109. flask_appbuilder/templates/appbuilder/general/widgets/multiple_chart.html +3 -2
  110. flask_appbuilder/templates/appbuilder/general/widgets/search.html +11 -10
  111. flask_appbuilder/templates/appbuilder/init.html +37 -43
  112. flask_appbuilder/templates/appbuilder/navbar_menu.html +1 -1
  113. flask_appbuilder/templates/appbuilder/navbar_right.html +2 -2
  114. flask_appbuilder/templates/appbuilder/swagger/swagger.html +22 -19
  115. flask_appbuilder/translations/de/LC_MESSAGES/messages.mo +0 -0
  116. flask_appbuilder/translations/de/LC_MESSAGES/messages.po +305 -161
  117. flask_appbuilder/translations/fa/LC_MESSAGES/messages.mo +0 -0
  118. flask_appbuilder/translations/fa/LC_MESSAGES/messages.po +802 -0
  119. flask_appbuilder/translations/fr/LC_MESSAGES/messages.po +461 -319
  120. flask_appbuilder/translations/pt_BR/LC_MESSAGES/messages.po +650 -650
  121. flask_appbuilder/translations/ru/LC_MESSAGES/messages.po +1 -1
  122. flask_appbuilder/translations/sl/LC_MESSAGES/messages.mo +0 -0
  123. flask_appbuilder/translations/sl/LC_MESSAGES/messages.po +690 -0
  124. flask_appbuilder/translations/tr/LC_MESSAGES/messages.mo +0 -0
  125. flask_appbuilder/translations/tr/LC_MESSAGES/messages.po +1015 -0
  126. flask_appbuilder/upload.py +20 -22
  127. flask_appbuilder/urltools.py +39 -19
  128. flask_appbuilder/utils/base.py +76 -0
  129. flask_appbuilder/utils/legacy.py +33 -0
  130. flask_appbuilder/utils/limit.py +20 -0
  131. flask_appbuilder/validators.py +73 -14
  132. flask_appbuilder/views.py +75 -424
  133. flask_appbuilder/widgets.py +50 -51
  134. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/METADATA +36 -76
  135. flask_appbuilder-5.0.2rc1.dist-info/RECORD +240 -0
  136. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/WHEEL +1 -1
  137. flask_appbuilder-5.0.2rc1.dist-info/entry_points.txt +2 -0
  138. Flask_AppBuilder-3.2.1rc1.dist-info/RECORD +0 -270
  139. Flask_AppBuilder-3.2.1rc1.dist-info/entry_points.txt +0 -6
  140. flask_appbuilder/console.py +0 -426
  141. flask_appbuilder/models/mongoengine/__init__.py +0 -0
  142. flask_appbuilder/models/mongoengine/fields.py +0 -65
  143. flask_appbuilder/models/mongoengine/filters.py +0 -145
  144. flask_appbuilder/models/mongoengine/interface.py +0 -328
  145. flask_appbuilder/security/mongoengine/__init__.py +0 -0
  146. flask_appbuilder/security/mongoengine/manager.py +0 -402
  147. flask_appbuilder/security/mongoengine/models.py +0 -120
  148. flask_appbuilder/static/appbuilder/css/font-awesome.min.css +0 -4
  149. flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.css +0 -9
  150. flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.js +0 -28
  151. flask_appbuilder/static/appbuilder/fonts/FontAwesome.otf +0 -0
  152. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.eot +0 -0
  153. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.svg +0 -2671
  154. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.ttf +0 -0
  155. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff +0 -0
  156. flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff2 +0 -0
  157. flask_appbuilder/static/appbuilder/img/aol.png +0 -0
  158. flask_appbuilder/static/appbuilder/img/flags/flags16.png +0 -0
  159. flask_appbuilder/static/appbuilder/img/flickr.png +0 -0
  160. flask_appbuilder/static/appbuilder/img/google.png +0 -0
  161. flask_appbuilder/static/appbuilder/img/myopenid.png +0 -0
  162. flask_appbuilder/static/appbuilder/img/yahoo.png +0 -0
  163. flask_appbuilder/static/appbuilder/js/_google_charts.js +0 -39
  164. flask_appbuilder/static/appbuilder/js/html5shiv.js +0 -8
  165. flask_appbuilder/static/appbuilder/js/respond.min.js +0 -6
  166. flask_appbuilder/static/appbuilder/select2/select2-spinner.gif +0 -0
  167. flask_appbuilder/static/appbuilder/select2/select2.css +0 -1205
  168. flask_appbuilder/static/appbuilder/select2/select2.js +0 -23
  169. flask_appbuilder/static/appbuilder/select2/select2.png +0 -0
  170. flask_appbuilder/static/appbuilder/select2/select2x2.png +0 -0
  171. flask_appbuilder/templates/appbuilder/general/security/login_oid.html +0 -129
  172. flask_appbuilder/templates/appbuilder/general/security/resetpassword.html +0 -29
  173. flask_appbuilder/tests/__init__.py +0 -0
  174. flask_appbuilder/tests/__pycache__/__init__.cpython-36.pyc +0 -0
  175. flask_appbuilder/tests/__pycache__/__init__.cpython-37.pyc +0 -0
  176. flask_appbuilder/tests/__pycache__/_test_auth_ldap.cpython-37.pyc +0 -0
  177. flask_appbuilder/tests/__pycache__/_test_auth_oauth.cpython-37.pyc +0 -0
  178. flask_appbuilder/tests/__pycache__/_test_ldapsearch.cpython-36.pyc +0 -0
  179. flask_appbuilder/tests/__pycache__/_test_oauth_registration_role.cpython-36.pyc +0 -0
  180. flask_appbuilder/tests/__pycache__/base.cpython-36.pyc +0 -0
  181. flask_appbuilder/tests/__pycache__/base.cpython-37.pyc +0 -0
  182. flask_appbuilder/tests/__pycache__/config_api.cpython-36.pyc +0 -0
  183. flask_appbuilder/tests/__pycache__/config_api.cpython-37.pyc +0 -0
  184. flask_appbuilder/tests/__pycache__/const.cpython-36.pyc +0 -0
  185. flask_appbuilder/tests/__pycache__/const.cpython-37.pyc +0 -0
  186. flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-36.pyc +0 -0
  187. flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-37.pyc +0 -0
  188. flask_appbuilder/tests/__pycache__/test_api.cpython-36.pyc +0 -0
  189. flask_appbuilder/tests/__pycache__/test_api.cpython-37.pyc +0 -0
  190. flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-36.pyc +0 -0
  191. flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-37.pyc +0 -0
  192. flask_appbuilder/tests/__pycache__/test_menu.cpython-36.pyc +0 -0
  193. flask_appbuilder/tests/__pycache__/test_menu.cpython-37.pyc +0 -0
  194. flask_appbuilder/tests/__pycache__/test_mongoengine.cpython-36.pyc +0 -0
  195. flask_appbuilder/tests/__pycache__/test_mvc.cpython-36.pyc +0 -0
  196. flask_appbuilder/tests/__pycache__/test_mvc.cpython-37.pyc +0 -0
  197. flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-36.pyc +0 -0
  198. flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-37.pyc +0 -0
  199. flask_appbuilder/tests/_test_auth_ldap.py +0 -1045
  200. flask_appbuilder/tests/_test_auth_oauth.py +0 -419
  201. flask_appbuilder/tests/_test_ldapsearch.py +0 -135
  202. flask_appbuilder/tests/_test_oauth_registration_role.py +0 -59
  203. flask_appbuilder/tests/app.db +0 -0
  204. flask_appbuilder/tests/base.py +0 -90
  205. flask_appbuilder/tests/config_api.py +0 -21
  206. flask_appbuilder/tests/const.py +0 -9
  207. flask_appbuilder/tests/mongoengine/__init__.py +0 -0
  208. flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-36.pyc +0 -0
  209. flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-37.pyc +0 -0
  210. flask_appbuilder/tests/mongoengine/__pycache__/models.cpython-36.pyc +0 -0
  211. flask_appbuilder/tests/mongoengine/models.py +0 -41
  212. flask_appbuilder/tests/sqla/__init__.py +0 -0
  213. flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-36.pyc +0 -0
  214. flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-37.pyc +0 -0
  215. flask_appbuilder/tests/sqla/__pycache__/models.cpython-36.pyc +0 -0
  216. flask_appbuilder/tests/sqla/__pycache__/models.cpython-37.pyc +0 -0
  217. flask_appbuilder/tests/sqla/models.py +0 -340
  218. flask_appbuilder/tests/test_0_fixture.py +0 -39
  219. flask_appbuilder/tests/test_api.py +0 -2790
  220. flask_appbuilder/tests/test_fab_cli.py +0 -72
  221. flask_appbuilder/tests/test_menu.py +0 -122
  222. flask_appbuilder/tests/test_mongoengine.py +0 -572
  223. flask_appbuilder/tests/test_mvc.py +0 -1710
  224. flask_appbuilder/tests/test_sqlalchemy.py +0 -24
  225. flask_appbuilder/translations/__pycache__/__init__.cpython-36.pyc +0 -0
  226. flask_appbuilder/translations/es/LC_MESSAGES/messages.po~ +0 -582
  227. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/LICENSE +0 -0
  228. {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/top_level.txt +0 -0
@@ -16,10 +16,10 @@ log = logging.getLogger(__name__)
16
16
 
17
17
  def aggregate(label=""):
18
18
  """
19
- Use this decorator to set a label for your aggregation functions on charts.
19
+ Use this decorator to set a label for your aggregation functions on charts.
20
20
 
21
- :param label:
22
- The label to complement with the column
21
+ :param label:
22
+ The label to complement with the column
23
23
  """
24
24
 
25
25
  def wrap(f):
@@ -32,8 +32,8 @@ def aggregate(label=""):
32
32
  @aggregate(_("Count of"))
33
33
  def aggregate_count(items, col):
34
34
  """
35
- Function to use on Group by Charts.
36
- accepts a list and returns the count of the list's items
35
+ Function to use on Group by Charts.
36
+ accepts a list and returns the count of the list's items
37
37
  """
38
38
  return len(list(items))
39
39
 
@@ -41,8 +41,8 @@ def aggregate_count(items, col):
41
41
  @aggregate(_("Sum of"))
42
42
  def aggregate_sum(items, col):
43
43
  """
44
- Function to use on Group by Charts.
45
- accepts a list and returns the sum of the list's items
44
+ Function to use on Group by Charts.
45
+ accepts a list and returns the sum of the list's items
46
46
  """
47
47
  return sum(getattr(item, col) for item in items)
48
48
 
@@ -50,8 +50,8 @@ def aggregate_sum(items, col):
50
50
  @aggregate(_("Avg. of"))
51
51
  def aggregate_avg(items, col):
52
52
  """
53
- Function to use on Group by Charts.
54
- accepts a list and returns the average of the list's items
53
+ Function to use on Group by Charts.
54
+ accepts a list and returns the average of the list's items
55
55
  """
56
56
  try:
57
57
  return aggregate_sum(items, col) / aggregate_count(items, col)
@@ -70,12 +70,12 @@ class BaseGroupBy(object):
70
70
  self, column_name, name, aggregate_func=aggregate_count, aggregate_col=""
71
71
  ):
72
72
  """
73
- Constructor.
73
+ Constructor.
74
74
 
75
- :param column_name:
76
- Model field name
77
- :param name:
78
- The group by name
75
+ :param column_name:
76
+ Model field name
77
+ :param name:
78
+ The group by name
79
79
 
80
80
  """
81
81
  self.column_name = column_name
@@ -85,7 +85,7 @@ class BaseGroupBy(object):
85
85
 
86
86
  def apply(self, data):
87
87
  """
88
- Override this to implement you own new filters
88
+ Override this to implement you own new filters
89
89
  """
90
90
  pass
91
91
 
@@ -118,7 +118,7 @@ class GroupByCol(BaseGroupBy):
118
118
  },
119
119
  ]
120
120
  json_data["rows"] = []
121
- for (grouped, items) in groupby(data, self.get_group_col):
121
+ for grouped, items in groupby(data, self.get_group_col):
122
122
  aggregate_value = self.aggregate_func(items, self.aggregate_col)
123
123
  json_data["rows"].append(
124
124
  {
@@ -181,13 +181,13 @@ class GroupByDateMonth(BaseGroupBy):
181
181
 
182
182
  class BaseProcessData(object):
183
183
  """
184
- Base class to process data.
185
- It will group data by one or many columns or functions.
186
- The aggregation is made by an already defined function, or by a custom function
184
+ Base class to process data.
185
+ It will group data by one or many columns or functions.
186
+ The aggregation is made by an already defined function, or by a custom function
187
187
 
188
- :group_bys_cols: A list of columns or functions to group data.
189
- :aggr_by_cols: A list of tuples [(<AGGR FUNC>,'<COLNAME>'),...].
190
- :formatter_by_cols: A dict.
188
+ :group_bys_cols: A list of columns or functions to group data.
189
+ :aggr_by_cols: A list of tuples [(<AGGR FUNC>,'<COLNAME>'),...].
190
+ :formatter_by_cols: A dict.
191
191
  """
192
192
 
193
193
  group_bys_cols = None
@@ -255,7 +255,7 @@ class BaseProcessData(object):
255
255
  for group_col_data, i in zip(item[0], enumerate(item[0])):
256
256
  row[self.group_bys_cols[i]] = str(group_col_data)
257
257
  for col_data, i in zip(item[1:], enumerate(item[1:])):
258
- log.debug("{0},{1}".format(col_data, i))
258
+ log.debug("%s,%s", col_data, i)
259
259
  key = self.aggr_by_cols[i].__name__ + self.aggr_by_cols[i]
260
260
  if isinstance(col_data, datetime.date):
261
261
  row[key] = str(col_data)
@@ -266,18 +266,18 @@ class BaseProcessData(object):
266
266
 
267
267
  def to_json(self, data, labels=None):
268
268
  """
269
- Will return a dict with Google JSON structure for charts
269
+ Will return a dict with Google JSON structure for charts
270
270
 
271
- The Google structure::
271
+ The Google structure::
272
272
 
273
- {
274
- cols: [{id:<COL_NAME>, label:<LABEL FOR COL>, type: <COL TYPE>}, ...]
275
- rows: [{c: [{v: <COL VALUE}, ...], ... ]
276
- }
273
+ {
274
+ cols: [{id:<COL_NAME>, label:<LABEL FOR COL>, type: <COL TYPE>}, ...]
275
+ rows: [{c: [{v: <COL VALUE}, ...], ... ]
276
+ }
277
277
 
278
- :param data:
279
- :param labels: dict with labels to include on Google JSON strcut
280
- :return: dict with Google JSON structure
278
+ :param data:
279
+ :param labels: dict with labels to include on Google JSON strcut
280
+ :return: dict with Google JSON structure
281
281
  """
282
282
  labels = labels or dict()
283
283
  json_data = dict()
@@ -331,20 +331,18 @@ class DirectProcessData(BaseProcessData):
331
331
 
332
332
  class GroupByProcessData(BaseProcessData):
333
333
  """
334
- Groups by data by chosen columns (property group_bys_cols).
334
+ Groups by data by chosen columns (property group_bys_cols).
335
335
 
336
- :data: A list of objects
337
- :sort: boolean, if true python will sort the data
338
- :return: A List of lists with group column and aggregation
336
+ :data: A list of objects
337
+ :sort: boolean, if true python will sort the data
338
+ :return: A List of lists with group column and aggregation
339
339
  """
340
340
 
341
341
  def apply(self, data, sort=True):
342
342
  if sort:
343
343
  data = sorted(data, key=self.attrgetter(*self.group_bys_cols))
344
344
  result = []
345
- for (grouped, items) in groupby(
346
- data, key=self.attrgetter(*self.group_bys_cols)
347
- ):
345
+ for grouped, items in groupby(data, key=self.attrgetter(*self.group_bys_cols)):
348
346
  items = list(items)
349
347
  result_item = [self.format_columns(grouped)]
350
348
  for aggr_by_col in self.aggr_by_cols:
@@ -1,4 +1,4 @@
1
- import datetime
1
+ from datetime import datetime
2
2
  import logging
3
3
 
4
4
  from flask import g
@@ -13,7 +13,7 @@ log = logging.getLogger(__name__)
13
13
 
14
14
  class FileColumn(types.TypeDecorator):
15
15
  """
16
- Extends SQLAlchemy to support and mostly identify a File Column
16
+ Extends SQLAlchemy to support and mostly identify a File Column
17
17
  """
18
18
 
19
19
  impl = types.Text
@@ -21,7 +21,7 @@ class FileColumn(types.TypeDecorator):
21
21
 
22
22
  class ImageColumn(types.TypeDecorator):
23
23
  """
24
- Extends SQLAlchemy to support and mostly identify an Image Column
24
+ Extends SQLAlchemy to support and mostly identify an Image Column
25
25
 
26
26
  """
27
27
 
@@ -33,24 +33,24 @@ class ImageColumn(types.TypeDecorator):
33
33
  self.size = size
34
34
 
35
35
 
36
- class AuditMixin(object):
36
+ class AuditMixin:
37
37
  """
38
- AuditMixin
39
- Mixin for models, adds 4 columns to stamp,
40
- time and user on creation and modification
41
- will create the following columns:
42
-
43
- :created on:
44
- :changed on:
45
- :created by:
46
- :changed by:
38
+ AuditMixin
39
+ Mixin for models, adds 4 columns to stamp,
40
+ time and user on creation and modification
41
+ will create the following columns:
42
+
43
+ :created on:
44
+ :changed on:
45
+ :created by:
46
+ :changed by:
47
47
  """
48
48
 
49
- created_on = Column(DateTime, default=datetime.datetime.now, nullable=False)
49
+ created_on = Column(DateTime, default=lambda: datetime.now(), nullable=False)
50
50
  changed_on = Column(
51
51
  DateTime,
52
- default=datetime.datetime.now,
53
- onupdate=datetime.datetime.now,
52
+ default=lambda: datetime.now(),
53
+ onupdate=lambda: datetime.now(),
54
54
  nullable=False,
55
55
  )
56
56
 
@@ -62,8 +62,10 @@ class AuditMixin(object):
62
62
 
63
63
  @declared_attr
64
64
  def created_by(cls):
65
+ from flask_appbuilder.security.sqla.models import User
66
+
65
67
  return relationship(
66
- "User",
68
+ User,
67
69
  primaryjoin="%s.created_by_fk == User.id" % cls.__name__,
68
70
  enable_typechecks=False,
69
71
  )
@@ -80,8 +82,10 @@ class AuditMixin(object):
80
82
 
81
83
  @declared_attr
82
84
  def changed_by(cls):
85
+ from flask_appbuilder.security.sqla.models import User
86
+
83
87
  return relationship(
84
- "User",
88
+ User,
85
89
  primaryjoin="%s.changed_by_fk == User.id" % cls.__name__,
86
90
  enable_typechecks=False,
87
91
  )
@@ -1,91 +1,38 @@
1
- import datetime
2
- import logging
3
- import re
4
-
5
- from flask_sqlalchemy import _QueryProperty, DefaultMeta, SQLAlchemy
6
-
7
- try:
8
- from sqlalchemy.ext.declarative import as_declarative
9
- except ImportError:
10
- from sqlalchemy.ext.declarative.api import as_declarative
11
-
12
- try:
13
- from sqlalchemy.orm.util import identity_key # noqa
14
-
15
- has_identity_key = True
16
- except ImportError:
17
- has_identity_key = False
18
-
19
- log = logging.getLogger(__name__)
20
-
21
- _camelcase_re = re.compile(r"([A-Z]+)(?=[a-z0-9])")
22
-
23
-
24
- class SQLA(SQLAlchemy):
25
- """
26
- This is a child class of flask_SQLAlchemy
27
- It's purpose is to override the declarative base of the original
28
- package. So that it is bound to F.A.B. Model class allowing the dev
29
- to be in the same namespace of the security tables (and others)
30
- and can use AuditMixin class alike.
1
+ from __future__ import annotations
31
2
 
32
- Use it and configure it just like flask_SQLAlchemy
33
- """
3
+ import datetime
4
+ from typing import Any
34
5
 
35
- def make_declarative_base(self, model, metadata=None):
36
- base = Model
37
- base.query = _QueryProperty(self)
38
- return base
6
+ from sqlalchemy.orm import declarative_base
39
7
 
40
- def get_tables_for_bind(self, bind=None):
41
- """Returns a list of all tables relevant for a bind."""
42
- result = []
43
- tables = Model.metadata.tables
44
- for key in tables:
45
- if tables[key].info.get("bind_key") == bind:
46
- result.append(tables[key])
47
- return result
8
+ Base = declarative_base()
48
9
 
49
10
 
50
- class ModelDeclarativeMeta(DefaultMeta):
51
- """
52
- Base Model declarative meta for all Models definitions.
53
- Setups bind_keys to support multiple databases.
54
- Setup the table name based on the class camelcase name.
11
+ class Model(Base):
12
+ __abstract__ = True
55
13
  """
14
+ Use this class has the base for your models,
15
+ it will define your table names automatically
16
+ MyModel will be called my_model on the database.
56
17
 
18
+ ::
57
19
 
58
- @as_declarative(name="Model", metaclass=ModelDeclarativeMeta)
59
- class Model(object):
60
- """
61
- Use this class has the base for your models,
62
- it will define your table names automatically
63
- MyModel will be called my_model on the database.
20
+ from sqlalchemy import Integer, String
21
+ from flask_appbuilder import Model
64
22
 
65
- ::
66
-
67
- from sqlalchemy import Integer, String
68
- from flask_appbuilder import Model
69
-
70
- class MyModel(Model):
71
- id = Column(Integer, primary_key=True)
72
- name = Column(String(50), unique = True, nullable=False)
23
+ class MyModel(Model):
24
+ id = Column(Integer, primary_key=True)
25
+ name = Column(String(50), unique = True, nullable=False)
73
26
 
74
27
  """
75
28
 
76
29
  __table_args__ = {"extend_existing": True}
77
30
 
78
- def to_json(self):
79
- result = dict()
31
+ def to_json(self) -> dict[str, Any]:
32
+ result = {}
80
33
  for key in self.__mapper__.c.keys():
81
34
  col = getattr(self, key)
82
- if isinstance(col, datetime.datetime) or isinstance(col, datetime.date):
35
+ if isinstance(col, (datetime.datetime, datetime.date)):
83
36
  col = col.isoformat()
84
37
  result[key] = col
85
38
  return result
86
-
87
-
88
- """
89
- This is for retro compatibility
90
- """
91
- Base = Model
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from flask_sqlalchemy import SQLAlchemy
4
+ from sqlalchemy.engine import Connection, Engine
5
+ from sqlalchemy.sql.schema import Table
6
+
7
+
8
+ class SQLA(SQLAlchemy):
9
+ """
10
+ This is a child class of flask_SQLAlchemy
11
+ It's purpose is to override the declarative base of the original
12
+ package. So that it is bound to F.A.B. Model class allowing the dev
13
+ to be in the same namespace of the security tables (and others)
14
+ and can use AuditMixin class alike.
15
+
16
+ Configure just like flask_sqlalchemy SQLAlchemy
17
+ """
18
+
19
+ @staticmethod
20
+ def get_tables_for_bind(bind: Engine | Connection) -> list[Table]:
21
+ from sqlalchemy import inspect
22
+
23
+ inspector = inspect(bind)
24
+ return inspector.get_table_names()
@@ -0,0 +1,132 @@
1
+ import datetime
2
+ import logging
3
+ import re
4
+
5
+ from flask_sqlalchemy import (
6
+ _QueryProperty,
7
+ DefaultMeta,
8
+ get_state,
9
+ SessionBase,
10
+ SignallingSession,
11
+ SQLAlchemy,
12
+ )
13
+ from sqlalchemy import orm
14
+
15
+ try:
16
+ from sqlalchemy.ext.declarative import as_declarative
17
+ except ImportError:
18
+ from sqlalchemy.ext.declarative.api import as_declarative
19
+
20
+ try:
21
+ from sqlalchemy.orm.util import identity_key # noqa
22
+
23
+ has_identity_key = True
24
+ except ImportError:
25
+ has_identity_key = False
26
+
27
+ log = logging.getLogger(__name__)
28
+
29
+ _camelcase_re = re.compile(r"([A-Z]+)(?=[a-z0-9])")
30
+
31
+
32
+ class CustomSignallingSession(SignallingSession):
33
+ """
34
+ Custom Signaling Session to support SQLALchemy>=1.4 with flask-sqlalchemy 2.X
35
+ https://github.com/pallets/flask-sqlalchemy/issues/953
36
+ """
37
+
38
+ def get_bind(self, mapper=None, *args, **kwargs):
39
+ """Return the engine or connection for a given model or
40
+ table, using the ``__bind_key__`` if it is set.
41
+
42
+ Patch from https://github.com/pallets/flask-sqlalchemy/pull/1001
43
+ """
44
+ # mapper is None if someone tries to just get a connection
45
+ if mapper is not None:
46
+ try:
47
+ # SA >= 1.3
48
+ persist_selectable = mapper.persist_selectable
49
+ except AttributeError:
50
+ # SA < 1.3
51
+ persist_selectable = mapper.mapped_table
52
+ info = getattr(persist_selectable, "info", {})
53
+ bind_key = info.get("bind_key")
54
+ if bind_key is not None:
55
+ state = get_state(self.app)
56
+ return state.db.get_engine(self.app, bind=bind_key)
57
+ return SessionBase.get_bind(self, mapper, *args, **kwargs)
58
+
59
+
60
+ class SQLA(SQLAlchemy):
61
+ """
62
+ This is a child class of flask_SQLAlchemy
63
+ It's purpose is to override the declarative base of the original
64
+ package. So that it is bound to F.A.B. Model class allowing the dev
65
+ to be in the same namespace of the security tables (and others)
66
+ and can use AuditMixin class alike.
67
+
68
+ Use it and configure it just like flask_SQLAlchemy
69
+ """
70
+
71
+ def make_declarative_base(self, model, metadata=None):
72
+ base = Model
73
+ base.query = _QueryProperty(self)
74
+ return base
75
+
76
+ def get_tables_for_bind(self, bind=None):
77
+ """Returns a list of all tables relevant for a bind."""
78
+ result = []
79
+ tables = Model.metadata.tables
80
+ for key in tables:
81
+ if tables[key].info.get("bind_key") == bind:
82
+ result.append(tables[key])
83
+ return result
84
+
85
+ def create_session(self, options):
86
+ """
87
+ Custom Session factory to support SQLALchemy>=1.4 with flask-sqlalchemy 2.X
88
+
89
+ https://github.com/pallets/flask-sqlalchemy/issues/953
90
+
91
+ :param options: dict of keyword arguments passed to session class
92
+ """
93
+
94
+ return orm.sessionmaker(class_=CustomSignallingSession, db=self, **options)
95
+
96
+
97
+ class ModelDeclarativeMeta(DefaultMeta):
98
+ """
99
+ Base Model declarative meta for all Models definitions.
100
+ Setups bind_keys to support multiple databases.
101
+ Setup the table name based on the class camelcase name.
102
+ """
103
+
104
+
105
+ @as_declarative(name="Model", metaclass=ModelDeclarativeMeta)
106
+ class Model(object):
107
+ """
108
+ Use this class has the base for your models,
109
+ it will define your table names automatically
110
+ MyModel will be called my_model on the database.
111
+
112
+ ::
113
+
114
+ from sqlalchemy import Integer, String
115
+ from flask_appbuilder import Model
116
+
117
+ class MyModel(Model):
118
+ id = Column(Integer, primary_key=True)
119
+ name = Column(String(50), unique = True, nullable=False)
120
+
121
+ """
122
+
123
+ __table_args__ = {"extend_existing": True}
124
+
125
+ def to_json(self):
126
+ result = dict()
127
+ for key in self.__mapper__.c.keys():
128
+ col = getattr(self, key)
129
+ if isinstance(col, datetime.datetime) or isinstance(col, datetime.date):
130
+ col = col.isoformat()
131
+ result[key] = col
132
+ return result