cornflow 1.3.4rc2__tar.gz → 1.3.5__tar.gz

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 (194) hide show
  1. {cornflow-1.3.4rc2/cornflow.egg-info → cornflow-1.3.5}/PKG-INFO +2 -2
  2. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/auxiliar.py +2 -2
  3. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/dag.py +3 -0
  4. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/config.py +11 -0
  5. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/__init__.py +4 -0
  6. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/data_check.py +2 -0
  7. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/execution.py +205 -9
  8. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/schemas.py +1 -0
  9. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/user_role.py +12 -2
  10. cornflow-1.3.5/cornflow/migrations/versions/4729cd156460_.py +38 -0
  11. cornflow-1.3.5/cornflow/migrations/versions/9cacf194fa1c_.py +40 -0
  12. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/dag.py +5 -1
  13. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/execution.py +70 -2
  14. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/dag.py +2 -0
  15. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/execution.py +24 -3
  16. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/schemas.py +1 -0
  17. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/const.py +27 -1
  18. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/const.py +2 -0
  19. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_cli.py +5 -5
  20. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_dags.py +2 -0
  21. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_data_checks.py +39 -0
  22. cornflow-1.3.5/cornflow/tests/unit/test_executions.py +494 -0
  23. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_roles.py +30 -17
  24. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_schemas.py +7 -1
  25. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_users.py +93 -1
  26. {cornflow-1.3.4rc2 → cornflow-1.3.5/cornflow.egg-info}/PKG-INFO +2 -2
  27. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow.egg-info/SOURCES.txt +2 -0
  28. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow.egg-info/requires.txt +1 -1
  29. {cornflow-1.3.4rc2 → cornflow-1.3.5}/requirements.txt +1 -1
  30. {cornflow-1.3.4rc2 → cornflow-1.3.5}/setup.py +1 -1
  31. cornflow-1.3.4rc2/cornflow/tests/unit/test_executions.py +0 -124
  32. {cornflow-1.3.4rc2 → cornflow-1.3.5}/MANIFEST.in +0 -0
  33. {cornflow-1.3.4rc2 → cornflow-1.3.5}/README.rst +0 -0
  34. {cornflow-1.3.4rc2 → cornflow-1.3.5}/airflow_config/__init__.py +0 -0
  35. {cornflow-1.3.4rc2 → cornflow-1.3.5}/airflow_config/airflow_local_settings.py +0 -0
  36. {cornflow-1.3.4rc2 → cornflow-1.3.5}/airflow_config/plugins/XCom/__init__.py +0 -0
  37. {cornflow-1.3.4rc2 → cornflow-1.3.5}/airflow_config/plugins/XCom/gce_xcom_backend.py +0 -0
  38. {cornflow-1.3.4rc2 → cornflow-1.3.5}/airflow_config/plugins/__init__.py +0 -0
  39. {cornflow-1.3.4rc2 → cornflow-1.3.5}/airflow_config/webserver_ldap.py +0 -0
  40. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/__init__.py +0 -0
  41. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/app.py +0 -0
  42. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/__init__.py +0 -0
  43. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/actions.py +0 -0
  44. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/arguments.py +0 -0
  45. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/config.py +0 -0
  46. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/migrations.py +0 -0
  47. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/permissions.py +0 -0
  48. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/roles.py +0 -0
  49. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/schemas.py +0 -0
  50. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/service.py +0 -0
  51. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/__init__.py +0 -0
  52. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/api_generator.py +0 -0
  53. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/endpoint_tools.py +0 -0
  54. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/models_tools.py +0 -0
  55. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/schema_generator.py +0 -0
  56. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/schemas_tools.py +0 -0
  57. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/tools/tools.py +0 -0
  58. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/users.py +0 -0
  59. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/utils.py +0 -0
  60. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/cli/views.py +0 -0
  61. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/__init__.py +0 -0
  62. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/access.py +0 -0
  63. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/actions.py +0 -0
  64. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/cleanup.py +0 -0
  65. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/permissions.py +0 -0
  66. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/roles.py +0 -0
  67. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/schemas.py +0 -0
  68. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/users.py +0 -0
  69. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/commands/views.py +0 -0
  70. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/action.py +0 -0
  71. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/alarms.py +0 -0
  72. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/apiview.py +0 -0
  73. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/case.py +0 -0
  74. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/dag.py +0 -0
  75. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/example_data.py +0 -0
  76. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/health.py +0 -0
  77. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/instance.py +0 -0
  78. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/licenses.py +0 -0
  79. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/login.py +0 -0
  80. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/main_alarms.py +0 -0
  81. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/meta_resource.py +0 -0
  82. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/permission.py +0 -0
  83. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/roles.py +0 -0
  84. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/signup.py +0 -0
  85. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/tables.py +0 -0
  86. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/token.py +0 -0
  87. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/endpoints/user.py +0 -0
  88. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/gunicorn.py +0 -0
  89. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/README +0 -0
  90. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/alembic.ini +0 -0
  91. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/env.py +0 -0
  92. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/script.py.mako +0 -0
  93. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/00757b557b02_.py +0 -0
  94. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/1af47a419bbd_.py +0 -0
  95. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/4aac5e0c6e66_.py +0 -0
  96. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/509d280698e4_.py +0 -0
  97. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/7c3ea5ab5501_.py +0 -0
  98. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/991b98e24225_.py +0 -0
  99. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/999b98e24225.py +0 -0
  100. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/a472b5ad50b7_.py +0 -0
  101. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/c2db9409cb5f_.py +0 -0
  102. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/c8a6c762e818_.py +0 -0
  103. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/ca449af8034c_.py +0 -0
  104. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/cef1df240b27_.py +0 -0
  105. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/d0e0700dcd8e_.py +0 -0
  106. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/d1b5be1f0549_.py +0 -0
  107. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/e1a50dae1ac9_.py +0 -0
  108. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/e937a5234ce4_.py +0 -0
  109. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/ebdd955fcc5e_.py +0 -0
  110. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/migrations/versions/f3bee20314a2_.py +0 -0
  111. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/__init__.py +0 -0
  112. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/action.py +0 -0
  113. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/alarms.py +0 -0
  114. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/base_data_model.py +0 -0
  115. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/case.py +0 -0
  116. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/dag_permissions.py +0 -0
  117. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/instance.py +0 -0
  118. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/main_alarms.py +0 -0
  119. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/meta_models.py +0 -0
  120. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/permissions.py +0 -0
  121. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/role.py +0 -0
  122. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/user.py +0 -0
  123. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/user_role.py +0 -0
  124. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/models/view.py +0 -0
  125. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/__init__.py +0 -0
  126. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/action.py +0 -0
  127. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/alarms.py +0 -0
  128. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/case.py +0 -0
  129. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/common.py +0 -0
  130. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/example_data.py +0 -0
  131. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/health.py +0 -0
  132. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/instance.py +0 -0
  133. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/main_alarms.py +0 -0
  134. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/model_json.py +0 -0
  135. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/patch.py +0 -0
  136. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/permissions.py +0 -0
  137. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/query.py +0 -0
  138. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/role.py +0 -0
  139. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/solution_log.py +0 -0
  140. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/tables.py +0 -0
  141. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/user.py +0 -0
  142. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/user_role.py +0 -0
  143. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/schemas/view.py +0 -0
  144. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/__init__.py +0 -0
  145. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/authentication/__init__.py +0 -0
  146. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/authentication/auth.py +0 -0
  147. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/authentication/decorators.py +0 -0
  148. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/authentication/ldap.py +0 -0
  149. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/compress.py +0 -0
  150. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/email.py +0 -0
  151. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/exceptions.py +0 -0
  152. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/licenses.py +0 -0
  153. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/log_config.py +0 -0
  154. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/query_tools.py +0 -0
  155. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/utils.py +0 -0
  156. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/utils_tables.py +0 -0
  157. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/shared/validators.py +0 -0
  158. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/__init__.py +0 -0
  159. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/base_test_execution.py +0 -0
  160. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/custom_liveServer.py +0 -0
  161. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/custom_test_case.py +0 -0
  162. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/integration/__init__.py +0 -0
  163. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/integration/test_commands.py +0 -0
  164. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/integration/test_cornflowclient.py +0 -0
  165. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/integration/test_data_checks_kpis.py +0 -0
  166. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/ldap/__init__.py +0 -0
  167. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/ldap/test_ldap_authentication.py +0 -0
  168. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/__init__.py +0 -0
  169. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_actions.py +0 -0
  170. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_alarms.py +0 -0
  171. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_apiview.py +0 -0
  172. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_application.py +0 -0
  173. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_cases.py +0 -0
  174. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_commands.py +0 -0
  175. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_example_data.py +0 -0
  176. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_external_role_creation.py +0 -0
  177. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_generate_from_schema.py +0 -0
  178. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_get_resources.py +0 -0
  179. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_health.py +0 -0
  180. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_instances.py +0 -0
  181. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_instances_file.py +0 -0
  182. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_licenses.py +0 -0
  183. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_log_in.py +0 -0
  184. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_main_alarms.py +0 -0
  185. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_permissions.py +0 -0
  186. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_schema_from_models.py +0 -0
  187. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_sign_up.py +0 -0
  188. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_tables.py +0 -0
  189. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/test_token.py +0 -0
  190. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow/tests/unit/tools.py +0 -0
  191. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow.egg-info/dependency_links.txt +0 -0
  192. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow.egg-info/entry_points.txt +0 -0
  193. {cornflow-1.3.4rc2 → cornflow-1.3.5}/cornflow.egg-info/top_level.txt +0 -0
  194. {cornflow-1.3.4rc2 → cornflow-1.3.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cornflow
3
- Version: 1.3.4rc2
3
+ Version: 1.3.5
4
4
  Summary: cornflow is an open source multi-solver optimization server with a REST API built using flask.
5
5
  Home-page: https://github.com/baobabsoluciones/cornflow
6
6
  Author: baobab soluciones
@@ -14,7 +14,7 @@ Requires-Dist: alembic==1.9.2
14
14
  Requires-Dist: apispec==6.3.0
15
15
  Requires-Dist: cachetools==5.3.3
16
16
  Requires-Dist: click==8.1.7
17
- Requires-Dist: cornflow-client==1.3.4rc2
17
+ Requires-Dist: cornflow-client==1.3.5
18
18
  Requires-Dist: cryptography==46.0.5
19
19
  Requires-Dist: disposable-email-domains==0.0.162
20
20
  Requires-Dist: Flask==2.3.2
@@ -87,8 +87,8 @@ def get_new_roles_to_add(extra_permissions, resources_roles_with_access):
87
87
 
88
88
  for role_id in roles_with_access:
89
89
  if role_id not in existing_roles:
90
- if role_id in ALL_DEFAULT_ROLES:
91
- # Create standard role with predefined name
90
+ if role_id in ROLES_MAP:
91
+ # Create role with its predefined name from ROLES_MAP
92
92
  role_name = ROLES_MAP[role_id]
93
93
  new_role = RoleModel(
94
94
  {
@@ -49,6 +49,7 @@ def register_deployed_dags_command(
49
49
  "instance_checks_schema": schemas[dag["dag_id"]]["instance_checks"],
50
50
  "solution_checks_schema": schemas[dag["dag_id"]]["solution_checks"],
51
51
  "config_schema": schemas[dag["dag_id"]]["config"],
52
+ "kpis_schema": schemas[dag["dag_id"]]["kpis"],
52
53
  }
53
54
  )
54
55
  for dag in dag_list
@@ -92,6 +93,7 @@ def register_deployed_dags_command_test(dags: list = None, verbose: bool = False
92
93
  "solution_schema": get_pulp_jsonschema(),
93
94
  "instance_checks_schema": dict(),
94
95
  "solution_checks_schema": dict(),
96
+ "kpis_schema": dict(),
95
97
  "config_schema": get_empty_schema(solvers=["cbc", "PULP_CBC_CMD"]),
96
98
  }
97
99
  )
@@ -104,6 +106,7 @@ def register_deployed_dags_command_test(dags: list = None, verbose: bool = False
104
106
  "solution_schema": dict(),
105
107
  "instance_checks_schema": dict(),
106
108
  "solution_checks_schema": dict(),
109
+ "kpis_schema": dict(),
107
110
  "config_schema": dict(),
108
111
  }
109
112
  )
