Flowfile 0.4.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 +179 -73
- flowfile/__main__.py +10 -7
- flowfile/api.py +52 -59
- 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-d3248f8d.js → CloudConnectionView-f13f202b.js} +11 -11
- flowfile/web/static/assets/{CloudStorageReader-d65bf041.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-e83be3ed.js → CloudStorageWriter-8e781e11.js} +10 -8
- flowfile/web/static/assets/{ColumnSelector-47996a16.css → ColumnSelector-371637fb.css} +2 -2
- flowfile/web/static/assets/{ColumnSelector-cce661cf.js → ColumnSelector-8ad68ea9.js} +3 -5
- flowfile/web/static/assets/{ContextMenu-c13f91d0.css → ContextMenu-26d4dd27.css} +6 -6
- flowfile/web/static/assets/{ContextMenu-11a4652a.js → ContextMenu-31ee57f0.js} +3 -3
- flowfile/web/static/assets/{ContextMenu-160afb08.js → ContextMenu-69a74055.js} +3 -3
- flowfile/web/static/assets/{ContextMenu-cf18d2cc.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-d395d38c.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-b812dc0b.js → CustomNode-8479239b.js} +36 -24
- flowfile/web/static/assets/{DatabaseConnectionSettings-7000bf2c.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-4f035d0c.js → DatabaseReader-c58b9552.js} +25 -15
- flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
- flowfile/web/static/assets/{DatabaseManager-9662ec5b.js → DatabaseView-d26a9140.js} +11 -11
- flowfile/web/static/assets/{DatabaseWriter-2f570e53.css → DatabaseWriter-217a99f1.css} +19 -19
- flowfile/web/static/assets/{DatabaseWriter-f65dcd54.js → DatabaseWriter-4d05ddc7.js} +17 -10
- flowfile/web/static/assets/{designer-e3c150ec.css → DesignerView-a6d0ee84.css} +629 -538
- flowfile/web/static/assets/{designer-f3656d8c.js → DesignerView-e6f5c0e8.js} +1214 -3209
- flowfile/web/static/assets/{documentation-52b241e7.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-94c43dfc.js → ExploreData-7b54caca.js} +18 -9
- flowfile/web/static/assets/{ExternalSource-ac04b3cc.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-71472193.js → Formula-aac42b1e.js} +13 -11
- flowfile/web/static/assets/{FuzzyMatch-1010f966.css → FuzzyMatch-ad6361d6.css} +68 -69
- flowfile/web/static/assets/{FuzzyMatch-b317f631.js → FuzzyMatch-cd9bbfca.js} +12 -10
- flowfile/web/static/assets/{Pivot-cf333e3d.css → GraphSolver-c24dec17.css} +5 -5
- flowfile/web/static/assets/{GraphSolver-754a234f.js → GraphSolver-c7e6780e.js} +13 -11
- flowfile/web/static/assets/{GroupBy-6c6f9802.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-a1b800be.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-a9640276.js → ManualInput-8d3374b2.js} +170 -116
- flowfile/web/static/assets/{MultiSelect-97213888.js → MultiSelect-ad1b6243.js} +2 -2
- flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-6ffe088a.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-e638088a.js → NumericInput-7100234c.js} +2 -2
- flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-90eb2cba.js → NumericInput.vue_vue_type_script_setup_true_lang-5130219f.js} +5 -2
- flowfile/web/static/assets/{Output-ddc9079f.css → Output-35e97000.css} +6 -6
- flowfile/web/static/assets/{Output-76750610.js → Output-f5efd2aa.js} +60 -38
- flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
- flowfile/web/static/assets/{Pivot-7814803f.js → Pivot-d981d23c.js} +11 -9
- flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
- flowfile/web/static/assets/{PivotValidation-f92137d2.js → PivotValidation-39386e95.js} +3 -3
- flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
- flowfile/web/static/assets/{PivotValidation-76dd431a.js → PivotValidation-63de1f73.js} +3 -3
- flowfile/web/static/assets/{PolarsCode-650322d1.css → PolarsCode-2b1f1f23.css} +4 -4
- flowfile/web/static/assets/{PolarsCode-889c3008.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-6b17491f.css → Read-36e7bd51.css} +12 -12
- flowfile/web/static/assets/{Read-637b72a7.js → Read-aec2e377.js} +83 -105
- flowfile/web/static/assets/{RecordCount-2b050c41.js → RecordCount-78ed6845.js} +6 -4
- flowfile/web/static/assets/{RecordId-81df7784.js → RecordId-2156e890.js} +8 -6
- flowfile/web/static/assets/{SQLQueryComponent-36cef432.css → SQLQueryComponent-1c2f26b4.css} +5 -5
- flowfile/web/static/assets/{SQLQueryComponent-88dcfe53.js → SQLQueryComponent-48c72f5b.js} +3 -3
- flowfile/web/static/assets/{Sample-258ad2a9.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-2a2cb7e2.js → SecretsView-17df66ee.js} +35 -36
- flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
- flowfile/web/static/assets/{Select-850215fd.js → Select-0aee4c54.js} +9 -7
- flowfile/web/static/assets/{SettingsSection-55bae608.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-0e8d9123.js → SettingsSection-cd341bb6.js} +3 -3
- flowfile/web/static/assets/{SettingsSection-29b4fa6b.js → SettingsSection-f2002a6d.js} +3 -3
- flowfile/web/static/assets/{SingleSelect-bebd408b.js → SingleSelect-460cc0ea.js} +2 -2
- flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-6093741c.js → SingleSelect.vue_vue_type_script_setup_true_lang-30741bb2.js} +1 -1
- flowfile/web/static/assets/{SliderInput-6a05ab61.js → SliderInput-5d926864.js} +7 -4
- flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
- flowfile/web/static/assets/{Sort-10ab48ed.js → Sort-3cdc971b.js} +9 -7
- flowfile/web/static/assets/{Unique-f9fb0809.css → Sort-8a871341.css} +10 -10
- flowfile/web/static/assets/{TextInput-df9d6259.js → TextInput-a2d0bfbd.js} +2 -2
- flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-000e1178.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-6c2d93d8.js → TextToRows-918945f7.js} +11 -10
- flowfile/web/static/assets/{ToggleSwitch-0ff7ac52.js → ToggleSwitch-f0ef5196.js} +2 -2
- flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-c6dc3029.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-1bab97cb.js → UnavailableFields-bdad6144.js} +4 -4
- flowfile/web/static/assets/{Union-af6c3d9b.css → Union-d6a8d7d5.css} +7 -7
- flowfile/web/static/assets/{Union-b563478a.js → Union-e8ab8c86.js} +8 -6
- flowfile/web/static/assets/{Unique-f90db5db.js → Unique-8cd4f976.js} +13 -22
- 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-bcb0025f.js → Unpivot-8da14095.js} +10 -8
- flowfile/web/static/assets/{UnpivotValidation-c4e73b04.js → UnpivotValidation-6f7d89ff.js} +3 -3
- flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
- flowfile/web/static/assets/{VueGraphicWalker-bb8535e2.js → VueGraphicWalker-3fb312e1.js} +4 -4
- flowfile/web/static/assets/{VueGraphicWalker-ed5ab88b.css → VueGraphicWalker-430f0b86.css} +1 -1
- flowfile/web/static/assets/{api-4c8e3822.js → api-24483f0d.js} +1 -1
- flowfile/web/static/assets/{api-2d6adc4f.js → api-8b81fa73.js} +1 -1
- flowfile/web/static/assets/{dropDown-35135ba8.css → dropDown-3d8dc5fa.css} +40 -40
- flowfile/web/static/assets/{dropDown-1bca8a74.js → dropDown-ac0fda9d.js} +3 -3
- flowfile/web/static/assets/{fullEditor-2985687e.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-0476ba4e.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-246f201c.js → index-fb6493ae.js} +41626 -40869
- flowfile/web/static/assets/node.types-2c15bb7e.js +82 -0
- flowfile/web/static/assets/nodeInput-0eb13f1a.js +2 -0
- flowfile/web/static/assets/{outputCsv-d686eeaf.js → outputCsv-8f8ba42d.js} +3 -3
- flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
- flowfile/web/static/assets/{outputExcel-8809ea2f.js → outputExcel-393f4fef.js} +3 -3
- flowfile/web/static/assets/{outputExcel-b41305c0.css → outputExcel-f5d272b2.css} +26 -26
- flowfile/web/static/assets/{outputParquet-53ba645a.js → outputParquet-07c81f65.js} +4 -4
- flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
- flowfile/web/static/assets/{readCsv-053bf97b.js → readCsv-07f6d9ad.js} +21 -20
- flowfile/web/static/assets/{readCsv-bca3ed53.css → readCsv-3bfac4c3.css} +15 -15
- flowfile/web/static/assets/{readExcel-e1b381ea.css → readExcel-3db6b763.css} +13 -13
- flowfile/web/static/assets/{readExcel-ad531eab.js → readExcel-ed69bc8f.js} +10 -12
- flowfile/web/static/assets/{readParquet-cee068e2.css → readParquet-c5244ad5.css} +4 -4
- flowfile/web/static/assets/{readParquet-58e899a1.js → readParquet-e3ed4528.js} +4 -7
- flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
- flowfile/web/static/assets/{selectDynamic-b38de2ba.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-db9b8936.js → vue-codemirror.esm-0965f39f.js} +31 -637
- flowfile/web/static/assets/{vue-content-loader.es-b5f3ac30.js → vue-content-loader.es-c506ad97.js} +1 -1
- flowfile/web/static/index.html +2 -2
- {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +4 -4
- flowfile-0.5.3.dist-info/RECORD +402 -0
- {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +1 -1
- {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +1 -0
- flowfile_core/__init__.py +13 -3
- 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 +27 -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 +391 -279
- 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 +152 -103
- flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +526 -477
- 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 +43 -32
- 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 +15 -11
- 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 +360 -191
- flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
- flowfile_core/flowfile/flow_data_engine/utils.py +101 -67
- flowfile_core/flowfile/flow_graph.py +1011 -561
- flowfile_core/flowfile/flow_graph_utils.py +31 -49
- flowfile_core/flowfile/flow_node/flow_node.py +332 -232
- flowfile_core/flowfile/flow_node/models.py +54 -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 +82 -32
- flowfile_core/flowfile/manage/compatibility_enhancements.py +493 -47
- flowfile_core/flowfile/manage/io_flowfile.py +391 -0
- 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 +136 -35
- flowfile_core/flowfile/schema_callbacks.py +77 -54
- 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 +72 -55
- 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 +77 -43
- 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 -55
- flowfile_core/schemas/input_schema.py +398 -154
- flowfile_core/schemas/output_model.py +50 -35
- flowfile_core/schemas/schemas.py +207 -67
- flowfile_core/schemas/transform_schema.py +1360 -435
- flowfile_core/schemas/yaml_types.py +117 -0
- flowfile_core/secret_manager/secret_manager.py +17 -13
- flowfile_core/{flowfile/node_designer/data_types.py → types.py} +33 -3
- 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 +107 -50
- 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 +581 -489
- flowfile_frame/flow_frame.pyi +123 -104
- flowfile_frame/flow_frame_methods.py +236 -252
- 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 -4
- flowfile_worker/configs.py +11 -19
- flowfile_worker/create/__init__.py +14 -27
- flowfile_worker/create/funcs.py +143 -94
- flowfile_worker/create/models.py +139 -68
- 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 -93
- 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/README.md +56 -0
- tools/migrate/__init__.py +12 -0
- tools/migrate/__main__.py +118 -0
- tools/migrate/legacy_schemas.py +682 -0
- tools/migrate/migrate.py +610 -0
- tools/migrate/tests/__init__.py +0 -0
- tools/migrate/tests/conftest.py +21 -0
- tools/migrate/tests/test_migrate.py +622 -0
- tools/migrate/tests/test_migration_e2e.py +1009 -0
- tools/migrate/tests/test_node_migrations.py +843 -0
- 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-812dcbca.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/outputCsv-9cc59e0b.css +0 -2499
- flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
- flowfile/web/static/assets/secretApi-538058f3.js +0 -46
- flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
- flowfile-0.4.1.dist-info/RECORD +0 -376
- flowfile_core/flowfile/manage/open_flowfile.py +0 -143
- {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
- /flowfile_core/flowfile/manage/manage_flowfile.py → /tools/__init__.py +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,23 +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]
|
|
401
|
+
node_information.description = (
|
|
402
|
+
self.setting_input.description if hasattr(self.setting_input, "description") else ""
|
|
403
|
+
)
|
|
373
404
|
node_information.is_setup = self.is_setup
|
|
374
405
|
node_information.x_position = self.setting_input.pos_x
|
|
375
406
|
node_information.y_position = self.setting_input.pos_y
|
|
@@ -403,7 +434,7 @@ class FlowNode:
|
|
|
403
434
|
self._function = function
|
|
404
435
|
|
|
405
436
|
@property
|
|
406
|
-
def all_inputs(self) ->
|
|
437
|
+
def all_inputs(self) -> list["FlowNode"]:
|
|
407
438
|
"""Gets a list of all nodes connected to any input port.
|
|
408
439
|
|
|
409
440
|
Returns:
|
|
@@ -435,8 +466,9 @@ class FlowNode:
|
|
|
435
466
|
self._hash = self.calculate_hash(self.setting_input)
|
|
436
467
|
return self._hash
|
|
437
468
|
|
|
438
|
-
def add_node_connection(
|
|
439
|
-
|
|
469
|
+
def add_node_connection(
|
|
470
|
+
self, from_node: "FlowNode", insert_type: Literal["main", "left", "right"] = "main"
|
|
471
|
+
) -> None:
|
|
440
472
|
"""Adds a connection from a source node to this node.
|
|
441
473
|
|
|
442
474
|
Args:
|
|
@@ -447,19 +479,19 @@ class FlowNode:
|
|
|
447
479
|
Exception: If the insert_type is invalid.
|
|
448
480
|
"""
|
|
449
481
|
from_node.leads_to_nodes.append(self)
|
|
450
|
-
if insert_type ==
|
|
482
|
+
if insert_type == "main":
|
|
451
483
|
if self.node_template.input <= 2 or self.node_inputs.main_inputs is None:
|
|
452
484
|
self.node_inputs.main_inputs = [from_node]
|
|
453
485
|
else:
|
|
454
486
|
self.node_inputs.main_inputs.append(from_node)
|
|
455
|
-
elif insert_type ==
|
|
487
|
+
elif insert_type == "right":
|
|
456
488
|
self.node_inputs.right_input = from_node
|
|
457
|
-
elif insert_type ==
|
|
489
|
+
elif insert_type == "left":
|
|
458
490
|
self.node_inputs.left_input = from_node
|
|
459
491
|
else:
|
|
460
|
-
raise Exception(
|
|
492
|
+
raise Exception("Cannot find the connection")
|
|
461
493
|
if self.setting_input.is_setup:
|
|
462
|
-
if hasattr(self.setting_input,
|
|
494
|
+
if hasattr(self.setting_input, "depending_on_id") and insert_type == "main":
|
|
463
495
|
self.setting_input.depending_on_id = from_node.node_id
|
|
464
496
|
self.reset()
|
|
465
497
|
from_node.reset()
|
|
@@ -471,7 +503,7 @@ class FlowNode:
|
|
|
471
503
|
deep: If True, the reset propagates recursively through the entire downstream graph.
|
|
472
504
|
"""
|
|
473
505
|
for node in self.leads_to_nodes:
|
|
474
|
-
self.print(f
|
|
506
|
+
self.print(f"resetting node: {node.node_id}")
|
|
475
507
|
node.reset(deep)
|
|
476
508
|
|
|
477
509
|
def get_flow_file_column_schema(self, col_name: str) -> FlowfileColumn | None:
|
|
@@ -487,7 +519,7 @@ class FlowNode:
|
|
|
487
519
|
if s.column_name == col_name:
|
|
488
520
|
return s
|
|
489
521
|
|
|
490
|
-
def get_predicted_schema(self, force: bool = False) ->
|
|
522
|
+
def get_predicted_schema(self, force: bool = False) -> list[FlowfileColumn] | None:
|
|
491
523
|
"""Predicts the output schema of the node without full execution.
|
|
492
524
|
|
|
493
525
|
It uses the schema_callback or infers from predicted data.
|
|
@@ -498,22 +530,24 @@ class FlowNode:
|
|
|
498
530
|
Returns:
|
|
499
531
|
A list of FlowfileColumn objects representing the predicted schema.
|
|
500
532
|
"""
|
|
533
|
+
|
|
501
534
|
if self.node_schema.predicted_schema and not force:
|
|
502
535
|
return self.node_schema.predicted_schema
|
|
503
536
|
if self.schema_callback is not None and (self.node_schema.predicted_schema is None or force):
|
|
504
|
-
self.print(
|
|
537
|
+
self.print("Getting the data from a schema callback")
|
|
505
538
|
if force:
|
|
506
539
|
# Force the schema callback to reset, so that it will be executed again
|
|
507
540
|
self.schema_callback.reset()
|
|
508
541
|
schema = self.schema_callback()
|
|
509
542
|
if schema is not None and len(schema) > 0:
|
|
510
|
-
self.print(
|
|
543
|
+
self.print("Calculating the schema based on the schema callback")
|
|
511
544
|
self.node_schema.predicted_schema = schema
|
|
512
545
|
return self.node_schema.predicted_schema
|
|
513
546
|
predicted_data = self._predicted_data_getter()
|
|
514
547
|
if predicted_data is not None and predicted_data.schema is not None:
|
|
515
|
-
self.print(
|
|
548
|
+
self.print("Calculating the schema based on the predicted resulting data")
|
|
516
549
|
self.node_schema.predicted_schema = self._predicted_data_getter().schema
|
|
550
|
+
|
|
517
551
|
return self.node_schema.predicted_schema
|
|
518
552
|
|
|
519
553
|
@property
|
|
@@ -524,7 +558,7 @@ class FlowNode:
|
|
|
524
558
|
True if the node is set up, False otherwise.
|
|
525
559
|
"""
|
|
526
560
|
if not self.node_information.is_setup:
|
|
527
|
-
if self.function.__name__ !=
|
|
561
|
+
if self.function.__name__ != "placeholder":
|
|
528
562
|
self.node_information.is_setup = True
|
|
529
563
|
self.setting_input.is_setup = True
|
|
530
564
|
return self.node_information.is_setup
|
|
@@ -535,7 +569,7 @@ class FlowNode:
|
|
|
535
569
|
Args:
|
|
536
570
|
v: The message or value to log.
|
|
537
571
|
"""
|
|
538
|
-
logger.info(f
|
|
572
|
+
logger.info(f"{self.node_type}, node_id: {self.node_id}: {v}")
|
|
539
573
|
|
|
540
574
|
def get_resulting_data(self) -> FlowDataEngine | None:
|
|
541
575
|
"""Executes the node's function to produce the actual output data.
|
|
@@ -550,11 +584,11 @@ class FlowNode:
|
|
|
550
584
|
"""
|
|
551
585
|
if self.is_setup:
|
|
552
586
|
if self.results.resulting_data is None and self.results.errors is None:
|
|
553
|
-
self.print(
|
|
587
|
+
self.print("getting resulting data")
|
|
554
588
|
try:
|
|
555
589
|
if isinstance(self.function, FlowDataEngine):
|
|
556
590
|
fl: FlowDataEngine = self.function
|
|
557
|
-
elif self.node_type ==
|
|
591
|
+
elif self.node_type == "external_source":
|
|
558
592
|
fl: FlowDataEngine = self.function()
|
|
559
593
|
fl.collect_external()
|
|
560
594
|
self.node_settings.streamable = False
|
|
@@ -587,14 +621,14 @@ class FlowNode:
|
|
|
587
621
|
return fl
|
|
588
622
|
except ValueError as e:
|
|
589
623
|
if str(e) == "generator already executing":
|
|
590
|
-
logger.info(
|
|
624
|
+
logger.info("Generator already executing, waiting for the result")
|
|
591
625
|
sleep(1)
|
|
592
626
|
return self._predicted_data_getter()
|
|
593
627
|
fl = FlowDataEngine()
|
|
594
628
|
return fl
|
|
595
629
|
|
|
596
630
|
except Exception as e:
|
|
597
|
-
logger.warning(
|
|
631
|
+
logger.warning("there was an issue with the function, returning an empty Flowfile")
|
|
598
632
|
logger.warning(e)
|
|
599
633
|
|
|
600
634
|
def get_predicted_resulting_data(self) -> FlowDataEngine:
|
|
@@ -606,7 +640,7 @@ class FlowNode:
|
|
|
606
640
|
A FlowDataEngine instance with a schema but no data.
|
|
607
641
|
"""
|
|
608
642
|
if self.needs_run(False) and self.schema_callback is not None or self.node_schema.result_schema is not None:
|
|
609
|
-
self.print(
|
|
643
|
+
self.print("Getting data based on the schema")
|
|
610
644
|
|
|
611
645
|
_s = self.schema_callback() if self.node_schema.result_schema is None else self.node_schema.result_schema
|
|
612
646
|
return FlowDataEngine.create_from_schema(_s)
|
|
@@ -646,7 +680,7 @@ class FlowNode:
|
|
|
646
680
|
yield n
|
|
647
681
|
|
|
648
682
|
@property
|
|
649
|
-
def schema(self) ->
|
|
683
|
+
def schema(self) -> list[FlowfileColumn]:
|
|
650
684
|
"""Gets the definitive output schema of the node.
|
|
651
685
|
|
|
652
686
|
If not already run, it falls back to the predicted schema.
|
|
@@ -658,7 +692,7 @@ class FlowNode:
|
|
|
658
692
|
if self.is_setup and self.results.errors is None:
|
|
659
693
|
if self.node_schema.result_schema is not None and len(self.node_schema.result_schema) > 0:
|
|
660
694
|
return self.node_schema.result_schema
|
|
661
|
-
elif self.node_type ==
|
|
695
|
+
elif self.node_type == "output":
|
|
662
696
|
if len(self.node_inputs.main_inputs) > 0:
|
|
663
697
|
self.node_schema.result_schema = self.node_inputs.main_inputs[0].schema
|
|
664
698
|
else:
|
|
@@ -677,11 +711,15 @@ class FlowNode:
|
|
|
677
711
|
"""
|
|
678
712
|
|
|
679
713
|
if results_exists(self.hash):
|
|
680
|
-
logger.warning(
|
|
714
|
+
logger.warning("Not implemented")
|
|
681
715
|
clear_task_from_worker(self.hash)
|
|
682
716
|
|
|
683
|
-
def needs_run(
|
|
684
|
-
|
|
717
|
+
def needs_run(
|
|
718
|
+
self,
|
|
719
|
+
performance_mode: bool,
|
|
720
|
+
node_logger: NodeLogger = None,
|
|
721
|
+
execution_location: schemas.ExecutionLocationsLiteral = "remote",
|
|
722
|
+
) -> bool:
|
|
685
723
|
"""Determines if the node needs to be executed.
|
|
686
724
|
|
|
687
725
|
The decision is based on its run state, caching settings, and execution mode.
|
|
@@ -700,7 +738,7 @@ class FlowNode:
|
|
|
700
738
|
flow_logger = logger if node_logger is None else node_logger
|
|
701
739
|
cache_result_exists = results_exists(self.hash)
|
|
702
740
|
if not self.node_stats.has_run_with_current_setup:
|
|
703
|
-
flow_logger.info(
|
|
741
|
+
flow_logger.info("Node has not run, needs to run")
|
|
704
742
|
return True
|
|
705
743
|
if self.node_settings.cache_results and cache_result_exists:
|
|
706
744
|
return False
|
|
@@ -734,7 +772,9 @@ class FlowNode:
|
|
|
734
772
|
if example_data is None:
|
|
735
773
|
example_data = resulting_data.get_sample(100).to_arrow()
|
|
736
774
|
return example_data
|
|
775
|
+
|
|
737
776
|
return get_example_data
|
|
777
|
+
|
|
738
778
|
resulting_data = self.get_resulting_data()
|
|
739
779
|
|
|
740
780
|
if not performance_mode:
|
|
@@ -756,8 +796,13 @@ class FlowNode:
|
|
|
756
796
|
try:
|
|
757
797
|
resulting_data = self.get_resulting_data()
|
|
758
798
|
if not performance_mode:
|
|
759
|
-
external_sampler = ExternalSampler(
|
|
760
|
-
|
|
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
|
+
)
|
|
761
806
|
self.store_example_data_generator(external_sampler)
|
|
762
807
|
if self.results.errors is None and not self.node_stats.is_canceled:
|
|
763
808
|
self.node_stats.has_run_with_current_setup = True
|
|
@@ -787,48 +832,58 @@ class FlowNode:
|
|
|
787
832
|
Exception: If the node_logger is not provided or if execution fails.
|
|
788
833
|
"""
|
|
789
834
|
if node_logger is None:
|
|
790
|
-
raise Exception(
|
|
835
|
+
raise Exception("Node logger is not defined")
|
|
791
836
|
if self.node_settings.cache_results and results_exists(self.hash):
|
|
792
837
|
try:
|
|
793
838
|
self.results.resulting_data = get_external_df_result(self.hash)
|
|
794
839
|
self._cache_progress = None
|
|
795
840
|
return
|
|
796
|
-
except Exception
|
|
797
|
-
node_logger.warning(
|
|
798
|
-
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":
|
|
799
844
|
self.results.resulting_data = self.get_resulting_data()
|
|
800
845
|
self.node_stats.has_run_with_current_setup = True
|
|
801
846
|
return
|
|
802
847
|
try:
|
|
803
848
|
self.get_resulting_data()
|
|
804
849
|
except Exception as e:
|
|
805
|
-
self.results.errors =
|
|
850
|
+
self.results.errors = "Error with creating the lazy frame, most likely due to invalid graph"
|
|
806
851
|
raise e
|
|
807
852
|
if not performance_mode:
|
|
808
|
-
external_df_fetcher = ExternalDfFetcher(
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
+
)
|
|
812
860
|
self._fetch_cached_df = external_df_fetcher
|
|
813
861
|
try:
|
|
814
862
|
lf = external_df_fetcher.get_result()
|
|
815
863
|
self.results.resulting_data = FlowDataEngine(
|
|
816
|
-
lf,
|
|
817
|
-
|
|
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,
|
|
818
871
|
)
|
|
819
872
|
if not performance_mode:
|
|
820
873
|
self.store_example_data_generator(external_df_fetcher)
|
|
821
874
|
self.node_stats.has_run_with_current_setup = True
|
|
822
875
|
|
|
823
876
|
except Exception as e:
|
|
824
|
-
node_logger.error(
|
|
877
|
+
node_logger.error("Error with external process")
|
|
825
878
|
if external_df_fetcher.error_code == -1:
|
|
826
879
|
try:
|
|
827
880
|
self.results.resulting_data = self.get_resulting_data()
|
|
828
|
-
self.results.warnings = (
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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
|
+
)
|
|
832
887
|
except Exception as e:
|
|
833
888
|
self.results.errors = str(e)
|
|
834
889
|
raise e
|
|
@@ -855,15 +910,18 @@ class FlowNode:
|
|
|
855
910
|
self._fetch_cached_df.cancel()
|
|
856
911
|
self.node_stats.is_canceled = True
|
|
857
912
|
else:
|
|
858
|
-
logger.warning(
|
|
913
|
+
logger.warning("No external process to cancel")
|
|
859
914
|
self.node_stats.is_canceled = True
|
|
860
915
|
|
|
861
|
-
def execute_node(
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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
|
+
):
|
|
867
925
|
"""Orchestrates the execution, handling location, caching, and retries.
|
|
868
926
|
|
|
869
927
|
Args:
|
|
@@ -879,7 +937,7 @@ class FlowNode:
|
|
|
879
937
|
Exception: If the node_logger is not defined.
|
|
880
938
|
"""
|
|
881
939
|
if node_logger is None:
|
|
882
|
-
raise Exception(
|
|
940
|
+
raise Exception("Flow logger is not defined")
|
|
883
941
|
# TODO: Simplify which route is being picked there are many duplicate checks
|
|
884
942
|
|
|
885
943
|
if reset_cache:
|
|
@@ -888,63 +946,78 @@ class FlowNode:
|
|
|
888
946
|
self.node_stats.has_completed_last_run = False
|
|
889
947
|
|
|
890
948
|
if self.is_setup:
|
|
891
|
-
node_logger.info(f
|
|
892
|
-
if (
|
|
893
|
-
|
|
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
|
+
):
|
|
894
955
|
self.clear_table_example()
|
|
895
956
|
self.prepare_before_run()
|
|
896
957
|
self.reset()
|
|
897
958
|
try:
|
|
898
|
-
if (
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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")
|
|
903
965
|
if self.node_settings.cache_results:
|
|
904
966
|
performance_mode = False
|
|
905
|
-
self.execute_remote(
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
967
|
+
self.execute_remote(
|
|
968
|
+
performance_mode=(performance_mode if not self.node_settings.cache_results else False),
|
|
969
|
+
node_logger=node_logger,
|
|
970
|
+
)
|
|
909
971
|
else:
|
|
910
|
-
node_logger.info(
|
|
972
|
+
node_logger.info("Running the node locally")
|
|
911
973
|
self.execute_local(performance_mode=performance_mode, flow_id=node_logger.flow_id)
|
|
912
974
|
except Exception as e:
|
|
913
|
-
if
|
|
914
|
-
logger.warning(
|
|
915
|
-
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()
|
|
916
978
|
for node_input in all_inputs:
|
|
917
|
-
node_input.execute_node(
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
+
)
|
|
924
992
|
else:
|
|
925
993
|
self.results.errors = str(e)
|
|
926
994
|
if "Connection refused" in str(e) and "/submit_query/" in str(e):
|
|
927
|
-
node_logger.warning(
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
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
|
+
)
|
|
931
1003
|
else:
|
|
932
|
-
node_logger.error(f
|
|
933
|
-
elif (
|
|
934
|
-
|
|
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
|
+
):
|
|
935
1008
|
try:
|
|
936
|
-
node_logger.info(
|
|
1009
|
+
node_logger.info("Executing fully locally")
|
|
937
1010
|
self.execute_full_local(performance_mode)
|
|
938
1011
|
except Exception as e:
|
|
939
1012
|
self.results.errors = str(e)
|
|
940
|
-
node_logger.error(f
|
|
1013
|
+
node_logger.error(f"Error with running the node: {e}")
|
|
941
1014
|
self.node_stats.error = str(e)
|
|
942
1015
|
self.node_stats.has_completed_last_run = False
|
|
943
1016
|
|
|
944
1017
|
else:
|
|
945
|
-
node_logger.info(
|
|
1018
|
+
node_logger.info("Node has already run, not running the node")
|
|
946
1019
|
else:
|
|
947
|
-
node_logger.warning(f
|
|
1020
|
+
node_logger.warning(f"Node {self.__name__} is not setup, cannot run the node")
|
|
948
1021
|
|
|
949
1022
|
def store_example_data_generator(self, external_df_fetcher: ExternalDfFetcher | ExternalSampler):
|
|
950
1023
|
"""Stores a generator function for fetching a sample of the result data.
|
|
@@ -957,7 +1030,7 @@ class FlowNode:
|
|
|
957
1030
|
self.results.example_data_path = file_ref
|
|
958
1031
|
self.results.example_data_generator = get_read_top_n(file_path=file_ref, n=100)
|
|
959
1032
|
else:
|
|
960
|
-
logger.error(
|
|
1033
|
+
logger.error("Could not get the sample data, the external process is not ready")
|
|
961
1034
|
|
|
962
1035
|
def needs_reset(self) -> bool:
|
|
963
1036
|
"""Checks if the node's hash has changed, indicating an outdated state.
|
|
@@ -977,7 +1050,7 @@ class FlowNode:
|
|
|
977
1050
|
"""
|
|
978
1051
|
needs_reset = self.needs_reset() or deep
|
|
979
1052
|
if needs_reset:
|
|
980
|
-
logger.info(f
|
|
1053
|
+
logger.info(f"{self.node_id}: Node needs reset")
|
|
981
1054
|
self.node_stats.has_run_with_current_setup = False
|
|
982
1055
|
self.results.reset()
|
|
983
1056
|
self.node_schema.result_schema = None
|
|
@@ -988,7 +1061,7 @@ class FlowNode:
|
|
|
988
1061
|
if self.is_correct:
|
|
989
1062
|
self._schema_callback = None # Ensure the schema callback is reset
|
|
990
1063
|
if self.schema_callback:
|
|
991
|
-
logger.info(f
|
|
1064
|
+
logger.info(f"{self.node_id}: Resetting the schema callback")
|
|
992
1065
|
self.schema_callback.start()
|
|
993
1066
|
self.evaluate_nodes()
|
|
994
1067
|
_ = self.hash # Recalculate the hash after reset
|
|
@@ -1002,17 +1075,18 @@ class FlowNode:
|
|
|
1002
1075
|
Returns:
|
|
1003
1076
|
True if the connection was found and removed, False otherwise.
|
|
1004
1077
|
"""
|
|
1005
|
-
logger.info(f
|
|
1078
|
+
logger.info(f"Deleting lead to node: {node_id}")
|
|
1006
1079
|
for i, lead_to_node in enumerate(self.leads_to_nodes):
|
|
1007
|
-
logger.info(f
|
|
1080
|
+
logger.info(f"Checking lead to node: {lead_to_node.node_id}")
|
|
1008
1081
|
if lead_to_node.node_id == node_id:
|
|
1009
|
-
logger.info(f
|
|
1082
|
+
logger.info(f"Found the node to delete: {node_id}")
|
|
1010
1083
|
self.leads_to_nodes.pop(i)
|
|
1011
1084
|
return True
|
|
1012
1085
|
return False
|
|
1013
1086
|
|
|
1014
|
-
def delete_input_node(
|
|
1015
|
-
|
|
1087
|
+
def delete_input_node(
|
|
1088
|
+
self, node_id: int, connection_type: input_schema.InputConnectionClass = "input-0", complete: bool = False
|
|
1089
|
+
) -> bool:
|
|
1016
1090
|
"""Removes a connection from a specific input node.
|
|
1017
1091
|
|
|
1018
1092
|
Args:
|
|
@@ -1024,23 +1098,23 @@ class FlowNode:
|
|
|
1024
1098
|
True if a connection was found and removed, False otherwise.
|
|
1025
1099
|
"""
|
|
1026
1100
|
deleted: bool = False
|
|
1027
|
-
if connection_type ==
|
|
1101
|
+
if connection_type == "input-0":
|
|
1028
1102
|
for i, node in enumerate(self.node_inputs.main_inputs):
|
|
1029
1103
|
if node.node_id == node_id:
|
|
1030
1104
|
self.node_inputs.main_inputs.pop(i)
|
|
1031
1105
|
deleted = True
|
|
1032
1106
|
if not complete:
|
|
1033
1107
|
continue
|
|
1034
|
-
elif connection_type ==
|
|
1108
|
+
elif connection_type == "input-1" or complete:
|
|
1035
1109
|
if self.node_inputs.right_input is not None and self.node_inputs.right_input.node_id == node_id:
|
|
1036
1110
|
self.node_inputs.right_input = None
|
|
1037
1111
|
deleted = True
|
|
1038
|
-
elif connection_type ==
|
|
1112
|
+
elif connection_type == "input-2" or complete:
|
|
1039
1113
|
if self.node_inputs.left_input is not None and self.node_inputs.right_input.node_id == node_id:
|
|
1040
1114
|
self.node_inputs.left_input = None
|
|
1041
1115
|
deleted = True
|
|
1042
1116
|
else:
|
|
1043
|
-
logger.warning(
|
|
1117
|
+
logger.warning("Could not find the connection to delete...")
|
|
1044
1118
|
if deleted:
|
|
1045
1119
|
self.reset()
|
|
1046
1120
|
return deleted
|
|
@@ -1053,7 +1127,7 @@ class FlowNode:
|
|
|
1053
1127
|
"""
|
|
1054
1128
|
return f"Node id: {self.node_id} ({self.node_type})"
|
|
1055
1129
|
|
|
1056
|
-
def _get_readable_schema(self) ->
|
|
1130
|
+
def _get_readable_schema(self) -> list[dict] | None:
|
|
1057
1131
|
"""Helper to get a simplified, dictionary representation of the output schema.
|
|
1058
1132
|
|
|
1059
1133
|
Returns:
|
|
@@ -1071,11 +1145,14 @@ class FlowNode:
|
|
|
1071
1145
|
Returns:
|
|
1072
1146
|
A dictionary containing key information about the node.
|
|
1073
1147
|
"""
|
|
1074
|
-
return dict(
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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
|
+
)
|
|
1079
1156
|
|
|
1080
1157
|
@property
|
|
1081
1158
|
def number_of_leads_to_nodes(self) -> int | None:
|
|
@@ -1146,14 +1223,13 @@ class FlowNode:
|
|
|
1146
1223
|
Returns:
|
|
1147
1224
|
A `TableExample` object, or None if the node is not set up.
|
|
1148
1225
|
"""
|
|
1149
|
-
self.print(
|
|
1226
|
+
self.print("Getting a table example")
|
|
1150
1227
|
if self.is_setup and include_data and self.node_stats.has_completed_last_run:
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
self.print('getting the table example')
|
|
1228
|
+
if self.node_template.node_group == "output":
|
|
1229
|
+
self.print("getting the table example")
|
|
1154
1230
|
return self.main_input[0].get_table_example(include_data)
|
|
1155
1231
|
|
|
1156
|
-
logger.info(
|
|
1232
|
+
logger.info("getting the table example since the node has run")
|
|
1157
1233
|
example_data_getter = self.results.example_data_generator
|
|
1158
1234
|
if example_data_getter is not None:
|
|
1159
1235
|
data = example_data_getter().to_pylist()
|
|
@@ -1165,26 +1241,34 @@ class FlowNode:
|
|
|
1165
1241
|
fl = self.get_resulting_data()
|
|
1166
1242
|
has_example_data = self.results.example_data_generator is not None
|
|
1167
1243
|
|
|
1168
|
-
return TableExample(
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
+
)
|
|
1175
1255
|
else:
|
|
1176
|
-
logger.warning(
|
|
1256
|
+
logger.warning("getting the table example but the node has not run")
|
|
1177
1257
|
try:
|
|
1178
1258
|
schema = [FileColumn.model_validate(c.get_column_repr()) for c in self.schema]
|
|
1179
1259
|
except Exception as e:
|
|
1180
1260
|
logger.warning(e)
|
|
1181
1261
|
schema = []
|
|
1182
1262
|
columns = [s.name for s in schema]
|
|
1183
|
-
return TableExample(
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
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
|
+
)
|
|
1188
1272
|
|
|
1189
1273
|
def get_node_data(self, flow_id: int, include_example: bool = False) -> NodeData:
|
|
1190
1274
|
"""Gathers all necessary data for representing the node in the UI.
|
|
@@ -1196,11 +1280,13 @@ class FlowNode:
|
|
|
1196
1280
|
Returns:
|
|
1197
1281
|
A `NodeData` object.
|
|
1198
1282
|
"""
|
|
1199
|
-
node = NodeData(
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
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
|
+
)
|
|
1204
1290
|
if self.main_input:
|
|
1205
1291
|
node.main_input = self.main_input[0].get_table_example()
|
|
1206
1292
|
if self.left_input:
|
|
@@ -1212,6 +1298,9 @@ class FlowNode:
|
|
|
1212
1298
|
node = setting_generator.get_setting_generator(self.node_type)(node)
|
|
1213
1299
|
|
|
1214
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
|
|
1215
1304
|
return node
|
|
1216
1305
|
|
|
1217
1306
|
def get_output_data(self) -> TableExample:
|
|
@@ -1228,12 +1317,14 @@ class FlowNode:
|
|
|
1228
1317
|
Returns:
|
|
1229
1318
|
A `NodeInput` object.
|
|
1230
1319
|
"""
|
|
1231
|
-
return schemas.NodeInput(
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
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]:
|
|
1237
1328
|
"""Generates `NodeEdge` objects for all input connections to this node.
|
|
1238
1329
|
|
|
1239
1330
|
Returns:
|
|
@@ -1242,24 +1333,33 @@ class FlowNode:
|
|
|
1242
1333
|
edges = []
|
|
1243
1334
|
if self.node_inputs.main_inputs is not None:
|
|
1244
1335
|
for i, main_input in enumerate(self.node_inputs.main_inputs):
|
|
1245
|
-
edges.append(
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
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
|
+
)
|
|
1251
1345
|
if self.node_inputs.left_input is not None:
|
|
1252
|
-
edges.append(
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
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
|
+
)
|
|
1258
1355
|
if self.node_inputs.right_input is not None:
|
|
1259
|
-
edges.append(
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
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
|
+
)
|
|
1265
1365
|
return edges
|