Flowfile 0.5.1__py3-none-any.whl → 0.5.3__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.
- build_backends/main.py +25 -22
- build_backends/main_prd.py +10 -19
- flowfile/__init__.py +178 -74
- flowfile/__main__.py +10 -7
- flowfile/api.py +51 -57
- flowfile/web/__init__.py +14 -9
- flowfile/web/static/assets/AdminView-49392a9a.js +713 -0
- flowfile/web/static/assets/AdminView-f53bad23.css +129 -0
- flowfile/web/static/assets/CloudConnectionView-36bcd6df.css +72 -0
- flowfile/web/static/assets/{CloudConnectionManager-0dfba9f2.js → CloudConnectionView-f13f202b.js} +11 -11
- flowfile/web/static/assets/{CloudStorageReader-d5b1b6c9.js → CloudStorageReader-0023d4a5.js} +10 -8
- flowfile/web/static/assets/{CloudStorageReader-29d14fcc.css → CloudStorageReader-24c54524.css} +27 -27
- flowfile/web/static/assets/{CloudStorageWriter-b0ee067f.css → CloudStorageWriter-60547855.css} +26 -26
- flowfile/web/static/assets/{CloudStorageWriter-00d87aad.js → CloudStorageWriter-8e781e11.js} +10 -8
- flowfile/web/static/assets/{ColumnSelector-47996a16.css → ColumnSelector-371637fb.css} +2 -2
- flowfile/web/static/assets/{ColumnSelector-4685e75d.js → ColumnSelector-8ad68ea9.js} +3 -5
- flowfile/web/static/assets/{ContextMenu-c13f91d0.css → ContextMenu-26d4dd27.css} +6 -6
- flowfile/web/static/assets/{ContextMenu-23e909da.js → ContextMenu-31ee57f0.js} +3 -3
- flowfile/web/static/assets/{ContextMenu-70ae0c79.js → ContextMenu-69a74055.js} +3 -3
- flowfile/web/static/assets/{ContextMenu-f149cf7c.js → ContextMenu-8e2051c6.js} +3 -3
- flowfile/web/static/assets/{ContextMenu-4c74eef1.css → ContextMenu-8ec1729e.css} +6 -6
- flowfile/web/static/assets/{ContextMenu-63cfa99b.css → ContextMenu-9b310c60.css} +6 -6
- flowfile/web/static/assets/{CrossJoin-702a3edd.js → CrossJoin-03df6938.js} +12 -10
- flowfile/web/static/assets/{CrossJoin-1119d18e.css → CrossJoin-71b4cc10.css} +20 -20
- flowfile/web/static/assets/CustomNode-59e99a86.css +32 -0
- flowfile/web/static/assets/{CustomNode-b1519993.js → CustomNode-8479239b.js} +36 -24
- flowfile/web/static/assets/{DatabaseConnectionSettings-6f3e4ea5.js → DatabaseConnectionSettings-869e3efd.js} +5 -4
- flowfile/web/static/assets/{DatabaseConnectionSettings-0c04b2e5.css → DatabaseConnectionSettings-e91df89a.css} +13 -13
- flowfile/web/static/assets/{DatabaseReader-ae61773c.css → DatabaseReader-36898a00.css} +24 -24
- flowfile/web/static/assets/{DatabaseReader-d38c7295.js → DatabaseReader-c58b9552.js} +25 -15
- flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
- flowfile/web/static/assets/{DatabaseManager-cf5ef661.js → DatabaseView-d26a9140.js} +11 -11
- flowfile/web/static/assets/{DatabaseWriter-2f570e53.css → DatabaseWriter-217a99f1.css} +19 -19
- flowfile/web/static/assets/{DatabaseWriter-b04ef46a.js → DatabaseWriter-4d05ddc7.js} +17 -10
- flowfile/web/static/assets/{designer-8da3ba3a.css → DesignerView-a6d0ee84.css} +614 -546
- flowfile/web/static/assets/{designer-9633482a.js → DesignerView-e6f5c0e8.js} +1107 -3170
- flowfile/web/static/assets/{documentation-ca400224.js → DocumentationView-2e78ef1b.js} +5 -5
- flowfile/web/static/assets/{documentation-12216a74.css → DocumentationView-fd46c656.css} +7 -7
- flowfile/web/static/assets/{ExploreData-2d0cf4db.css → ExploreData-10c5acc8.css} +13 -12
- flowfile/web/static/assets/{ExploreData-5fa10ed8.js → ExploreData-7b54caca.js} +18 -9
- flowfile/web/static/assets/{ExternalSource-d39af878.js → ExternalSource-3fa399b2.js} +9 -7
- flowfile/web/static/assets/{ExternalSource-e37b6275.css → ExternalSource-47ab05a3.css} +17 -17
- flowfile/web/static/assets/Filter-7494ea97.css +48 -0
- flowfile/web/static/assets/Filter-8cbbdbf3.js +287 -0
- flowfile/web/static/assets/{Formula-bb96803d.css → Formula-53d58c43.css} +7 -7
- flowfile/web/static/assets/{Formula-6b04fb1d.js → Formula-aac42b1e.js} +13 -11
- flowfile/web/static/assets/{FuzzyMatch-1010f966.css → FuzzyMatch-ad6361d6.css} +68 -69
- flowfile/web/static/assets/{FuzzyMatch-999521f4.js → FuzzyMatch-cd9bbfca.js} +12 -10
- flowfile/web/static/assets/{Pivot-cf333e3d.css → GraphSolver-c24dec17.css} +5 -5
- flowfile/web/static/assets/{GraphSolver-17dd2198.js → GraphSolver-c7e6780e.js} +13 -11
- flowfile/web/static/assets/{GroupBy-6b039e18.js → GroupBy-93c5d22b.js} +9 -7
- flowfile/web/static/assets/{GroupBy-b9505323.css → GroupBy-be7ac0bf.css} +10 -10
- flowfile/web/static/assets/{Join-fd79b451.css → Join-28b5e18f.css} +22 -22
- flowfile/web/static/assets/{Join-24d0f113.js → Join-a19b2de2.js} +13 -11
- flowfile/web/static/assets/LoginView-0df4ed0a.js +134 -0
- flowfile/web/static/assets/LoginView-d325d632.css +172 -0
- flowfile/web/static/assets/ManualInput-3702e677.css +293 -0
- flowfile/web/static/assets/{ManualInput-34639209.js → ManualInput-8d3374b2.js} +170 -116
- flowfile/web/static/assets/{MultiSelect-0e8724a3.js → MultiSelect-ad1b6243.js} +2 -2
- flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-b0e538c2.js → MultiSelect.vue_vue_type_script_setup_true_lang-e278950d.js} +1 -1
- flowfile/web/static/assets/NodeDesigner-40b647c9.js +2610 -0
- flowfile/web/static/assets/NodeDesigner-5f53be3f.css +1429 -0
- flowfile/web/static/assets/{NumericInput-3d63a470.js → NumericInput-7100234c.js} +2 -2
- flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-e0edeccc.js → NumericInput.vue_vue_type_script_setup_true_lang-5130219f.js} +5 -2
- flowfile/web/static/assets/{Output-283fe388.css → Output-35e97000.css} +6 -6
- flowfile/web/static/assets/{Output-edea9802.js → Output-f5efd2aa.js} +12 -9
- flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
- flowfile/web/static/assets/{Pivot-61d19301.js → Pivot-d981d23c.js} +11 -9
- flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
- flowfile/web/static/assets/{PivotValidation-f97fec5b.js → PivotValidation-39386e95.js} +3 -3
- flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
- flowfile/web/static/assets/{PivotValidation-de9f43fe.js → PivotValidation-63de1f73.js} +3 -3
- flowfile/web/static/assets/{PolarsCode-650322d1.css → PolarsCode-2b1f1f23.css} +4 -4
- flowfile/web/static/assets/{PolarsCode-bc3c9984.js → PolarsCode-f9d69217.js} +18 -9
- flowfile/web/static/assets/PopOver-b22f049e.js +939 -0
- flowfile/web/static/assets/PopOver-d96599db.css +33 -0
- flowfile/web/static/assets/{Read-e808b239.css → Read-36e7bd51.css} +12 -12
- flowfile/web/static/assets/{Read-64a3f259.js → Read-aec2e377.js} +14 -11
- flowfile/web/static/assets/{RecordCount-3d5039be.js → RecordCount-78ed6845.js} +6 -4
- flowfile/web/static/assets/{RecordId-597510e0.js → RecordId-2156e890.js} +8 -6
- flowfile/web/static/assets/{SQLQueryComponent-36cef432.css → SQLQueryComponent-1c2f26b4.css} +5 -5
- flowfile/web/static/assets/{SQLQueryComponent-df51adbe.js → SQLQueryComponent-48c72f5b.js} +3 -3
- flowfile/web/static/assets/{Sample-4be0a507.js → Sample-1352ca74.js} +6 -4
- flowfile/web/static/assets/SecretSelector-22b5ff89.js +113 -0
- flowfile/web/static/assets/SecretSelector-6329f743.css +43 -0
- flowfile/web/static/assets/{SecretManager-4839be57.js → SecretsView-17df66ee.js} +35 -36
- flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
- flowfile/web/static/assets/{Select-9b72f201.js → Select-0aee4c54.js} +9 -7
- flowfile/web/static/assets/{SettingsSection-f0f75a42.js → SettingsSection-0784e157.js} +3 -3
- flowfile/web/static/assets/{SettingsSection-71e6b7e3.css → SettingsSection-07fbbc39.css} +4 -4
- flowfile/web/static/assets/{SettingsSection-5c696bee.css → SettingsSection-26fe48d4.css} +4 -4
- flowfile/web/static/assets/{SettingsSection-2e4d03c4.css → SettingsSection-8f980839.css} +4 -4
- flowfile/web/static/assets/{SettingsSection-e1e9c953.js → SettingsSection-cd341bb6.js} +3 -3
- flowfile/web/static/assets/{SettingsSection-7ded385d.js → SettingsSection-f2002a6d.js} +3 -3
- flowfile/web/static/assets/{SingleSelect-6c777aac.js → SingleSelect-460cc0ea.js} +2 -2
- flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-33e3ff9b.js → SingleSelect.vue_vue_type_script_setup_true_lang-30741bb2.js} +1 -1
- flowfile/web/static/assets/{SliderInput-7cb93e62.js → SliderInput-5d926864.js} +7 -4
- flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
- flowfile/web/static/assets/{Sort-6cbde21a.js → Sort-3cdc971b.js} +9 -7
- flowfile/web/static/assets/{Unique-f9fb0809.css → Sort-8a871341.css} +10 -10
- flowfile/web/static/assets/{TextInput-d9a40c11.js → TextInput-a2d0bfbd.js} +2 -2
- flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-5896c375.js → TextInput.vue_vue_type_script_setup_true_lang-abad1ca2.js} +5 -2
- flowfile/web/static/assets/{TextToRows-5d2c1190.css → TextToRows-12afb4f4.css} +10 -10
- flowfile/web/static/assets/{TextToRows-c4fcbf4d.js → TextToRows-918945f7.js} +11 -10
- flowfile/web/static/assets/{ToggleSwitch-4ef91d19.js → ToggleSwitch-f0ef5196.js} +2 -2
- flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-38478c20.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-5605c793.js} +1 -1
- flowfile/web/static/assets/{UnavailableFields-5edd5322.css → UnavailableFields-54d2f518.css} +6 -6
- flowfile/web/static/assets/{UnavailableFields-a03f512c.js → UnavailableFields-bdad6144.js} +4 -4
- flowfile/web/static/assets/{Union-af6c3d9b.css → Union-d6a8d7d5.css} +7 -7
- flowfile/web/static/assets/{Union-bfe9b996.js → Union-e8ab8c86.js} +8 -6
- flowfile/web/static/assets/{Unique-5d023a27.js → Unique-8cd4f976.js} +13 -10
- flowfile/web/static/assets/{Sort-3643d625.css → Unique-9fb2f567.css} +10 -10
- flowfile/web/static/assets/{Unpivot-1e422df3.css → Unpivot-710a2948.css} +7 -7
- flowfile/web/static/assets/{Unpivot-91cc5354.js → Unpivot-8da14095.js} +10 -8
- flowfile/web/static/assets/{UnpivotValidation-7ee2de44.js → UnpivotValidation-6f7d89ff.js} +3 -3
- flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
- flowfile/web/static/assets/{VueGraphicWalker-e51b9924.js → VueGraphicWalker-3fb312e1.js} +4 -4
- flowfile/web/static/assets/{VueGraphicWalker-ed5ab88b.css → VueGraphicWalker-430f0b86.css} +1 -1
- flowfile/web/static/assets/{api-cf1221f0.js → api-24483f0d.js} +1 -1
- flowfile/web/static/assets/{api-c1bad5ca.js → api-8b81fa73.js} +1 -1
- flowfile/web/static/assets/{dropDown-35135ba8.css → dropDown-3d8dc5fa.css} +40 -40
- flowfile/web/static/assets/{dropDown-614b998d.js → dropDown-ac0fda9d.js} +3 -3
- flowfile/web/static/assets/{fullEditor-f7971590.js → fullEditor-5497a84a.js} +11 -10
- flowfile/web/static/assets/{fullEditor-178376bb.css → fullEditor-a0be62b3.css} +74 -62
- flowfile/web/static/assets/{genericNodeSettings-924759c7.css → genericNodeSettings-3b2507ea.css} +10 -10
- flowfile/web/static/assets/{genericNodeSettings-4fe5f36b.js → genericNodeSettings-99014e1d.js} +5 -5
- flowfile/web/static/assets/index-07dda503.js +38 -0
- flowfile/web/static/assets/index-3ba44389.js +2696 -0
- flowfile/web/static/assets/{index-50508d4d.css → index-e6289dd0.css} +1945 -569
- flowfile/web/static/assets/{index-5429bbf8.js → index-fb6493ae.js} +41626 -40867
- flowfile/web/static/assets/node.types-2c15bb7e.js +82 -0
- flowfile/web/static/assets/nodeInput-0eb13f1a.js +2 -0
- flowfile/web/static/assets/{outputCsv-076b85ab.js → outputCsv-8f8ba42d.js} +3 -3
- flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
- flowfile/web/static/assets/{outputExcel-0fd17dbe.js → outputExcel-393f4fef.js} +3 -3
- flowfile/web/static/assets/{outputExcel-b41305c0.css → outputExcel-f5d272b2.css} +26 -26
- flowfile/web/static/assets/{outputParquet-b61e0847.js → outputParquet-07c81f65.js} +4 -4
- flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
- flowfile/web/static/assets/{readCsv-a8bb8b61.js → readCsv-07f6d9ad.js} +3 -3
- flowfile/web/static/assets/{readCsv-c767cb37.css → readCsv-3bfac4c3.css} +15 -15
- flowfile/web/static/assets/{readExcel-806d2826.css → readExcel-3db6b763.css} +13 -13
- flowfile/web/static/assets/{readExcel-67b4aee0.js → readExcel-ed69bc8f.js} +5 -5
- flowfile/web/static/assets/{readParquet-48c81530.css → readParquet-c5244ad5.css} +4 -4
- flowfile/web/static/assets/{readParquet-92ce1dbc.js → readParquet-e3ed4528.js} +3 -3
- flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
- flowfile/web/static/assets/{selectDynamic-92e25ee3.js → selectDynamic-80b92899.js} +5 -5
- flowfile/web/static/assets/{selectDynamic-aa913ff4.css → selectDynamic-f2fb394f.css} +21 -20
- flowfile/web/static/assets/{vue-codemirror.esm-41b0e0d7.js → vue-codemirror.esm-0965f39f.js} +31 -640
- flowfile/web/static/assets/{vue-content-loader.es-2c8e608f.js → vue-content-loader.es-c506ad97.js} +1 -1
- flowfile/web/static/index.html +2 -2
- {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +2 -3
- flowfile-0.5.3.dist-info/RECORD +402 -0
- flowfile_core/__init__.py +13 -6
- flowfile_core/auth/jwt.py +51 -16
- flowfile_core/auth/models.py +32 -7
- flowfile_core/auth/password.py +89 -0
- flowfile_core/auth/secrets.py +8 -6
- flowfile_core/configs/__init__.py +9 -7
- flowfile_core/configs/flow_logger.py +15 -14
- flowfile_core/configs/node_store/__init__.py +72 -4
- flowfile_core/configs/node_store/nodes.py +155 -172
- flowfile_core/configs/node_store/user_defined_node_registry.py +108 -27
- flowfile_core/configs/settings.py +28 -15
- flowfile_core/database/connection.py +7 -6
- flowfile_core/database/init_db.py +96 -2
- flowfile_core/database/models.py +3 -1
- flowfile_core/fileExplorer/__init__.py +17 -0
- flowfile_core/fileExplorer/funcs.py +123 -57
- flowfile_core/fileExplorer/utils.py +10 -11
- flowfile_core/flowfile/_extensions/real_time_interface.py +10 -8
- flowfile_core/flowfile/analytics/analytics_processor.py +26 -24
- flowfile_core/flowfile/analytics/graphic_walker.py +11 -12
- flowfile_core/flowfile/analytics/utils.py +1 -1
- flowfile_core/flowfile/code_generator/code_generator.py +358 -244
- flowfile_core/flowfile/connection_manager/_connection_manager.py +6 -5
- flowfile_core/flowfile/connection_manager/models.py +1 -1
- flowfile_core/flowfile/database_connection_manager/db_connections.py +60 -44
- flowfile_core/flowfile/database_connection_manager/models.py +1 -1
- flowfile_core/flowfile/extensions.py +17 -12
- flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +34 -32
- flowfile_core/flowfile/flow_data_engine/create/funcs.py +115 -83
- flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +481 -423
- flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +2 -2
- flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +92 -52
- flowfile_core/flowfile/flow_data_engine/flow_file_column/polars_type.py +12 -11
- flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +6 -6
- flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +26 -30
- flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +31 -20
- flowfile_core/flowfile/flow_data_engine/join/__init__.py +1 -1
- flowfile_core/flowfile/flow_data_engine/join/utils.py +11 -9
- flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +14 -15
- flowfile_core/flowfile/flow_data_engine/pivot_table.py +5 -7
- flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +95 -82
- flowfile_core/flowfile/flow_data_engine/read_excel_tables.py +66 -65
- flowfile_core/flowfile/flow_data_engine/sample_data.py +27 -21
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/__init__.py +1 -1
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +13 -11
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +190 -127
- flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
- flowfile_core/flowfile/flow_data_engine/utils.py +99 -67
- flowfile_core/flowfile/flow_graph.py +918 -571
- flowfile_core/flowfile/flow_graph_utils.py +31 -49
- flowfile_core/flowfile/flow_node/flow_node.py +330 -233
- flowfile_core/flowfile/flow_node/models.py +53 -41
- flowfile_core/flowfile/flow_node/schema_callback.py +14 -19
- flowfile_core/flowfile/graph_tree/graph_tree.py +41 -41
- flowfile_core/flowfile/handler.py +80 -30
- flowfile_core/flowfile/manage/compatibility_enhancements.py +209 -126
- flowfile_core/flowfile/manage/io_flowfile.py +54 -57
- flowfile_core/flowfile/node_designer/__init__.py +15 -13
- flowfile_core/flowfile/node_designer/_type_registry.py +34 -37
- flowfile_core/flowfile/node_designer/custom_node.py +162 -36
- flowfile_core/flowfile/node_designer/ui_components.py +135 -34
- flowfile_core/flowfile/schema_callbacks.py +71 -51
- flowfile_core/flowfile/setting_generator/__init__.py +0 -1
- flowfile_core/flowfile/setting_generator/setting_generator.py +6 -5
- flowfile_core/flowfile/setting_generator/settings.py +64 -53
- flowfile_core/flowfile/sources/external_sources/base_class.py +12 -10
- flowfile_core/flowfile/sources/external_sources/custom_external_sources/external_source.py +27 -17
- flowfile_core/flowfile/sources/external_sources/custom_external_sources/sample_users.py +9 -9
- flowfile_core/flowfile/sources/external_sources/factory.py +0 -1
- flowfile_core/flowfile/sources/external_sources/sql_source/models.py +45 -31
- flowfile_core/flowfile/sources/external_sources/sql_source/sql_source.py +198 -73
- flowfile_core/flowfile/sources/external_sources/sql_source/utils.py +250 -196
- flowfile_core/flowfile/util/calculate_layout.py +9 -13
- flowfile_core/flowfile/util/execution_orderer.py +25 -17
- flowfile_core/flowfile/util/node_skipper.py +4 -4
- flowfile_core/flowfile/utils.py +19 -21
- flowfile_core/main.py +26 -19
- flowfile_core/routes/auth.py +284 -11
- flowfile_core/routes/cloud_connections.py +25 -25
- flowfile_core/routes/logs.py +21 -29
- flowfile_core/routes/public.py +3 -3
- flowfile_core/routes/routes.py +70 -34
- flowfile_core/routes/secrets.py +25 -27
- flowfile_core/routes/user_defined_components.py +483 -4
- flowfile_core/run_lock.py +0 -1
- flowfile_core/schemas/__init__.py +4 -6
- flowfile_core/schemas/analysis_schemas/graphic_walker_schemas.py +55 -55
- flowfile_core/schemas/cloud_storage_schemas.py +59 -53
- flowfile_core/schemas/input_schema.py +231 -144
- flowfile_core/schemas/output_model.py +49 -34
- flowfile_core/schemas/schemas.py +116 -89
- flowfile_core/schemas/transform_schema.py +518 -263
- flowfile_core/schemas/yaml_types.py +21 -7
- flowfile_core/secret_manager/secret_manager.py +17 -13
- flowfile_core/types.py +29 -9
- flowfile_core/utils/arrow_reader.py +7 -6
- flowfile_core/utils/excel_file_manager.py +3 -3
- flowfile_core/utils/fileManager.py +7 -7
- flowfile_core/utils/fl_executor.py +8 -10
- flowfile_core/utils/utils.py +4 -4
- flowfile_core/utils/validate_setup.py +5 -4
- flowfile_frame/__init__.py +106 -51
- flowfile_frame/adapters.py +2 -9
- flowfile_frame/adding_expr.py +73 -32
- flowfile_frame/cloud_storage/frame_helpers.py +27 -23
- flowfile_frame/cloud_storage/secret_manager.py +12 -26
- flowfile_frame/config.py +2 -5
- flowfile_frame/expr.py +311 -218
- flowfile_frame/expr.pyi +160 -159
- flowfile_frame/expr_name.py +23 -23
- flowfile_frame/flow_frame.py +571 -476
- flowfile_frame/flow_frame.pyi +123 -104
- flowfile_frame/flow_frame_methods.py +227 -246
- flowfile_frame/group_frame.py +50 -20
- flowfile_frame/join.py +2 -2
- flowfile_frame/lazy.py +129 -87
- flowfile_frame/lazy_methods.py +83 -30
- flowfile_frame/list_name_space.py +55 -50
- flowfile_frame/selectors.py +148 -68
- flowfile_frame/series.py +9 -7
- flowfile_frame/utils.py +19 -21
- flowfile_worker/__init__.py +12 -7
- flowfile_worker/configs.py +11 -19
- flowfile_worker/create/__init__.py +14 -9
- flowfile_worker/create/funcs.py +114 -77
- flowfile_worker/create/models.py +46 -43
- flowfile_worker/create/pl_types.py +14 -15
- flowfile_worker/create/read_excel_tables.py +34 -41
- flowfile_worker/create/utils.py +22 -19
- flowfile_worker/external_sources/s3_source/main.py +18 -51
- flowfile_worker/external_sources/s3_source/models.py +34 -27
- flowfile_worker/external_sources/sql_source/main.py +8 -5
- flowfile_worker/external_sources/sql_source/models.py +13 -9
- flowfile_worker/flow_logger.py +10 -8
- flowfile_worker/funcs.py +214 -155
- flowfile_worker/main.py +11 -17
- flowfile_worker/models.py +35 -28
- flowfile_worker/process_manager.py +2 -3
- flowfile_worker/routes.py +121 -90
- flowfile_worker/secrets.py +9 -6
- flowfile_worker/spawner.py +80 -49
- flowfile_worker/utils.py +3 -2
- shared/__init__.py +2 -7
- shared/storage_config.py +25 -13
- test_utils/postgres/commands.py +3 -2
- test_utils/postgres/fixtures.py +9 -9
- test_utils/s3/commands.py +1 -1
- test_utils/s3/data_generator.py +3 -4
- test_utils/s3/demo_data_generator.py +4 -7
- test_utils/s3/fixtures.py +7 -5
- tools/migrate/__init__.py +1 -1
- tools/migrate/__main__.py +16 -29
- tools/migrate/legacy_schemas.py +251 -190
- tools/migrate/migrate.py +193 -181
- tools/migrate/tests/conftest.py +1 -3
- tools/migrate/tests/test_migrate.py +36 -41
- tools/migrate/tests/test_migration_e2e.py +28 -29
- tools/migrate/tests/test_node_migrations.py +50 -20
- flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +0 -86
- flowfile/web/static/assets/CustomNode-74a37f74.css +0 -32
- flowfile/web/static/assets/DatabaseManager-30fa27e5.css +0 -64
- flowfile/web/static/assets/Filter-9b6d08db.js +0 -164
- flowfile/web/static/assets/Filter-f62091b3.css +0 -20
- flowfile/web/static/assets/ManualInput-3246a08d.css +0 -96
- flowfile/web/static/assets/PivotValidation-891ddfb0.css +0 -13
- flowfile/web/static/assets/PivotValidation-c46cd420.css +0 -13
- flowfile/web/static/assets/SliderInput-b8fb6a8c.css +0 -4
- flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +0 -13
- flowfile/web/static/assets/nodeInput-5d0d6b79.js +0 -41
- flowfile/web/static/assets/outputCsv-9cc59e0b.css +0 -2499
- flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
- flowfile/web/static/assets/secretApi-68435402.js +0 -46
- flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
- flowfile-0.5.1.dist-info/RECORD +0 -388
- {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +0 -0
- {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +0 -0
- {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,22 +1,34 @@
|
|
|
1
|
-
from
|
|
2
|
-
from flowfile_core.configs import logger
|
|
3
|
-
from flowfile_core.flowfile.flow_data_engine.flow_file_column.main import FlowfileColumn
|
|
4
|
-
from flowfile_core.flowfile.flow_data_engine.flow_data_engine import FlowDataEngine
|
|
5
|
-
from flowfile_core.utils.arrow_reader import get_read_top_n
|
|
6
|
-
from flowfile_core.schemas import input_schema, schemas
|
|
7
|
-
from flowfile_core.configs.flow_logger import NodeLogger
|
|
8
|
-
|
|
9
|
-
from flowfile_core.schemas.output_model import TableExample, FileColumn, NodeData
|
|
10
|
-
from flowfile_core.flowfile.utils import get_hash
|
|
11
|
-
from flowfile_core.configs import node_store
|
|
12
|
-
from flowfile_core.flowfile.setting_generator import setting_generator, setting_updator
|
|
1
|
+
from collections.abc import Callable, Generator
|
|
13
2
|
from time import sleep
|
|
3
|
+
from typing import Any, Literal, Optional
|
|
4
|
+
|
|
5
|
+
from flowfile_core.configs import logger, node_store
|
|
6
|
+
from flowfile_core.configs.flow_logger import NodeLogger
|
|
7
|
+
from flowfile_core.flowfile.flow_data_engine.flow_data_engine import FlowDataEngine
|
|
8
|
+
from flowfile_core.flowfile.flow_data_engine.flow_file_column.main import FlowfileColumn
|
|
14
9
|
from flowfile_core.flowfile.flow_data_engine.subprocess_operations import (
|
|
15
|
-
|
|
16
|
-
ExternalDatabaseFetcher,
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
ExternalCloudWriter,
|
|
11
|
+
ExternalDatabaseFetcher,
|
|
12
|
+
ExternalDatabaseWriter,
|
|
13
|
+
ExternalDfFetcher,
|
|
14
|
+
ExternalSampler,
|
|
15
|
+
clear_task_from_worker,
|
|
16
|
+
get_external_df_result,
|
|
17
|
+
results_exists,
|
|
18
|
+
)
|
|
19
|
+
from flowfile_core.flowfile.flow_node.models import (
|
|
20
|
+
NodeResults,
|
|
21
|
+
NodeSchemaInformation,
|
|
22
|
+
NodeStepInputs,
|
|
23
|
+
NodeStepSettings,
|
|
24
|
+
NodeStepStats,
|
|
25
|
+
)
|
|
19
26
|
from flowfile_core.flowfile.flow_node.schema_callback import SingleExecutionFuture
|
|
27
|
+
from flowfile_core.flowfile.setting_generator import setting_generator, setting_updator
|
|
28
|
+
from flowfile_core.flowfile.utils import get_hash
|
|
29
|
+
from flowfile_core.schemas import input_schema, schemas
|
|
30
|
+
from flowfile_core.schemas.output_model import FileColumn, NodeData, TableExample
|
|
31
|
+
from flowfile_core.utils.arrow_reader import get_read_top_n
|
|
20
32
|
|
|
21
33
|
|
|
22
34
|
class FlowNode:
|
|
@@ -25,6 +37,7 @@ class FlowNode:
|
|
|
25
37
|
This class manages the node's state, its data processing function,
|
|
26
38
|
and its connections to other nodes within the graph.
|
|
27
39
|
"""
|
|
40
|
+
|
|
28
41
|
parent_uuid: str
|
|
29
42
|
node_type: str
|
|
30
43
|
node_template: node_store.NodeTemplate
|
|
@@ -34,31 +47,38 @@ class FlowNode:
|
|
|
34
47
|
node_stats: NodeStepStats
|
|
35
48
|
node_settings: NodeStepSettings
|
|
36
49
|
results: NodeResults
|
|
37
|
-
node_information:
|
|
38
|
-
leads_to_nodes:
|
|
39
|
-
user_provided_schema_callback:
|
|
50
|
+
node_information: schemas.NodeInformation | None = None
|
|
51
|
+
leads_to_nodes: list["FlowNode"] = [] # list with target flows, after execution the step will trigger those step(s)
|
|
52
|
+
user_provided_schema_callback: Callable | None = None # user provided callback function for schema calculation
|
|
40
53
|
_setting_input: Any = None
|
|
41
|
-
_hash:
|
|
54
|
+
_hash: str | None = None # host this for caching results
|
|
42
55
|
_function: Callable = None # the function that needs to be executed when triggered
|
|
43
56
|
_name: str = None # name of the node, used for display
|
|
44
|
-
_schema_callback:
|
|
57
|
+
_schema_callback: SingleExecutionFuture | None = None # Function that calculates the schema without executing
|
|
45
58
|
_state_needs_reset: bool = False
|
|
46
|
-
_fetch_cached_df:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
_fetch_cached_df: (
|
|
60
|
+
ExternalDfFetcher | ExternalDatabaseFetcher | ExternalDatabaseWriter | ExternalCloudWriter | None
|
|
61
|
+
) = None
|
|
62
|
+
_cache_progress: (
|
|
63
|
+
ExternalDfFetcher | ExternalDatabaseFetcher | ExternalDatabaseWriter | ExternalCloudWriter | None
|
|
64
|
+
) = None
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
node_id: str | int,
|
|
69
|
+
function: Callable,
|
|
70
|
+
parent_uuid: str,
|
|
71
|
+
setting_input: Any,
|
|
72
|
+
name: str,
|
|
73
|
+
node_type: str,
|
|
74
|
+
input_columns: list[str] = None,
|
|
75
|
+
output_schema: list[FlowfileColumn] = None,
|
|
76
|
+
drop_columns: list[str] = None,
|
|
77
|
+
renew_schema: bool = True,
|
|
78
|
+
pos_x: float = 0,
|
|
79
|
+
pos_y: float = 0,
|
|
80
|
+
schema_callback: Callable = None,
|
|
81
|
+
):
|
|
62
82
|
"""Initializes a FlowNode instance.
|
|
63
83
|
|
|
64
84
|
Args:
|
|
@@ -83,16 +103,17 @@ class FlowNode:
|
|
|
83
103
|
self.node_information.id = node_id
|
|
84
104
|
self.node_type = node_type
|
|
85
105
|
self.node_settings.renew_schema = renew_schema
|
|
86
|
-
self.update_node(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
self.update_node(
|
|
107
|
+
function=function,
|
|
108
|
+
input_columns=input_columns,
|
|
109
|
+
output_schema=output_schema,
|
|
110
|
+
drop_columns=drop_columns,
|
|
111
|
+
setting_input=setting_input,
|
|
112
|
+
name=name,
|
|
113
|
+
pos_x=pos_x,
|
|
114
|
+
pos_y=pos_y,
|
|
115
|
+
schema_callback=schema_callback,
|
|
116
|
+
)
|
|
96
117
|
|
|
97
118
|
def post_init(self):
|
|
98
119
|
"""Initializes or resets the node's attributes to their default states."""
|
|
@@ -127,7 +148,7 @@ class FlowNode:
|
|
|
127
148
|
self._state_needs_reset = v
|
|
128
149
|
|
|
129
150
|
@staticmethod
|
|
130
|
-
def create_schema_callback_from_function(f: Callable) -> Callable[[],
|
|
151
|
+
def create_schema_callback_from_function(f: Callable) -> Callable[[], list[FlowfileColumn]]:
|
|
131
152
|
"""Wraps a node's function to create a schema callback that extracts the schema.
|
|
132
153
|
|
|
133
154
|
Args:
|
|
@@ -136,13 +157,15 @@ class FlowNode:
|
|
|
136
157
|
Returns:
|
|
137
158
|
A callable that, when executed, returns the output schema.
|
|
138
159
|
"""
|
|
139
|
-
|
|
160
|
+
|
|
161
|
+
def schema_callback() -> list[FlowfileColumn]:
|
|
140
162
|
try:
|
|
141
|
-
logger.info(
|
|
163
|
+
logger.info("Executing the schema callback function based on the node function")
|
|
142
164
|
return f().schema
|
|
143
165
|
except Exception as e:
|
|
144
|
-
logger.warning(f
|
|
166
|
+
logger.warning(f"Error with the schema callback: {e}")
|
|
145
167
|
return []
|
|
168
|
+
|
|
146
169
|
return schema_callback
|
|
147
170
|
|
|
148
171
|
@property
|
|
@@ -171,7 +194,7 @@ class FlowNode:
|
|
|
171
194
|
if f is None:
|
|
172
195
|
return
|
|
173
196
|
|
|
174
|
-
def error_callback(e: Exception) ->
|
|
197
|
+
def error_callback(e: Exception) -> list:
|
|
175
198
|
logger.warning(e)
|
|
176
199
|
|
|
177
200
|
self.node_settings.setup_errors = True
|
|
@@ -190,7 +213,7 @@ class FlowNode:
|
|
|
190
213
|
"""
|
|
191
214
|
return not self.has_input and self.node_template.input == 0
|
|
192
215
|
|
|
193
|
-
def get_input_type(self, node_id: int) ->
|
|
216
|
+
def get_input_type(self, node_id: int) -> list:
|
|
194
217
|
"""Gets the type of connection ('main', 'left', 'right') for a given input node ID.
|
|
195
218
|
|
|
196
219
|
Args:
|
|
@@ -201,24 +224,25 @@ class FlowNode:
|
|
|
201
224
|
"""
|
|
202
225
|
relation_type = []
|
|
203
226
|
if node_id in [n.node_id for n in self.node_inputs.main_inputs]:
|
|
204
|
-
relation_type.append(
|
|
227
|
+
relation_type.append("main")
|
|
205
228
|
if self.node_inputs.left_input is not None and node_id == self.node_inputs.left_input.node_id:
|
|
206
|
-
relation_type.append(
|
|
229
|
+
relation_type.append("left")
|
|
207
230
|
if self.node_inputs.right_input is not None and node_id == self.node_inputs.right_input.node_id:
|
|
208
|
-
relation_type.append(
|
|
231
|
+
relation_type.append("right")
|
|
209
232
|
return list(set(relation_type))
|
|
210
233
|
|
|
211
|
-
def update_node(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
234
|
+
def update_node(
|
|
235
|
+
self,
|
|
236
|
+
function: Callable,
|
|
237
|
+
input_columns: list[str] = None,
|
|
238
|
+
output_schema: list[FlowfileColumn] = None,
|
|
239
|
+
drop_columns: list[str] = None,
|
|
240
|
+
name: str = None,
|
|
241
|
+
setting_input: Any = None,
|
|
242
|
+
pos_x: float = 0,
|
|
243
|
+
pos_y: float = 0,
|
|
244
|
+
schema_callback: Callable = None,
|
|
245
|
+
):
|
|
222
246
|
"""Updates the properties of the node.
|
|
223
247
|
|
|
224
248
|
This is called during initialization and when settings are changed.
|
|
@@ -245,7 +269,7 @@ class FlowNode:
|
|
|
245
269
|
self.node_schema.output_columns = [] if output_schema is None else output_schema
|
|
246
270
|
self.node_schema.drop_columns = [] if drop_columns is None else drop_columns
|
|
247
271
|
self.node_settings.renew_schema = True
|
|
248
|
-
if hasattr(setting_input,
|
|
272
|
+
if hasattr(setting_input, "cache_results"):
|
|
249
273
|
self.node_settings.cache_results = setting_input.cache_results
|
|
250
274
|
|
|
251
275
|
self.results.errors = None
|
|
@@ -253,7 +277,7 @@ class FlowNode:
|
|
|
253
277
|
_ = self.hash
|
|
254
278
|
self.node_template = node_store.node_dict.get(self.node_type)
|
|
255
279
|
if self.node_template is None:
|
|
256
|
-
raise Exception(f
|
|
280
|
+
raise Exception(f"Node template {self.node_type} not found")
|
|
257
281
|
self.node_default = node_store.node_defaults.get(self.node_type)
|
|
258
282
|
self.setting_input = setting_input # wait until the end so that the hash is calculated correctly
|
|
259
283
|
|
|
@@ -292,10 +316,11 @@ class FlowNode:
|
|
|
292
316
|
Args:
|
|
293
317
|
setting_input: The new settings object.
|
|
294
318
|
"""
|
|
295
|
-
is_manual_input = (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
319
|
+
is_manual_input = (
|
|
320
|
+
self.node_type == "manual_input"
|
|
321
|
+
and isinstance(setting_input, input_schema.NodeManualInput)
|
|
322
|
+
and isinstance(self._setting_input, input_schema.NodeManualInput)
|
|
323
|
+
)
|
|
299
324
|
if is_manual_input:
|
|
300
325
|
_ = self.hash
|
|
301
326
|
self._setting_input = setting_input
|
|
@@ -309,7 +334,7 @@ class FlowNode:
|
|
|
309
334
|
self.reset()
|
|
310
335
|
|
|
311
336
|
@property
|
|
312
|
-
def node_id(self) ->
|
|
337
|
+
def node_id(self) -> str | int:
|
|
313
338
|
"""Gets the unique identifier of the node.
|
|
314
339
|
|
|
315
340
|
Returns:
|
|
@@ -336,7 +361,7 @@ class FlowNode:
|
|
|
336
361
|
return self.node_inputs.right_input
|
|
337
362
|
|
|
338
363
|
@property
|
|
339
|
-
def main_input(self) ->
|
|
364
|
+
def main_input(self) -> list["FlowNode"]:
|
|
340
365
|
"""Gets the list of nodes connected to the main input port(s).
|
|
341
366
|
|
|
342
367
|
Returns:
|
|
@@ -353,24 +378,29 @@ class FlowNode:
|
|
|
353
378
|
"""
|
|
354
379
|
if isinstance(self.setting_input, input_schema.NodePromise):
|
|
355
380
|
return False
|
|
356
|
-
return (
|
|
357
|
-
|
|
358
|
-
|
|
381
|
+
return (
|
|
382
|
+
self.node_template.input == len(self.node_inputs.get_all_inputs())
|
|
383
|
+
or (self.node_template.multi and len(self.node_inputs.get_all_inputs()) > 0)
|
|
384
|
+
or (self.node_template.multi and self.node_template.can_be_start)
|
|
385
|
+
)
|
|
359
386
|
|
|
360
387
|
def set_node_information(self):
|
|
361
388
|
"""Populates the `node_information` attribute with the current state.
|
|
362
389
|
|
|
363
390
|
This includes the node's connections, settings, and position.
|
|
364
391
|
"""
|
|
365
|
-
logger.info(
|
|
392
|
+
logger.info("setting node information")
|
|
366
393
|
node_information = self.node_information
|
|
367
394
|
node_information.left_input_id = self.node_inputs.left_input.node_id if self.left_input else None
|
|
368
395
|
node_information.right_input_id = self.node_inputs.right_input.node_id if self.right_input else None
|
|
369
|
-
node_information.input_ids =
|
|
370
|
-
|
|
396
|
+
node_information.input_ids = (
|
|
397
|
+
[mi.node_id for mi in self.node_inputs.main_inputs] if self.node_inputs.main_inputs is not None else None
|
|
398
|
+
)
|
|
371
399
|
node_information.setting_input = self.setting_input
|
|
372
400
|
node_information.outputs = [n.node_id for n in self.leads_to_nodes]
|
|
373
|
-
node_information.description =
|
|
401
|
+
node_information.description = (
|
|
402
|
+
self.setting_input.description if hasattr(self.setting_input, "description") else ""
|
|
403
|
+
)
|
|
374
404
|
node_information.is_setup = self.is_setup
|
|
375
405
|
node_information.x_position = self.setting_input.pos_x
|
|
376
406
|
node_information.y_position = self.setting_input.pos_y
|
|
@@ -404,7 +434,7 @@ class FlowNode:
|
|
|
404
434
|
self._function = function
|
|
405
435
|
|
|
406
436
|
@property
|
|
407
|
-
def all_inputs(self) ->
|
|
437
|
+
def all_inputs(self) -> list["FlowNode"]:
|
|
408
438
|
"""Gets a list of all nodes connected to any input port.
|
|
409
439
|
|
|
410
440
|
Returns:
|
|
@@ -436,8 +466,9 @@ class FlowNode:
|
|
|
436
466
|
self._hash = self.calculate_hash(self.setting_input)
|
|
437
467
|
return self._hash
|
|
438
468
|
|
|
439
|
-
def add_node_connection(
|
|
440
|
-
|
|
469
|
+
def add_node_connection(
|
|
470
|
+
self, from_node: "FlowNode", insert_type: Literal["main", "left", "right"] = "main"
|
|
471
|
+
) -> None:
|
|
441
472
|
"""Adds a connection from a source node to this node.
|
|
442
473
|
|
|
443
474
|
Args:
|
|
@@ -448,19 +479,19 @@ class FlowNode:
|
|
|
448
479
|
Exception: If the insert_type is invalid.
|
|
449
480
|
"""
|
|
450
481
|
from_node.leads_to_nodes.append(self)
|
|
451
|
-
if insert_type ==
|
|
482
|
+
if insert_type == "main":
|
|
452
483
|
if self.node_template.input <= 2 or self.node_inputs.main_inputs is None:
|
|
453
484
|
self.node_inputs.main_inputs = [from_node]
|
|
454
485
|
else:
|
|
455
486
|
self.node_inputs.main_inputs.append(from_node)
|
|
456
|
-
elif insert_type ==
|
|
487
|
+
elif insert_type == "right":
|
|
457
488
|
self.node_inputs.right_input = from_node
|
|
458
|
-
elif insert_type ==
|
|
489
|
+
elif insert_type == "left":
|
|
459
490
|
self.node_inputs.left_input = from_node
|
|
460
491
|
else:
|
|
461
|
-
raise Exception(
|
|
492
|
+
raise Exception("Cannot find the connection")
|
|
462
493
|
if self.setting_input.is_setup:
|
|
463
|
-
if hasattr(self.setting_input,
|
|
494
|
+
if hasattr(self.setting_input, "depending_on_id") and insert_type == "main":
|
|
464
495
|
self.setting_input.depending_on_id = from_node.node_id
|
|
465
496
|
self.reset()
|
|
466
497
|
from_node.reset()
|
|
@@ -472,7 +503,7 @@ class FlowNode:
|
|
|
472
503
|
deep: If True, the reset propagates recursively through the entire downstream graph.
|
|
473
504
|
"""
|
|
474
505
|
for node in self.leads_to_nodes:
|
|
475
|
-
self.print(f
|
|
506
|
+
self.print(f"resetting node: {node.node_id}")
|
|
476
507
|
node.reset(deep)
|
|
477
508
|
|
|
478
509
|
def get_flow_file_column_schema(self, col_name: str) -> FlowfileColumn | None:
|
|
@@ -488,7 +519,7 @@ class FlowNode:
|
|
|
488
519
|
if s.column_name == col_name:
|
|
489
520
|
return s
|
|
490
521
|
|
|
491
|
-
def get_predicted_schema(self, force: bool = False) ->
|
|
522
|
+
def get_predicted_schema(self, force: bool = False) -> list[FlowfileColumn] | None:
|
|
492
523
|
"""Predicts the output schema of the node without full execution.
|
|
493
524
|
|
|
494
525
|
It uses the schema_callback or infers from predicted data.
|
|
@@ -503,18 +534,18 @@ class FlowNode:
|
|
|
503
534
|
if self.node_schema.predicted_schema and not force:
|
|
504
535
|
return self.node_schema.predicted_schema
|
|
505
536
|
if self.schema_callback is not None and (self.node_schema.predicted_schema is None or force):
|
|
506
|
-
self.print(
|
|
537
|
+
self.print("Getting the data from a schema callback")
|
|
507
538
|
if force:
|
|
508
539
|
# Force the schema callback to reset, so that it will be executed again
|
|
509
540
|
self.schema_callback.reset()
|
|
510
541
|
schema = self.schema_callback()
|
|
511
542
|
if schema is not None and len(schema) > 0:
|
|
512
|
-
self.print(
|
|
543
|
+
self.print("Calculating the schema based on the schema callback")
|
|
513
544
|
self.node_schema.predicted_schema = schema
|
|
514
545
|
return self.node_schema.predicted_schema
|
|
515
546
|
predicted_data = self._predicted_data_getter()
|
|
516
547
|
if predicted_data is not None and predicted_data.schema is not None:
|
|
517
|
-
self.print(
|
|
548
|
+
self.print("Calculating the schema based on the predicted resulting data")
|
|
518
549
|
self.node_schema.predicted_schema = self._predicted_data_getter().schema
|
|
519
550
|
|
|
520
551
|
return self.node_schema.predicted_schema
|
|
@@ -527,7 +558,7 @@ class FlowNode:
|
|
|
527
558
|
True if the node is set up, False otherwise.
|
|
528
559
|
"""
|
|
529
560
|
if not self.node_information.is_setup:
|
|
530
|
-
if self.function.__name__ !=
|
|
561
|
+
if self.function.__name__ != "placeholder":
|
|
531
562
|
self.node_information.is_setup = True
|
|
532
563
|
self.setting_input.is_setup = True
|
|
533
564
|
return self.node_information.is_setup
|
|
@@ -538,7 +569,7 @@ class FlowNode:
|
|
|
538
569
|
Args:
|
|
539
570
|
v: The message or value to log.
|
|
540
571
|
"""
|
|
541
|
-
logger.info(f
|
|
572
|
+
logger.info(f"{self.node_type}, node_id: {self.node_id}: {v}")
|
|
542
573
|
|
|
543
574
|
def get_resulting_data(self) -> FlowDataEngine | None:
|
|
544
575
|
"""Executes the node's function to produce the actual output data.
|
|
@@ -553,11 +584,11 @@ class FlowNode:
|
|
|
553
584
|
"""
|
|
554
585
|
if self.is_setup:
|
|
555
586
|
if self.results.resulting_data is None and self.results.errors is None:
|
|
556
|
-
self.print(
|
|
587
|
+
self.print("getting resulting data")
|
|
557
588
|
try:
|
|
558
589
|
if isinstance(self.function, FlowDataEngine):
|
|
559
590
|
fl: FlowDataEngine = self.function
|
|
560
|
-
elif self.node_type ==
|
|
591
|
+
elif self.node_type == "external_source":
|
|
561
592
|
fl: FlowDataEngine = self.function()
|
|
562
593
|
fl.collect_external()
|
|
563
594
|
self.node_settings.streamable = False
|
|
@@ -590,14 +621,14 @@ class FlowNode:
|
|
|
590
621
|
return fl
|
|
591
622
|
except ValueError as e:
|
|
592
623
|
if str(e) == "generator already executing":
|
|
593
|
-
logger.info(
|
|
624
|
+
logger.info("Generator already executing, waiting for the result")
|
|
594
625
|
sleep(1)
|
|
595
626
|
return self._predicted_data_getter()
|
|
596
627
|
fl = FlowDataEngine()
|
|
597
628
|
return fl
|
|
598
629
|
|
|
599
630
|
except Exception as e:
|
|
600
|
-
logger.warning(
|
|
631
|
+
logger.warning("there was an issue with the function, returning an empty Flowfile")
|
|
601
632
|
logger.warning(e)
|
|
602
633
|
|
|
603
634
|
def get_predicted_resulting_data(self) -> FlowDataEngine:
|
|
@@ -609,7 +640,7 @@ class FlowNode:
|
|
|
609
640
|
A FlowDataEngine instance with a schema but no data.
|
|
610
641
|
"""
|
|
611
642
|
if self.needs_run(False) and self.schema_callback is not None or self.node_schema.result_schema is not None:
|
|
612
|
-
self.print(
|
|
643
|
+
self.print("Getting data based on the schema")
|
|
613
644
|
|
|
614
645
|
_s = self.schema_callback() if self.node_schema.result_schema is None else self.node_schema.result_schema
|
|
615
646
|
return FlowDataEngine.create_from_schema(_s)
|
|
@@ -649,7 +680,7 @@ class FlowNode:
|
|
|
649
680
|
yield n
|
|
650
681
|
|
|
651
682
|
@property
|
|
652
|
-
def schema(self) ->
|
|
683
|
+
def schema(self) -> list[FlowfileColumn]:
|
|
653
684
|
"""Gets the definitive output schema of the node.
|
|
654
685
|
|
|
655
686
|
If not already run, it falls back to the predicted schema.
|
|
@@ -661,7 +692,7 @@ class FlowNode:
|
|
|
661
692
|
if self.is_setup and self.results.errors is None:
|
|
662
693
|
if self.node_schema.result_schema is not None and len(self.node_schema.result_schema) > 0:
|
|
663
694
|
return self.node_schema.result_schema
|
|
664
|
-
elif self.node_type ==
|
|
695
|
+
elif self.node_type == "output":
|
|
665
696
|
if len(self.node_inputs.main_inputs) > 0:
|
|
666
697
|
self.node_schema.result_schema = self.node_inputs.main_inputs[0].schema
|
|
667
698
|
else:
|
|
@@ -680,11 +711,15 @@ class FlowNode:
|
|
|
680
711
|
"""
|
|
681
712
|
|
|
682
713
|
if results_exists(self.hash):
|
|
683
|
-
logger.warning(
|
|
714
|
+
logger.warning("Not implemented")
|
|
684
715
|
clear_task_from_worker(self.hash)
|
|
685
716
|
|
|
686
|
-
def needs_run(
|
|
687
|
-
|
|
717
|
+
def needs_run(
|
|
718
|
+
self,
|
|
719
|
+
performance_mode: bool,
|
|
720
|
+
node_logger: NodeLogger = None,
|
|
721
|
+
execution_location: schemas.ExecutionLocationsLiteral = "remote",
|
|
722
|
+
) -> bool:
|
|
688
723
|
"""Determines if the node needs to be executed.
|
|
689
724
|
|
|
690
725
|
The decision is based on its run state, caching settings, and execution mode.
|
|
@@ -703,7 +738,7 @@ class FlowNode:
|
|
|
703
738
|
flow_logger = logger if node_logger is None else node_logger
|
|
704
739
|
cache_result_exists = results_exists(self.hash)
|
|
705
740
|
if not self.node_stats.has_run_with_current_setup:
|
|
706
|
-
flow_logger.info(
|
|
741
|
+
flow_logger.info("Node has not run, needs to run")
|
|
707
742
|
return True
|
|
708
743
|
if self.node_settings.cache_results and cache_result_exists:
|
|
709
744
|
return False
|
|
@@ -737,7 +772,9 @@ class FlowNode:
|
|
|
737
772
|
if example_data is None:
|
|
738
773
|
example_data = resulting_data.get_sample(100).to_arrow()
|
|
739
774
|
return example_data
|
|
775
|
+
|
|
740
776
|
return get_example_data
|
|
777
|
+
|
|
741
778
|
resulting_data = self.get_resulting_data()
|
|
742
779
|
|
|
743
780
|
if not performance_mode:
|
|
@@ -759,8 +796,13 @@ class FlowNode:
|
|
|
759
796
|
try:
|
|
760
797
|
resulting_data = self.get_resulting_data()
|
|
761
798
|
if not performance_mode:
|
|
762
|
-
external_sampler = ExternalSampler(
|
|
763
|
-
|
|
799
|
+
external_sampler = ExternalSampler(
|
|
800
|
+
lf=resulting_data.data_frame,
|
|
801
|
+
file_ref=self.hash,
|
|
802
|
+
wait_on_completion=True,
|
|
803
|
+
node_id=self.node_id,
|
|
804
|
+
flow_id=flow_id,
|
|
805
|
+
)
|
|
764
806
|
self.store_example_data_generator(external_sampler)
|
|
765
807
|
if self.results.errors is None and not self.node_stats.is_canceled:
|
|
766
808
|
self.node_stats.has_run_with_current_setup = True
|
|
@@ -790,48 +832,58 @@ class FlowNode:
|
|
|
790
832
|
Exception: If the node_logger is not provided or if execution fails.
|
|
791
833
|
"""
|
|
792
834
|
if node_logger is None:
|
|
793
|
-
raise Exception(
|
|
835
|
+
raise Exception("Node logger is not defined")
|
|
794
836
|
if self.node_settings.cache_results and results_exists(self.hash):
|
|
795
837
|
try:
|
|
796
838
|
self.results.resulting_data = get_external_df_result(self.hash)
|
|
797
839
|
self._cache_progress = None
|
|
798
840
|
return
|
|
799
|
-
except Exception
|
|
800
|
-
node_logger.warning(
|
|
801
|
-
if self.node_type ==
|
|
841
|
+
except Exception:
|
|
842
|
+
node_logger.warning("Failed to read the cache, rerunning the code")
|
|
843
|
+
if self.node_type == "output":
|
|
802
844
|
self.results.resulting_data = self.get_resulting_data()
|
|
803
845
|
self.node_stats.has_run_with_current_setup = True
|
|
804
846
|
return
|
|
805
847
|
try:
|
|
806
848
|
self.get_resulting_data()
|
|
807
849
|
except Exception as e:
|
|
808
|
-
self.results.errors =
|
|
850
|
+
self.results.errors = "Error with creating the lazy frame, most likely due to invalid graph"
|
|
809
851
|
raise e
|
|
810
852
|
if not performance_mode:
|
|
811
|
-
external_df_fetcher = ExternalDfFetcher(
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
853
|
+
external_df_fetcher = ExternalDfFetcher(
|
|
854
|
+
lf=self.get_resulting_data().data_frame,
|
|
855
|
+
file_ref=self.hash,
|
|
856
|
+
wait_on_completion=False,
|
|
857
|
+
flow_id=node_logger.flow_id,
|
|
858
|
+
node_id=self.node_id,
|
|
859
|
+
)
|
|
815
860
|
self._fetch_cached_df = external_df_fetcher
|
|
816
861
|
try:
|
|
817
862
|
lf = external_df_fetcher.get_result()
|
|
818
863
|
self.results.resulting_data = FlowDataEngine(
|
|
819
|
-
lf,
|
|
820
|
-
|
|
864
|
+
lf,
|
|
865
|
+
number_of_records=ExternalDfFetcher(
|
|
866
|
+
lf=lf,
|
|
867
|
+
operation_type="calculate_number_of_records",
|
|
868
|
+
flow_id=node_logger.flow_id,
|
|
869
|
+
node_id=self.node_id,
|
|
870
|
+
).result,
|
|
821
871
|
)
|
|
822
872
|
if not performance_mode:
|
|
823
873
|
self.store_example_data_generator(external_df_fetcher)
|
|
824
874
|
self.node_stats.has_run_with_current_setup = True
|
|
825
875
|
|
|
826
876
|
except Exception as e:
|
|
827
|
-
node_logger.error(
|
|
877
|
+
node_logger.error("Error with external process")
|
|
828
878
|
if external_df_fetcher.error_code == -1:
|
|
829
879
|
try:
|
|
830
880
|
self.results.resulting_data = self.get_resulting_data()
|
|
831
|
-
self.results.warnings = (
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
881
|
+
self.results.warnings = (
|
|
882
|
+
"Error with external process (unknown error), "
|
|
883
|
+
"likely the process was killed by the server because of memory constraints, "
|
|
884
|
+
"continue with the process. "
|
|
885
|
+
"We cannot display example data..."
|
|
886
|
+
)
|
|
835
887
|
except Exception as e:
|
|
836
888
|
self.results.errors = str(e)
|
|
837
889
|
raise e
|
|
@@ -858,15 +910,18 @@ class FlowNode:
|
|
|
858
910
|
self._fetch_cached_df.cancel()
|
|
859
911
|
self.node_stats.is_canceled = True
|
|
860
912
|
else:
|
|
861
|
-
logger.warning(
|
|
913
|
+
logger.warning("No external process to cancel")
|
|
862
914
|
self.node_stats.is_canceled = True
|
|
863
915
|
|
|
864
|
-
def execute_node(
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
916
|
+
def execute_node(
|
|
917
|
+
self,
|
|
918
|
+
run_location: schemas.ExecutionLocationsLiteral,
|
|
919
|
+
reset_cache: bool = False,
|
|
920
|
+
performance_mode: bool = False,
|
|
921
|
+
retry: bool = True,
|
|
922
|
+
node_logger: NodeLogger = None,
|
|
923
|
+
optimize_for_downstream: bool = True,
|
|
924
|
+
):
|
|
870
925
|
"""Orchestrates the execution, handling location, caching, and retries.
|
|
871
926
|
|
|
872
927
|
Args:
|
|
@@ -882,7 +937,7 @@ class FlowNode:
|
|
|
882
937
|
Exception: If the node_logger is not defined.
|
|
883
938
|
"""
|
|
884
939
|
if node_logger is None:
|
|
885
|
-
raise Exception(
|
|
940
|
+
raise Exception("Flow logger is not defined")
|
|
886
941
|
# TODO: Simplify which route is being picked there are many duplicate checks
|
|
887
942
|
|
|
888
943
|
if reset_cache:
|
|
@@ -891,63 +946,78 @@ class FlowNode:
|
|
|
891
946
|
self.node_stats.has_completed_last_run = False
|
|
892
947
|
|
|
893
948
|
if self.is_setup:
|
|
894
|
-
node_logger.info(f
|
|
895
|
-
if (
|
|
896
|
-
|
|
949
|
+
node_logger.info(f"Starting to run {self.__name__}")
|
|
950
|
+
if (
|
|
951
|
+
self.needs_run(performance_mode, node_logger, run_location)
|
|
952
|
+
or self.node_template.node_group == "output"
|
|
953
|
+
and not (run_location == "local")
|
|
954
|
+
):
|
|
897
955
|
self.clear_table_example()
|
|
898
956
|
self.prepare_before_run()
|
|
899
957
|
self.reset()
|
|
900
958
|
try:
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
959
|
+
if (
|
|
960
|
+
run_location == "remote"
|
|
961
|
+
or (self.node_default.transform_type == "wide" and optimize_for_downstream)
|
|
962
|
+
and not run_location == "local"
|
|
963
|
+
) or self.node_settings.cache_results:
|
|
964
|
+
node_logger.info("Running the node remotely")
|
|
906
965
|
if self.node_settings.cache_results:
|
|
907
966
|
performance_mode = False
|
|
908
|
-
self.execute_remote(
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
967
|
+
self.execute_remote(
|
|
968
|
+
performance_mode=(performance_mode if not self.node_settings.cache_results else False),
|
|
969
|
+
node_logger=node_logger,
|
|
970
|
+
)
|
|
912
971
|
else:
|
|
913
|
-
node_logger.info(
|
|
972
|
+
node_logger.info("Running the node locally")
|
|
914
973
|
self.execute_local(performance_mode=performance_mode, flow_id=node_logger.flow_id)
|
|
915
974
|
except Exception as e:
|
|
916
|
-
if
|
|
917
|
-
logger.warning(
|
|
918
|
-
all_inputs:
|
|
975
|
+
if "No such file or directory (os error" in str(e) and retry:
|
|
976
|
+
logger.warning("Error with the input node, starting to rerun the input node...")
|
|
977
|
+
all_inputs: list[FlowNode] = self.node_inputs.get_all_inputs()
|
|
919
978
|
for node_input in all_inputs:
|
|
920
|
-
node_input.execute_node(
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
979
|
+
node_input.execute_node(
|
|
980
|
+
run_location=run_location,
|
|
981
|
+
performance_mode=performance_mode,
|
|
982
|
+
retry=True,
|
|
983
|
+
reset_cache=True,
|
|
984
|
+
node_logger=node_logger,
|
|
985
|
+
)
|
|
986
|
+
self.execute_node(
|
|
987
|
+
run_location=run_location,
|
|
988
|
+
performance_mode=performance_mode,
|
|
989
|
+
retry=False,
|
|
990
|
+
node_logger=node_logger,
|
|
991
|
+
)
|
|
927
992
|
else:
|
|
928
993
|
self.results.errors = str(e)
|
|
929
994
|
if "Connection refused" in str(e) and "/submit_query/" in str(e):
|
|
930
|
-
node_logger.warning(
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
995
|
+
node_logger.warning(
|
|
996
|
+
"There was an issue connecting to the remote worker, "
|
|
997
|
+
"ensure the worker process is running, "
|
|
998
|
+
"or change the settings to, so it executes locally"
|
|
999
|
+
)
|
|
1000
|
+
node_logger.error(
|
|
1001
|
+
"Could not execute in the remote worker. (Re)start the worker service, or change settings to local settings."
|
|
1002
|
+
)
|
|
934
1003
|
else:
|
|
935
|
-
node_logger.error(f
|
|
936
|
-
elif (
|
|
937
|
-
|
|
1004
|
+
node_logger.error(f"Error with running the node: {e}")
|
|
1005
|
+
elif (run_location == "local") and (
|
|
1006
|
+
not self.node_stats.has_run_with_current_setup or self.node_template.node_group == "output"
|
|
1007
|
+
):
|
|
938
1008
|
try:
|
|
939
|
-
node_logger.info(
|
|
1009
|
+
node_logger.info("Executing fully locally")
|
|
940
1010
|
self.execute_full_local(performance_mode)
|
|
941
1011
|
except Exception as e:
|
|
942
1012
|
self.results.errors = str(e)
|
|
943
|
-
node_logger.error(f
|
|
1013
|
+
node_logger.error(f"Error with running the node: {e}")
|
|
944
1014
|
self.node_stats.error = str(e)
|
|
945
1015
|
self.node_stats.has_completed_last_run = False
|
|
946
1016
|
|
|
947
1017
|
else:
|
|
948
|
-
node_logger.info(
|
|
1018
|
+
node_logger.info("Node has already run, not running the node")
|
|
949
1019
|
else:
|
|
950
|
-
node_logger.warning(f
|
|
1020
|
+
node_logger.warning(f"Node {self.__name__} is not setup, cannot run the node")
|
|
951
1021
|
|
|
952
1022
|
def store_example_data_generator(self, external_df_fetcher: ExternalDfFetcher | ExternalSampler):
|
|
953
1023
|
"""Stores a generator function for fetching a sample of the result data.
|
|
@@ -960,7 +1030,7 @@ class FlowNode:
|
|
|
960
1030
|
self.results.example_data_path = file_ref
|
|
961
1031
|
self.results.example_data_generator = get_read_top_n(file_path=file_ref, n=100)
|
|
962
1032
|
else:
|
|
963
|
-
logger.error(
|
|
1033
|
+
logger.error("Could not get the sample data, the external process is not ready")
|
|
964
1034
|
|
|
965
1035
|
def needs_reset(self) -> bool:
|
|
966
1036
|
"""Checks if the node's hash has changed, indicating an outdated state.
|
|
@@ -980,7 +1050,7 @@ class FlowNode:
|
|
|
980
1050
|
"""
|
|
981
1051
|
needs_reset = self.needs_reset() or deep
|
|
982
1052
|
if needs_reset:
|
|
983
|
-
logger.info(f
|
|
1053
|
+
logger.info(f"{self.node_id}: Node needs reset")
|
|
984
1054
|
self.node_stats.has_run_with_current_setup = False
|
|
985
1055
|
self.results.reset()
|
|
986
1056
|
self.node_schema.result_schema = None
|
|
@@ -991,7 +1061,7 @@ class FlowNode:
|
|
|
991
1061
|
if self.is_correct:
|
|
992
1062
|
self._schema_callback = None # Ensure the schema callback is reset
|
|
993
1063
|
if self.schema_callback:
|
|
994
|
-
logger.info(f
|
|
1064
|
+
logger.info(f"{self.node_id}: Resetting the schema callback")
|
|
995
1065
|
self.schema_callback.start()
|
|
996
1066
|
self.evaluate_nodes()
|
|
997
1067
|
_ = self.hash # Recalculate the hash after reset
|
|
@@ -1005,17 +1075,18 @@ class FlowNode:
|
|
|
1005
1075
|
Returns:
|
|
1006
1076
|
True if the connection was found and removed, False otherwise.
|
|
1007
1077
|
"""
|
|
1008
|
-
logger.info(f
|
|
1078
|
+
logger.info(f"Deleting lead to node: {node_id}")
|
|
1009
1079
|
for i, lead_to_node in enumerate(self.leads_to_nodes):
|
|
1010
|
-
logger.info(f
|
|
1080
|
+
logger.info(f"Checking lead to node: {lead_to_node.node_id}")
|
|
1011
1081
|
if lead_to_node.node_id == node_id:
|
|
1012
|
-
logger.info(f
|
|
1082
|
+
logger.info(f"Found the node to delete: {node_id}")
|
|
1013
1083
|
self.leads_to_nodes.pop(i)
|
|
1014
1084
|
return True
|
|
1015
1085
|
return False
|
|
1016
1086
|
|
|
1017
|
-
def delete_input_node(
|
|
1018
|
-
|
|
1087
|
+
def delete_input_node(
|
|
1088
|
+
self, node_id: int, connection_type: input_schema.InputConnectionClass = "input-0", complete: bool = False
|
|
1089
|
+
) -> bool:
|
|
1019
1090
|
"""Removes a connection from a specific input node.
|
|
1020
1091
|
|
|
1021
1092
|
Args:
|
|
@@ -1027,23 +1098,23 @@ class FlowNode:
|
|
|
1027
1098
|
True if a connection was found and removed, False otherwise.
|
|
1028
1099
|
"""
|
|
1029
1100
|
deleted: bool = False
|
|
1030
|
-
if connection_type ==
|
|
1101
|
+
if connection_type == "input-0":
|
|
1031
1102
|
for i, node in enumerate(self.node_inputs.main_inputs):
|
|
1032
1103
|
if node.node_id == node_id:
|
|
1033
1104
|
self.node_inputs.main_inputs.pop(i)
|
|
1034
1105
|
deleted = True
|
|
1035
1106
|
if not complete:
|
|
1036
1107
|
continue
|
|
1037
|
-
elif connection_type ==
|
|
1108
|
+
elif connection_type == "input-1" or complete:
|
|
1038
1109
|
if self.node_inputs.right_input is not None and self.node_inputs.right_input.node_id == node_id:
|
|
1039
1110
|
self.node_inputs.right_input = None
|
|
1040
1111
|
deleted = True
|
|
1041
|
-
elif connection_type ==
|
|
1112
|
+
elif connection_type == "input-2" or complete:
|
|
1042
1113
|
if self.node_inputs.left_input is not None and self.node_inputs.right_input.node_id == node_id:
|
|
1043
1114
|
self.node_inputs.left_input = None
|
|
1044
1115
|
deleted = True
|
|
1045
1116
|
else:
|
|
1046
|
-
logger.warning(
|
|
1117
|
+
logger.warning("Could not find the connection to delete...")
|
|
1047
1118
|
if deleted:
|
|
1048
1119
|
self.reset()
|
|
1049
1120
|
return deleted
|
|
@@ -1056,7 +1127,7 @@ class FlowNode:
|
|
|
1056
1127
|
"""
|
|
1057
1128
|
return f"Node id: {self.node_id} ({self.node_type})"
|
|
1058
1129
|
|
|
1059
|
-
def _get_readable_schema(self) ->
|
|
1130
|
+
def _get_readable_schema(self) -> list[dict] | None:
|
|
1060
1131
|
"""Helper to get a simplified, dictionary representation of the output schema.
|
|
1061
1132
|
|
|
1062
1133
|
Returns:
|
|
@@ -1074,11 +1145,14 @@ class FlowNode:
|
|
|
1074
1145
|
Returns:
|
|
1075
1146
|
A dictionary containing key information about the node.
|
|
1076
1147
|
"""
|
|
1077
|
-
return dict(
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1148
|
+
return dict(
|
|
1149
|
+
FlowNode=dict(
|
|
1150
|
+
node_id=self.node_id,
|
|
1151
|
+
step_name=self.__name__,
|
|
1152
|
+
output_columns=self.node_schema.output_columns,
|
|
1153
|
+
output_schema=self._get_readable_schema(),
|
|
1154
|
+
)
|
|
1155
|
+
)
|
|
1082
1156
|
|
|
1083
1157
|
@property
|
|
1084
1158
|
def number_of_leads_to_nodes(self) -> int | None:
|
|
@@ -1149,14 +1223,13 @@ class FlowNode:
|
|
|
1149
1223
|
Returns:
|
|
1150
1224
|
A `TableExample` object, or None if the node is not set up.
|
|
1151
1225
|
"""
|
|
1152
|
-
self.print(
|
|
1226
|
+
self.print("Getting a table example")
|
|
1153
1227
|
if self.is_setup and include_data and self.node_stats.has_completed_last_run:
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
self.print('getting the table example')
|
|
1228
|
+
if self.node_template.node_group == "output":
|
|
1229
|
+
self.print("getting the table example")
|
|
1157
1230
|
return self.main_input[0].get_table_example(include_data)
|
|
1158
1231
|
|
|
1159
|
-
logger.info(
|
|
1232
|
+
logger.info("getting the table example since the node has run")
|
|
1160
1233
|
example_data_getter = self.results.example_data_generator
|
|
1161
1234
|
if example_data_getter is not None:
|
|
1162
1235
|
data = example_data_getter().to_pylist()
|
|
@@ -1168,26 +1241,34 @@ class FlowNode:
|
|
|
1168
1241
|
fl = self.get_resulting_data()
|
|
1169
1242
|
has_example_data = self.results.example_data_generator is not None
|
|
1170
1243
|
|
|
1171
|
-
return TableExample(
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1244
|
+
return TableExample(
|
|
1245
|
+
node_id=self.node_id,
|
|
1246
|
+
name=str(self.node_id),
|
|
1247
|
+
number_of_records=999,
|
|
1248
|
+
number_of_columns=fl.number_of_fields,
|
|
1249
|
+
table_schema=schema,
|
|
1250
|
+
columns=fl.columns,
|
|
1251
|
+
data=data,
|
|
1252
|
+
has_example_data=has_example_data,
|
|
1253
|
+
has_run_with_current_setup=self.node_stats.has_run_with_current_setup,
|
|
1254
|
+
)
|
|
1178
1255
|
else:
|
|
1179
|
-
logger.warning(
|
|
1256
|
+
logger.warning("getting the table example but the node has not run")
|
|
1180
1257
|
try:
|
|
1181
1258
|
schema = [FileColumn.model_validate(c.get_column_repr()) for c in self.schema]
|
|
1182
1259
|
except Exception as e:
|
|
1183
1260
|
logger.warning(e)
|
|
1184
1261
|
schema = []
|
|
1185
1262
|
columns = [s.name for s in schema]
|
|
1186
|
-
return TableExample(
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1263
|
+
return TableExample(
|
|
1264
|
+
node_id=self.node_id,
|
|
1265
|
+
name=str(self.node_id),
|
|
1266
|
+
number_of_records=0,
|
|
1267
|
+
number_of_columns=len(columns),
|
|
1268
|
+
table_schema=schema,
|
|
1269
|
+
columns=columns,
|
|
1270
|
+
data=[],
|
|
1271
|
+
)
|
|
1191
1272
|
|
|
1192
1273
|
def get_node_data(self, flow_id: int, include_example: bool = False) -> NodeData:
|
|
1193
1274
|
"""Gathers all necessary data for representing the node in the UI.
|
|
@@ -1199,11 +1280,13 @@ class FlowNode:
|
|
|
1199
1280
|
Returns:
|
|
1200
1281
|
A `NodeData` object.
|
|
1201
1282
|
"""
|
|
1202
|
-
node = NodeData(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1283
|
+
node = NodeData(
|
|
1284
|
+
flow_id=flow_id,
|
|
1285
|
+
node_id=self.node_id,
|
|
1286
|
+
has_run=self.node_stats.has_run_with_current_setup,
|
|
1287
|
+
setting_input=self.setting_input,
|
|
1288
|
+
flow_type=self.node_type,
|
|
1289
|
+
)
|
|
1207
1290
|
if self.main_input:
|
|
1208
1291
|
node.main_input = self.main_input[0].get_table_example()
|
|
1209
1292
|
if self.left_input:
|
|
@@ -1215,6 +1298,9 @@ class FlowNode:
|
|
|
1215
1298
|
node = setting_generator.get_setting_generator(self.node_type)(node)
|
|
1216
1299
|
|
|
1217
1300
|
node = setting_updator.get_setting_updator(self.node_type)(node)
|
|
1301
|
+
# Save the updated settings back to the node so they persist across calls
|
|
1302
|
+
if node.setting_input is not None and not isinstance(node.setting_input, input_schema.NodePromise):
|
|
1303
|
+
self.setting_input = node.setting_input
|
|
1218
1304
|
return node
|
|
1219
1305
|
|
|
1220
1306
|
def get_output_data(self) -> TableExample:
|
|
@@ -1231,12 +1317,14 @@ class FlowNode:
|
|
|
1231
1317
|
Returns:
|
|
1232
1318
|
A `NodeInput` object.
|
|
1233
1319
|
"""
|
|
1234
|
-
return schemas.NodeInput(
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1320
|
+
return schemas.NodeInput(
|
|
1321
|
+
pos_y=self.setting_input.pos_y,
|
|
1322
|
+
pos_x=self.setting_input.pos_x,
|
|
1323
|
+
id=self.node_id,
|
|
1324
|
+
**self.node_template.__dict__,
|
|
1325
|
+
)
|
|
1326
|
+
|
|
1327
|
+
def get_edge_input(self) -> list[schemas.NodeEdge]:
|
|
1240
1328
|
"""Generates `NodeEdge` objects for all input connections to this node.
|
|
1241
1329
|
|
|
1242
1330
|
Returns:
|
|
@@ -1245,24 +1333,33 @@ class FlowNode:
|
|
|
1245
1333
|
edges = []
|
|
1246
1334
|
if self.node_inputs.main_inputs is not None:
|
|
1247
1335
|
for i, main_input in enumerate(self.node_inputs.main_inputs):
|
|
1248
|
-
edges.append(
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1336
|
+
edges.append(
|
|
1337
|
+
schemas.NodeEdge(
|
|
1338
|
+
id=f"{main_input.node_id}-{self.node_id}-{i}",
|
|
1339
|
+
source=main_input.node_id,
|
|
1340
|
+
target=self.node_id,
|
|
1341
|
+
sourceHandle="output-0",
|
|
1342
|
+
targetHandle="input-0",
|
|
1343
|
+
)
|
|
1344
|
+
)
|
|
1254
1345
|
if self.node_inputs.left_input is not None:
|
|
1255
|
-
edges.append(
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1346
|
+
edges.append(
|
|
1347
|
+
schemas.NodeEdge(
|
|
1348
|
+
id=f"{self.node_inputs.left_input.node_id}-{self.node_id}-right",
|
|
1349
|
+
source=self.node_inputs.left_input.node_id,
|
|
1350
|
+
target=self.node_id,
|
|
1351
|
+
sourceHandle="output-0",
|
|
1352
|
+
targetHandle="input-2",
|
|
1353
|
+
)
|
|
1354
|
+
)
|
|
1261
1355
|
if self.node_inputs.right_input is not None:
|
|
1262
|
-
edges.append(
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1356
|
+
edges.append(
|
|
1357
|
+
schemas.NodeEdge(
|
|
1358
|
+
id=f"{self.node_inputs.right_input.node_id}-{self.node_id}-left",
|
|
1359
|
+
source=self.node_inputs.right_input.node_id,
|
|
1360
|
+
target=self.node_id,
|
|
1361
|
+
sourceHandle="output-0",
|
|
1362
|
+
targetHandle="input-1",
|
|
1363
|
+
)
|
|
1364
|
+
)
|
|
1268
1365
|
return edges
|