@@ -13,6 +13,7 @@ from cornflow.shared.const import (
13
13
  from apispec import APISpec
14
14
  from apispec.ext.marshmallow import MarshmallowPlugin
15
15
 
16
+
16
17
  class DefaultConfig(object):
17
18
  """
18
19
  Default configuration class
@@ -110,6 +111,16 @@ class DefaultConfig(object):
110
111
  # Alarms endpoints
111
112
  ALARMS_ENDPOINTS = os.getenv("CF_ALARMS_ENDPOINT", 0)
112
113
 
114
+ # Execution files
115
+ EXECUTION_FILES = int(os.getenv("EXECUTION_FILES", 0))
116
+ execution_files_path = os.path.join(os.getcwd(), "execution_files")
117
+ EXECUTION_FILES_PATH = os.getenv("EXECUTION_FILES_PATH", execution_files_path)
118
+ # Cleanup frequency (in days). By default, execution files that are older than 90 days will be deleted.
119
+ # If 0: no executions files will ever be deleted.
120
+ EXECUTION_FILES_CLEANUP_FREQUENCY = int(
121
+ os.getenv("EXECUTION_FILES_CLEANUP_FREQUENCY", 90)
122
+ )
123
+
113
124
  # Token duration in hours
114
125
  TOKEN_DURATION = os.getenv("TOKEN_DURATION", 24)
115
126
 
@@ -39,6 +39,8 @@ from .execution import (
39
39
  ExecutionDataEndpoint,
40
40
  ExecutionLogEndpoint,
41
41
  ExecutionRelaunchEndpoint,
42
+ ExecutionFilesEndpoint,
43
+ ExecutionFilesCleanupEndpoint
42
44
  )
43
45
  from .health import HealthEndpoint
44
46
  from .instance import (
@@ -128,6 +130,8 @@ resources = [
128
130
  endpoint="execution-relaunch",
129
131
  ),
130
132
  dict(resource=ExecutionEndpoint, urls="/execution/", endpoint="execution"),
133
+ dict(resource=ExecutionFilesEndpoint, urls="/execution/files/<string:idx>/", endpoint="execution-files"),
134
+ dict(resource=ExecutionFilesCleanupEndpoint, urls="/execution/files/cleanup/", endpoint="execution-files-cleanup"),
131
135
  dict(resource=DAGDetailEndpoint, urls="/dag/<string:idx>/", endpoint="dag"),
132
136
  dict(resource=DAGEndpointManual, urls="/dag/", endpoint="dag-manual"),
133
137
  dict(
@@ -232,6 +232,7 @@ class DataCheckInstanceEndpoint(BaseMetaResource):
232
232
  instance_id=instance.id,
233
233
  name=f"data_check_instance_{instance.name}",
234
234
  schema=instance.schema,
235
+ checks_and_kpis_only=True,
235
236
  )
236
237
  schema = instance.schema
237
238
 
@@ -337,6 +338,7 @@ class DataCheckCaseKPIsEndpoint(BaseMetaResource):
337
338
  instance_id=instance.id,
338
339
  name=f"data_check_case_{case.name}",
339
340
  schema=schema,
341
+ checks_and_kpis_only=True,
340
342
  )
341
343
  if case.solution is not None:
342
344
  validation_schema = schema
@@ -4,18 +4,23 @@ or check the status of an ongoing one
4
4
  These endpoints hve different access url, but manage the same data entities
5
5
  """
6
6
 
7
- # Import from libraries
8
- from cornflow_client.airflow.api import Airflow
7
+ # Imports from external libraries
8
+ from datetime import datetime, timedelta, timezone
9
+ from flask import request, current_app, make_response, send_file
10
+ from flask_apispec import marshal_with, use_kwargs, doc
11
+
12
+ import os
13
+ import time
14
+ import zipfile
9
15
 
16
+ # Import from cornflow-client
17
+ from cornflow_client.airflow.api import Airflow
10
18
  from cornflow_client.databricks.api import Databricks
11
19
  from cornflow_client.constants import INSTANCE_SCHEMA, CONFIG_SCHEMA, SOLUTION_SCHEMA
12
- from flask import request, current_app
13
- from flask_apispec import marshal_with, use_kwargs, doc
14
20
 
15
21
  # Import from internal modules
16
22
  from cornflow.endpoints.meta_resource import BaseMetaResource
17
23
  from cornflow.models import InstanceModel, DeployedWorkflow, ExecutionModel
18
- from cornflow.shared.const import config_orchestrator
19
24
  from cornflow.schemas.execution import (
20
25
  ExecutionDetailsEndpointResponse,
21
26
  ExecutionDetailsEndpointWithIndicatorsResponse,
@@ -28,14 +33,15 @@ from cornflow.schemas.execution import (
28
33
  QueryFiltersExecution,
29
34
  ReLaunchExecutionRequest,
30
35
  ExecutionDetailsWithIndicatorsAndLogResponse,
36
+ ExecutionFilesPostRequest,
31
37
  )
32
38
  from cornflow.shared.authentication import Auth, authenticate
33
39
  from cornflow.shared.compress import compressed
34
40
  from cornflow.shared.const import (
41
+ config_orchestrator,
42
+ SERVICE_ROLE,
35
43
  AIRFLOW_BACKEND,
36
44
  DATABRICKS_BACKEND,
37
- )
38
- from cornflow.shared.const import (
39
45
  AIRFLOW_ERROR_MSG,
40
46
  AIRFLOW_NOT_REACHABLE_MSG,
41
47
  DAG_PAUSED_MSG,
@@ -49,6 +55,9 @@ from cornflow.shared.const import (
49
55
  DATABRICKS_TO_STATE_MAP,
50
56
  EXEC_STATE_STOPPED,
51
57
  EXEC_STATE_QUEUED,
58
+ EXECUTION_FILES_STATUS_OK,
59
+ EXECUTION_FILES_STATUS_DELETED,
60
+ EXECUTION_FILES_STATUS_MESSAGE_DICT,
52
61
  )
53
62
 
54
63
  from cornflow.shared.exceptions import (
@@ -57,6 +66,7 @@ from cornflow.shared.exceptions import (
57
66
  ObjectDoesNotExist,
58
67
  InvalidData,
59
68
  EndpointNotImplemented,
69
+ InvalidUsage,
60
70
  )
61
71
  from cornflow.shared.validators import (
62
72
  json_schema_validate_as_string,
@@ -142,14 +152,16 @@ class ExecutionEndpoint(OrchestratorMixin):
142
152
  created by the authenticated user) and a integer with the HTTP status code
143
153
  :rtype: Tuple(dict, integer)
144
154
  """
145
- executions = self.get_list(user=self.get_user(), **kwargs)
155
+ checks_and_kpis = kwargs.pop("checks_and_kpis", False)
156
+ executions = self.get_list(
157
+ user=self.get_user(), checks_and_kpis=checks_and_kpis, **kwargs
158
+ )
146
159
  current_app.logger.info(f"User {self.get_user()} gets list of executions")
147
160
 
148
161
  executions = [
149
162
  execution
150
163
  for execution in executions
151
164
  if not execution.config.get("checks_only", False)
152
- and not execution.config.get("checks_and_kpis_only", False)
153
165
  ]
154
166
 
155
167
  running_executions = [
@@ -754,6 +766,190 @@ class ExecutionLogEndpoint(ExecutionDetailsEndpointBase):
754
766
  return self.get_detail(user=self.get_user(), idx=idx)
755
767
 
756
768
 
769
+ class ExecutionFilesEndpointMixin(ExecutionDetailsEndpointBase):
770
+ """
771
+ Endpoint used to handle the execution files.
772
+ """
773
+
774
+ ROLES_WITH_ACCESS = [SERVICE_ROLE]
775
+
776
+ @staticmethod
777
+ def check_execution_files_active(func):
778
+ def wrapper(self, *args, **kwargs):
779
+ """
780
+ If execution files are not enabled, return an Exception
781
+ """
782
+ if not current_app.config["EXECUTION_FILES"]:
783
+ raise EndpointNotImplemented(
784
+ "Execution files are not active for this deployment"
785
+ )
786
+ return func(self, *args, **kwargs)
787
+
788
+ return wrapper
789
+
790
+ @staticmethod
791
+ def _get_execution_files_path(execution_idx):
792
+ """
793
+ Generate execution files path.
794
+ """
795
+ return os.path.join(
796
+ current_app.config["EXECUTION_FILES_PATH"], f"{execution_idx}.zip"
797
+ )
798
+
799
+
800
+ class ExecutionFilesEndpoint(ExecutionFilesEndpointMixin):
801
+ @doc(description="Get execution files", tags=["Executions"], inherit=False)
802
+ @authenticate(auth_class=Auth())
803
+ @ExecutionFilesEndpointMixin.check_execution_files_active
804
+ def get(self, idx):
805
+ """
806
+ Get the execution files for a specific execution.
807
+ :param str idx: ID of the execution.
808
+ """
809
+ current_app.logger.info(
810
+ f"User {self.get_user()} gets execution files for execution {idx}"
811
+ )
812
+ execution = self.data_model.get_one_object(user=self.get_user(), idx=idx)
813
+ if execution is None:
814
+ raise ObjectDoesNotExist(
815
+ log_txt=f"Error while user {self.get_user()} tries to get the files of execution {idx}. "
816
+ f"The execution does not exist."
817
+ )
818
+
819
+ execution_files_status = execution.execution_files_status
820
+ if execution_files_status != EXECUTION_FILES_STATUS_OK:
821
+ return {
822
+ "status": execution_files_status,
823
+ "error": EXECUTION_FILES_STATUS_MESSAGE_DICT[execution_files_status],
824
+ }, 400
825
+
826
+ zip_file_path = self._get_execution_files_path(idx)
827
+ if not os.path.exists(zip_file_path):
828
+ # Files were not found. We try indicating to the front to re-generate them.
829
+ execution.update({"execution_files_status": EXECUTION_FILES_STATUS_DELETED})
830
+ return {
831
+ "status": EXECUTION_FILES_STATUS_DELETED,
832
+ "error": EXECUTION_FILES_STATUS_MESSAGE_DICT[
833
+ EXECUTION_FILES_STATUS_DELETED
834
+ ],
835
+ }, 400
836
+
837
+ # Generate response
838
+ response = make_response(
839
+ send_file(
840
+ zip_file_path,
841
+ mimetype="application/zip",
842
+ as_attachment=True,
843
+ download_name=f'{execution.name}_{time.strftime("%Y%m%d-%H%M%S")}.zip',
844
+ )
845
+ )
846
+ response.headers["X-Message"] = EXECUTION_FILES_STATUS_MESSAGE_DICT[
847
+ execution_files_status
848
+ ]
849
+ response.headers["X-Status-Code"] = "200"
850
+ return response
851
+
852
+ @doc(description="Save an execution's files", tags=["Executions"], inherit=False)
853
+ @authenticate(auth_class=Auth())
854
+ @ExecutionFilesEndpointMixin.check_execution_files_active
855
+ def post(self, idx):
856
+ """
857
+ Save the execution files for a specific execution.
858
+ :param str idx: ID of the execution.
859
+ :param kwargs: dict with status
860
+ """
861
+ current_app.logger.info(
862
+ f"User {self.get_user()} creates execution files for execution {idx}"
863
+ )
864
+
865
+ # Check request data format
866
+ request_data = request.values.to_dict()
867
+ request_data = ExecutionFilesPostRequest().load(request_data)
868
+
869
+ execution_files_status = request_data["execution_files_status"]
870
+
871
+ execution = self.data_model.get_one_object(user=self.get_user(), idx=idx)
872
+ if execution is None:
873
+ raise ObjectDoesNotExist(
874
+ log_txt=f"Error while user {self.get_user()} tries to save the files of execution {idx}. "
875
+ f"The execution does not exist."
876
+ )
877
+
878
+ if execution_files_status != EXECUTION_FILES_STATUS_OK:
879
+ execution.update({"execution_files_status": execution_files_status})
880
+ return {"message": "Execution files status saved correctly"}, 200
881
+
882
+ file = request.files.get("execution_file")
883
+ if file is None:
884
+ raise InvalidUsage(
885
+ "Execution file status was 'OK' but not file was provided."
886
+ )
887
+
888
+ if not file.filename.lower().endswith(".zip") or not zipfile.is_zipfile(
889
+ file.stream
890
+ ):
891
+ raise InvalidUsage("The execution file must be a valid .zip file")
892
+
893
+ file.stream.seek(0)
894
+
895
+ zip_file_path = self._get_execution_files_path(idx)
896
+
897
+ if not os.path.exists(current_app.config["EXECUTION_FILES_PATH"]):
898
+ os.makedirs(current_app.config["EXECUTION_FILES_PATH"])
899
+
900
+ file.save(zip_file_path)
901
+ execution.update({"execution_files_status": execution_files_status})
902
+
903
+ return {"message": "Execution files saved correctly"}, 200
904
+
905
+
906
+ class ExecutionFilesCleanupEndpoint(ExecutionFilesEndpointMixin):
907
+ @doc(description="Clean old executions files", tags=["Executions"], inherit=False)
908
+ @authenticate(auth_class=Auth())
909
+ @ExecutionFilesEndpointMixin.check_execution_files_active
910
+ def delete(self):
911
+ """
912
+ Clean old execution files.
913
+ """
914
+ current_app.logger.info(f"User {self.get_user()} runs execution files cleanup")
915
+
916
+ execution_files_path = current_app.config["EXECUTION_FILES_PATH"]
917
+ cleanup_frequency = current_app.config["EXECUTION_FILES_CLEANUP_FREQUENCY"]
918
+ if cleanup_frequency == 0:
919
+ raise EndpointNotImplemented(
920
+ "Execution files cleanup is deactivated server-side."
921
+ )
922
+
923
+ if not os.path.exists(execution_files_path):
924
+ return {"message": "No execution files found"}, 200
925
+
926
+ nb_deleted_files = 0
927
+ for file in os.listdir(execution_files_path):
928
+ execution_id = file.replace(".zip", "")
929
+ full_file_path = os.path.join(execution_files_path, file)
930
+
931
+ execution = self.data_model.get_one_object(
932
+ user=self.get_user(), idx=execution_id
933
+ )
934
+ today = datetime.now(timezone.utc)
935
+ if execution is None or (
936
+ execution.updated_at.replace(tzinfo=timezone.utc)
937
+ <= today - timedelta(days=cleanup_frequency)
938
+ ):
939
+ try:
940
+ os.remove(full_file_path)
941
+ nb_deleted_files += 1
942
+ if execution is not None:
943
+ execution.update(
944
+ {"execution_files_status": EXECUTION_FILES_STATUS_DELETED}
945
+ )
946
+ except Exception as err:
947
+ current_app.logger.error(
948
+ f"Error deleting execution file {file}: {err}"
949
+ )
950
+ return {"message": f"{nb_deleted_files} files were deleted."}, 200
951
+
952
+
757
953
  # region aux_functions
758
954
 
759
955
 
@@ -70,6 +70,7 @@ class SchemaDetailsEndpoint(BaseMetaResource):
70
70
  "solution": deployed_dag.solution_schema,
71
71
  "instance_checks": deployed_dag.instance_checks_schema,
72
72
  "solution_checks": deployed_dag.solution_checks_schema,
73
+ "kpis": deployed_dag.kpis_schema,
73
74
  "config": deployed_dag.config_schema,
74
75
  "name": dag_name,
75
76
  }, 200
@@ -18,6 +18,10 @@ from cornflow.shared.exceptions import EndpointNotImplemented, ObjectAlreadyExis
18
18
 
19
19
  class UserRoleListEndpoint(BaseMetaResource):
20
20
  ROLES_WITH_ACCESS = [ADMIN_ROLE]
21
+ # Other roles can get their own roles, but not all roles
22
+ # This is because the admin role is the only one that can get all roles
23
+ # and the other roles can only get their own roles
24
+
21
25
  DESCRIPTION = (
22
26
  "Endpoint to get the list of roles assigned to users and create new assignments"
23
27
  )
@@ -34,16 +38,22 @@ class UserRoleListEndpoint(BaseMetaResource):
34
38
  API method to get the assigned roles to the users defined in the application.
35
39
  It requires authentication to be passed in the form of a token that has to be linked to
36
40
  an existing session (login) made by a user.
41
+ Admin users receive every assignment; other users receive only their own.
37
42
 
38
43
  :return: A dictionary with the response (data of the user assigned roles or an error message)
39
44
  and an integer with the HTTP status code.
40
45
  :rtype: Tuple(dict, integer)
41
46
 
42
47
  """
48
+ if self.is_admin():
49
+ current_app.logger.info(
50
+ f"User {self.get_user()} gets all user roles assignments"
51
+ )
52
+ return self.get_list()
43
53
  current_app.logger.info(
44
- f"User {self.get_user()} gets all user roles assignments"
54
+ f"User {self.get_user()} gets their own user role assignments"
45
55
  )
46
- return self.get_list()
56
+ return self.get_list(user_id=self.get_user_id())
47
57
 
48
58
  @doc(description="Creates a new role assignment", tags=["User roles"])
49
59
  @authenticate(auth_class=Auth())
@@ -0,0 +1,38 @@
1
+ """empty message
2
+
3
+ Revision ID: 4729cd156460
4
+ Revises: 9cacf194fa1c
5
+ Create Date: 2026-05-07 14:58:53.461691
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "4729cd156460"
15
+ down_revision = "9cacf194fa1c"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ with op.batch_alter_table("executions", schema=None) as batch_op:
22
+ batch_op.add_column(
23
+ sa.Column("execution_files_status", sa.SmallInteger(), nullable=True)
24
+ )
25
+
26
+ op.execute(
27
+ 'update "executions" set execution_files_status = 0 where execution_files_status is null'
28
+ )
29
+
30
+ with op.batch_alter_table("executions", schema=None) as batch_op:
31
+ batch_op.alter_column(
32
+ "execution_files_status", existing_type=sa.SmallInteger(), nullable=False
33
+ )
34
+
35
+
36
+ def downgrade():
37
+ with op.batch_alter_table("executions", schema=None) as batch_op:
38
+ batch_op.drop_column("execution_files_status")
@@ -0,0 +1,40 @@
1
+ """
2
+ Add kpis_schema to deployed_workflows, add checks_and_kpis_only to execution
3
+
4
+ Revision ID: 9cacf194fa1c
5
+ Revises: 509d280698e4
6
+ Create Date: 2026-04-13 09:44:59.661880
7
+
8
+ """
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ from sqlalchemy.dialects import postgresql
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision = "9cacf194fa1c"
16
+ down_revision = "509d280698e4"
17
+ branch_labels = None
18
+ depends_on = None
19
+
20
+
21
+ def upgrade():
22
+ with op.batch_alter_table("deployed_workflows", schema=None) as batch_op:
23
+ batch_op.add_column(
24
+ sa.Column(
25
+ "kpis_schema", postgresql.JSON(astext_type=sa.Text()), nullable=True
26
+ )
27
+ )
28
+
29
+ with op.batch_alter_table("executions", schema=None) as batch_op:
30
+ batch_op.add_column(
31
+ sa.Column("checks_and_kpis_only", sa.Boolean(), nullable=True)
32
+ )
33
+
34
+
35
+ def downgrade():
36
+ with op.batch_alter_table("executions", schema=None) as batch_op:
37
+ batch_op.drop_column("checks_and_kpis_only")
38
+
39
+ with op.batch_alter_table("deployed_workflows", schema=None) as batch_op:
40
+ batch_op.drop_column("kpis_schema")
@@ -7,6 +7,7 @@ from cornflow_client.constants import (
7
7
  SOLUTION_SCHEMA,
8
8
  INSTANCE_CHECKS_SCHEMA,
9
9
  SOLUTION_CHECKS_SCHEMA,
10
+ KPIS_SCHEMA,
10
11
  )
11
12
  from sqlalchemy.dialects.postgresql import TEXT, JSON
12
13
 
@@ -29,6 +30,7 @@ class DeployedWorkflow(TraceAttributesModel):
29
30
  config_schema = db.Column(JSON, nullable=True)
30
31
  instance_checks_schema = db.Column(JSON, nullable=True)
31
32
  solution_checks_schema = db.Column(JSON, nullable=True)
33
+ kpis_schema = db.Column(JSON, nullable=True)
32
34
 
33
35
  dag_permissions = db.relationship(
34
36
  "PermissionsDAG",
@@ -46,6 +48,7 @@ class DeployedWorkflow(TraceAttributesModel):
46
48
  self.instance_checks_schema = data.get("instance_checks_schema", None)
47
49
  self.solution_checks_schema = data.get("solution_checks_schema", None)
48
50
  self.config_schema = data.get("config_schema", None)
51
+ self.kpis_schema = data.get("kpis_schema", None)
49
52
 
50
53
  def __repr__(self):
51
54
  return f"<DAG {self.id}>"
@@ -70,7 +73,8 @@ class DeployedWorkflow(TraceAttributesModel):
70
73
  jsonschema = item.instance_checks_schema
71
74
  elif schema == SOLUTION_CHECKS_SCHEMA:
72
75
  jsonschema = item.solution_checks_schema
73
- # schema == CONFIG_SCHEMA
76
+ elif schema == KPIS_SCHEMA:
77
+ jsonschema = item.kpis_schema
74
78
  else:
75
79
  jsonschema = item.config_schema
76
80
 
@@ -3,13 +3,22 @@ Model for the executions
3
3
  """
4
4
 
5
5
  # Import from libraries
6
+ from flask import current_app
6
7
  import hashlib
7
8
  from sqlalchemy.dialects.postgresql import JSON, TEXT
9
+ from sqlalchemy import desc, or_
10
+ from sqlalchemy.sql.expression import false
8
11
 
9
12
  # Imports from internal modules
10
13
  from cornflow.models.base_data_model import BaseDataModel
11
14
  from cornflow.shared import db
12
- from cornflow.shared.const import DEFAULT_EXECUTION_CODE, EXECUTION_STATE_MESSAGE_DICT
15
+ from cornflow.shared.const import (
16
+ DEFAULT_EXECUTION_CODE,
17
+ EXECUTION_STATE_MESSAGE_DICT,
18
+ EXECUTION_FILES_STATUS_NOT_GENERATED,
19
+ EXECUTION_FILES_STATUS_OK,
20
+ EXECUTION_FILES_STATUS_NOT_UP_TO_DATE,
21
+ )
13
22
 
14
23
 
15
24
  class ExecutionModel(BaseDataModel):
@@ -70,6 +79,10 @@ class ExecutionModel(BaseDataModel):
70
79
  )
71
80
  kpis = db.Column(JSON, nullable=True)
72
81
  last_run_checks_and_kpis = db.Column(db.Boolean, nullable=True)
82
+ checks_and_kpis_only = db.Column(db.Boolean, nullable=True)
83
+ execution_files_status = db.Column(
84
+ db.SmallInteger, default=EXECUTION_FILES_STATUS_NOT_GENERATED, nullable=False
85
+ )
73
86
 
74
87
  def __init__(self, data):
75
88
  super().__init__(data)
@@ -92,6 +105,10 @@ class ExecutionModel(BaseDataModel):
92
105
  self.log_json = data.get("log_json")
93
106
  self.kpis = data.get("kpis")
94
107
  self.last_run_checks_and_kpis = data.get("last_run_checks_and_kpis", False)
108
+ self.checks_and_kpis_only = data.get("checks_and_kpis_only", False)
109
+ self.execution_files_status = data.get(
110
+ "execution_files_status", EXECUTION_FILES_STATUS_NOT_GENERATED
111
+ )
95
112
 
96
113
  def update(self, data):
97
114
  """
@@ -101,10 +118,12 @@ class ExecutionModel(BaseDataModel):
101
118
  :return: None
102
119
  :rtype: None
103
120
  """
104
- # Delete the checks and KPIs if the data has been modified since they are probably not valid anymore
121
+ # Delete the checks and KPIs if the data has been modified since they are probably not valid anymore.
105
122
  if "data" in data.keys():
106
123
  self.checks = None
107
124
  self.kpis = None
125
+ if self.execution_files_status == EXECUTION_FILES_STATUS_OK:
126
+ self.execution_files_status = EXECUTION_FILES_STATUS_NOT_UP_TO_DATE
108
127
  super().update(data)
109
128
 
110
129
  def update_config(self, config: dict):
@@ -142,6 +161,55 @@ class ExecutionModel(BaseDataModel):
142
161
  self.log_text = txt
143
162
  super().update({})
144
163
 
164
+ @classmethod
165
+ def get_all_objects(
166
+ cls,
167
+ user,
168
+ schema=None,
169
+ creation_date_gte=None,
170
+ creation_date_lte=None,
171
+ checks_and_kpis=False,
172
+ offset=0,
173
+ limit=10,
174
+ ):
175
+ """
176
+ Query to get all objects from a user
177
+ :param UserModel user: User object.
178
+ :param string schema: data_schema to filter (dag)
179
+ :param string creation_date_gte: created_at needs to be larger or equal to this
180
+ :param string creation_date_lte: created_at needs to be smaller or equal to this
181
+ :param int offset: query offset for pagination
182
+ :param bool checks_and_kpis: include checks and kpis executions
183
+ :param int limit: query size limit
184
+ :return: The objects
185
+ :rtype: list(:class:`BaseDataModel`)
186
+ """
187
+ query = cls.query.filter(cls.deleted_at == None)
188
+ user_access = int(current_app.config["USER_ACCESS_ALL_OBJECTS"])
189
+ if (
190
+ user is not None
191
+ and not user.is_admin()
192
+ and not user.is_service_user()
193
+ and user_access == 0
194
+ ):
195
+ query = query.filter(cls.user_id == user.id)
196
+
197
+ if schema:
198
+ query = query.filter(cls.schema == schema)
199
+ if creation_date_gte:
200
+ query = query.filter(cls.created_at >= creation_date_gte)
201
+ if creation_date_lte:
202
+ query = query.filter(cls.created_at <= creation_date_lte)
203
+ if not checks_and_kpis:
204
+ query = query.filter(
205
+ or_(
206
+ cls.checks_and_kpis_only == false(),
207
+ cls.checks_and_kpis_only.is_(None),
208
+ )
209
+ )
210
+
211
+ return query.order_by(desc(cls.created_at)).offset(offset).limit(limit).all()
212
+
145
213
  def __repr__(self):
146
214
  """
147
215
  Method to represent the class :class:`ExecutionModel`
@@ -11,6 +11,7 @@ class DeployedDAGSchema(Schema):
11
11
  config_schema = fields.Raw(required=True, allow_none=False)
12
12
  instance_checks_schema = fields.Raw(required=True, allow_none=False)
13
13
  solution_checks_schema = fields.Raw(required=True, allow_none=False)
14
+ kpis_schema = fields.Raw(required=True, allow_none=False)
14
15
 
15
16
 
16
17
  class DeployedDAGEditSchema(Schema):
@@ -20,3 +21,4 @@ class DeployedDAGEditSchema(Schema):
20
21
  config_schema = fields.Raw(required=False)
21
22
  instance_checks_schema = fields.Raw(required=True, allow_none=False)
22
23
  solution_checks_schema = fields.Raw(required=True, allow_none=False)
24
+ kpis_schema = fields.Raw(required=True, allow_none=False)