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.
Files changed (332) hide show
  1. build_backends/main.py +25 -22
  2. build_backends/main_prd.py +10 -19
  3. flowfile/__init__.py +179 -73
  4. flowfile/__main__.py +10 -7
  5. flowfile/api.py +52 -59
  6. flowfile/web/__init__.py +14 -9
  7. flowfile/web/static/assets/AdminView-49392a9a.js +713 -0
  8. flowfile/web/static/assets/AdminView-f53bad23.css +129 -0
  9. flowfile/web/static/assets/CloudConnectionView-36bcd6df.css +72 -0
  10. flowfile/web/static/assets/{CloudConnectionManager-d3248f8d.js → CloudConnectionView-f13f202b.js} +11 -11
  11. flowfile/web/static/assets/{CloudStorageReader-d65bf041.js → CloudStorageReader-0023d4a5.js} +10 -8
  12. flowfile/web/static/assets/{CloudStorageReader-29d14fcc.css → CloudStorageReader-24c54524.css} +27 -27
  13. flowfile/web/static/assets/{CloudStorageWriter-b0ee067f.css → CloudStorageWriter-60547855.css} +26 -26
  14. flowfile/web/static/assets/{CloudStorageWriter-e83be3ed.js → CloudStorageWriter-8e781e11.js} +10 -8
  15. flowfile/web/static/assets/{ColumnSelector-47996a16.css → ColumnSelector-371637fb.css} +2 -2
  16. flowfile/web/static/assets/{ColumnSelector-cce661cf.js → ColumnSelector-8ad68ea9.js} +3 -5
  17. flowfile/web/static/assets/{ContextMenu-c13f91d0.css → ContextMenu-26d4dd27.css} +6 -6
  18. flowfile/web/static/assets/{ContextMenu-11a4652a.js → ContextMenu-31ee57f0.js} +3 -3
  19. flowfile/web/static/assets/{ContextMenu-160afb08.js → ContextMenu-69a74055.js} +3 -3
  20. flowfile/web/static/assets/{ContextMenu-cf18d2cc.js → ContextMenu-8e2051c6.js} +3 -3
  21. flowfile/web/static/assets/{ContextMenu-4c74eef1.css → ContextMenu-8ec1729e.css} +6 -6
  22. flowfile/web/static/assets/{ContextMenu-63cfa99b.css → ContextMenu-9b310c60.css} +6 -6
  23. flowfile/web/static/assets/{CrossJoin-d395d38c.js → CrossJoin-03df6938.js} +12 -10
  24. flowfile/web/static/assets/{CrossJoin-1119d18e.css → CrossJoin-71b4cc10.css} +20 -20
  25. flowfile/web/static/assets/CustomNode-59e99a86.css +32 -0
  26. flowfile/web/static/assets/{CustomNode-b812dc0b.js → CustomNode-8479239b.js} +36 -24
  27. flowfile/web/static/assets/{DatabaseConnectionSettings-7000bf2c.js → DatabaseConnectionSettings-869e3efd.js} +5 -4
  28. flowfile/web/static/assets/{DatabaseConnectionSettings-0c04b2e5.css → DatabaseConnectionSettings-e91df89a.css} +13 -13
  29. flowfile/web/static/assets/{DatabaseReader-ae61773c.css → DatabaseReader-36898a00.css} +24 -24
  30. flowfile/web/static/assets/{DatabaseReader-4f035d0c.js → DatabaseReader-c58b9552.js} +25 -15
  31. flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
  32. flowfile/web/static/assets/{DatabaseManager-9662ec5b.js → DatabaseView-d26a9140.js} +11 -11
  33. flowfile/web/static/assets/{DatabaseWriter-2f570e53.css → DatabaseWriter-217a99f1.css} +19 -19
  34. flowfile/web/static/assets/{DatabaseWriter-f65dcd54.js → DatabaseWriter-4d05ddc7.js} +17 -10
  35. flowfile/web/static/assets/{designer-e3c150ec.css → DesignerView-a6d0ee84.css} +629 -538
  36. flowfile/web/static/assets/{designer-f3656d8c.js → DesignerView-e6f5c0e8.js} +1214 -3209
  37. flowfile/web/static/assets/{documentation-52b241e7.js → DocumentationView-2e78ef1b.js} +5 -5
  38. flowfile/web/static/assets/{documentation-12216a74.css → DocumentationView-fd46c656.css} +7 -7
  39. flowfile/web/static/assets/{ExploreData-2d0cf4db.css → ExploreData-10c5acc8.css} +13 -12
  40. flowfile/web/static/assets/{ExploreData-94c43dfc.js → ExploreData-7b54caca.js} +18 -9
  41. flowfile/web/static/assets/{ExternalSource-ac04b3cc.js → ExternalSource-3fa399b2.js} +9 -7
  42. flowfile/web/static/assets/{ExternalSource-e37b6275.css → ExternalSource-47ab05a3.css} +17 -17
  43. flowfile/web/static/assets/Filter-7494ea97.css +48 -0
  44. flowfile/web/static/assets/Filter-8cbbdbf3.js +287 -0
  45. flowfile/web/static/assets/{Formula-bb96803d.css → Formula-53d58c43.css} +7 -7
  46. flowfile/web/static/assets/{Formula-71472193.js → Formula-aac42b1e.js} +13 -11
  47. flowfile/web/static/assets/{FuzzyMatch-1010f966.css → FuzzyMatch-ad6361d6.css} +68 -69
  48. flowfile/web/static/assets/{FuzzyMatch-b317f631.js → FuzzyMatch-cd9bbfca.js} +12 -10
  49. flowfile/web/static/assets/{Pivot-cf333e3d.css → GraphSolver-c24dec17.css} +5 -5
  50. flowfile/web/static/assets/{GraphSolver-754a234f.js → GraphSolver-c7e6780e.js} +13 -11
  51. flowfile/web/static/assets/{GroupBy-6c6f9802.js → GroupBy-93c5d22b.js} +9 -7
  52. flowfile/web/static/assets/{GroupBy-b9505323.css → GroupBy-be7ac0bf.css} +10 -10
  53. flowfile/web/static/assets/{Join-fd79b451.css → Join-28b5e18f.css} +22 -22
  54. flowfile/web/static/assets/{Join-a1b800be.js → Join-a19b2de2.js} +13 -11
  55. flowfile/web/static/assets/LoginView-0df4ed0a.js +134 -0
  56. flowfile/web/static/assets/LoginView-d325d632.css +172 -0
  57. flowfile/web/static/assets/ManualInput-3702e677.css +293 -0
  58. flowfile/web/static/assets/{ManualInput-a9640276.js → ManualInput-8d3374b2.js} +170 -116
  59. flowfile/web/static/assets/{MultiSelect-97213888.js → MultiSelect-ad1b6243.js} +2 -2
  60. 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
  61. flowfile/web/static/assets/NodeDesigner-40b647c9.js +2610 -0
  62. flowfile/web/static/assets/NodeDesigner-5f53be3f.css +1429 -0
  63. flowfile/web/static/assets/{NumericInput-e638088a.js → NumericInput-7100234c.js} +2 -2
  64. 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
  65. flowfile/web/static/assets/{Output-ddc9079f.css → Output-35e97000.css} +6 -6
  66. flowfile/web/static/assets/{Output-76750610.js → Output-f5efd2aa.js} +60 -38
  67. flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
  68. flowfile/web/static/assets/{Pivot-7814803f.js → Pivot-d981d23c.js} +11 -9
  69. flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
  70. flowfile/web/static/assets/{PivotValidation-f92137d2.js → PivotValidation-39386e95.js} +3 -3
  71. flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
  72. flowfile/web/static/assets/{PivotValidation-76dd431a.js → PivotValidation-63de1f73.js} +3 -3
  73. flowfile/web/static/assets/{PolarsCode-650322d1.css → PolarsCode-2b1f1f23.css} +4 -4
  74. flowfile/web/static/assets/{PolarsCode-889c3008.js → PolarsCode-f9d69217.js} +18 -9
  75. flowfile/web/static/assets/PopOver-b22f049e.js +939 -0
  76. flowfile/web/static/assets/PopOver-d96599db.css +33 -0
  77. flowfile/web/static/assets/{Read-6b17491f.css → Read-36e7bd51.css} +12 -12
  78. flowfile/web/static/assets/{Read-637b72a7.js → Read-aec2e377.js} +83 -105
  79. flowfile/web/static/assets/{RecordCount-2b050c41.js → RecordCount-78ed6845.js} +6 -4
  80. flowfile/web/static/assets/{RecordId-81df7784.js → RecordId-2156e890.js} +8 -6
  81. flowfile/web/static/assets/{SQLQueryComponent-36cef432.css → SQLQueryComponent-1c2f26b4.css} +5 -5
  82. flowfile/web/static/assets/{SQLQueryComponent-88dcfe53.js → SQLQueryComponent-48c72f5b.js} +3 -3
  83. flowfile/web/static/assets/{Sample-258ad2a9.js → Sample-1352ca74.js} +6 -4
  84. flowfile/web/static/assets/SecretSelector-22b5ff89.js +113 -0
  85. flowfile/web/static/assets/SecretSelector-6329f743.css +43 -0
  86. flowfile/web/static/assets/{SecretManager-2a2cb7e2.js → SecretsView-17df66ee.js} +35 -36
  87. flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
  88. flowfile/web/static/assets/{Select-850215fd.js → Select-0aee4c54.js} +9 -7
  89. flowfile/web/static/assets/{SettingsSection-55bae608.js → SettingsSection-0784e157.js} +3 -3
  90. flowfile/web/static/assets/{SettingsSection-71e6b7e3.css → SettingsSection-07fbbc39.css} +4 -4
  91. flowfile/web/static/assets/{SettingsSection-5c696bee.css → SettingsSection-26fe48d4.css} +4 -4
  92. flowfile/web/static/assets/{SettingsSection-2e4d03c4.css → SettingsSection-8f980839.css} +4 -4
  93. flowfile/web/static/assets/{SettingsSection-0e8d9123.js → SettingsSection-cd341bb6.js} +3 -3
  94. flowfile/web/static/assets/{SettingsSection-29b4fa6b.js → SettingsSection-f2002a6d.js} +3 -3
  95. flowfile/web/static/assets/{SingleSelect-bebd408b.js → SingleSelect-460cc0ea.js} +2 -2
  96. 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
  97. flowfile/web/static/assets/{SliderInput-6a05ab61.js → SliderInput-5d926864.js} +7 -4
  98. flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
  99. flowfile/web/static/assets/{Sort-10ab48ed.js → Sort-3cdc971b.js} +9 -7
  100. flowfile/web/static/assets/{Unique-f9fb0809.css → Sort-8a871341.css} +10 -10
  101. flowfile/web/static/assets/{TextInput-df9d6259.js → TextInput-a2d0bfbd.js} +2 -2
  102. 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
  103. flowfile/web/static/assets/{TextToRows-5d2c1190.css → TextToRows-12afb4f4.css} +10 -10
  104. flowfile/web/static/assets/{TextToRows-6c2d93d8.js → TextToRows-918945f7.js} +11 -10
  105. flowfile/web/static/assets/{ToggleSwitch-0ff7ac52.js → ToggleSwitch-f0ef5196.js} +2 -2
  106. 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
  107. flowfile/web/static/assets/{UnavailableFields-5edd5322.css → UnavailableFields-54d2f518.css} +6 -6
  108. flowfile/web/static/assets/{UnavailableFields-1bab97cb.js → UnavailableFields-bdad6144.js} +4 -4
  109. flowfile/web/static/assets/{Union-af6c3d9b.css → Union-d6a8d7d5.css} +7 -7
  110. flowfile/web/static/assets/{Union-b563478a.js → Union-e8ab8c86.js} +8 -6
  111. flowfile/web/static/assets/{Unique-f90db5db.js → Unique-8cd4f976.js} +13 -22
  112. flowfile/web/static/assets/{Sort-3643d625.css → Unique-9fb2f567.css} +10 -10
  113. flowfile/web/static/assets/{Unpivot-1e422df3.css → Unpivot-710a2948.css} +7 -7
  114. flowfile/web/static/assets/{Unpivot-bcb0025f.js → Unpivot-8da14095.js} +10 -8
  115. flowfile/web/static/assets/{UnpivotValidation-c4e73b04.js → UnpivotValidation-6f7d89ff.js} +3 -3
  116. flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
  117. flowfile/web/static/assets/{VueGraphicWalker-bb8535e2.js → VueGraphicWalker-3fb312e1.js} +4 -4
  118. flowfile/web/static/assets/{VueGraphicWalker-ed5ab88b.css → VueGraphicWalker-430f0b86.css} +1 -1
  119. flowfile/web/static/assets/{api-4c8e3822.js → api-24483f0d.js} +1 -1
  120. flowfile/web/static/assets/{api-2d6adc4f.js → api-8b81fa73.js} +1 -1
  121. flowfile/web/static/assets/{dropDown-35135ba8.css → dropDown-3d8dc5fa.css} +40 -40
  122. flowfile/web/static/assets/{dropDown-1bca8a74.js → dropDown-ac0fda9d.js} +3 -3
  123. flowfile/web/static/assets/{fullEditor-2985687e.js → fullEditor-5497a84a.js} +11 -10
  124. flowfile/web/static/assets/{fullEditor-178376bb.css → fullEditor-a0be62b3.css} +74 -62
  125. flowfile/web/static/assets/{genericNodeSettings-924759c7.css → genericNodeSettings-3b2507ea.css} +10 -10
  126. flowfile/web/static/assets/{genericNodeSettings-0476ba4e.js → genericNodeSettings-99014e1d.js} +5 -5
  127. flowfile/web/static/assets/index-07dda503.js +38 -0
  128. flowfile/web/static/assets/index-3ba44389.js +2696 -0
  129. flowfile/web/static/assets/{index-50508d4d.css → index-e6289dd0.css} +1945 -569
  130. flowfile/web/static/assets/{index-246f201c.js → index-fb6493ae.js} +41626 -40869
  131. flowfile/web/static/assets/node.types-2c15bb7e.js +82 -0
  132. flowfile/web/static/assets/nodeInput-0eb13f1a.js +2 -0
  133. flowfile/web/static/assets/{outputCsv-d686eeaf.js → outputCsv-8f8ba42d.js} +3 -3
  134. flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
  135. flowfile/web/static/assets/{outputExcel-8809ea2f.js → outputExcel-393f4fef.js} +3 -3
  136. flowfile/web/static/assets/{outputExcel-b41305c0.css → outputExcel-f5d272b2.css} +26 -26
  137. flowfile/web/static/assets/{outputParquet-53ba645a.js → outputParquet-07c81f65.js} +4 -4
  138. flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
  139. flowfile/web/static/assets/{readCsv-053bf97b.js → readCsv-07f6d9ad.js} +21 -20
  140. flowfile/web/static/assets/{readCsv-bca3ed53.css → readCsv-3bfac4c3.css} +15 -15
  141. flowfile/web/static/assets/{readExcel-e1b381ea.css → readExcel-3db6b763.css} +13 -13
  142. flowfile/web/static/assets/{readExcel-ad531eab.js → readExcel-ed69bc8f.js} +10 -12
  143. flowfile/web/static/assets/{readParquet-cee068e2.css → readParquet-c5244ad5.css} +4 -4
  144. flowfile/web/static/assets/{readParquet-58e899a1.js → readParquet-e3ed4528.js} +4 -7
  145. flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
  146. flowfile/web/static/assets/{selectDynamic-b38de2ba.js → selectDynamic-80b92899.js} +5 -5
  147. flowfile/web/static/assets/{selectDynamic-aa913ff4.css → selectDynamic-f2fb394f.css} +21 -20
  148. flowfile/web/static/assets/{vue-codemirror.esm-db9b8936.js → vue-codemirror.esm-0965f39f.js} +31 -637
  149. flowfile/web/static/assets/{vue-content-loader.es-b5f3ac30.js → vue-content-loader.es-c506ad97.js} +1 -1
  150. flowfile/web/static/index.html +2 -2
  151. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +4 -4
  152. flowfile-0.5.3.dist-info/RECORD +402 -0
  153. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +1 -1
  154. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +1 -0
  155. flowfile_core/__init__.py +13 -3
  156. flowfile_core/auth/jwt.py +51 -16
  157. flowfile_core/auth/models.py +32 -7
  158. flowfile_core/auth/password.py +89 -0
  159. flowfile_core/auth/secrets.py +8 -6
  160. flowfile_core/configs/__init__.py +9 -7
  161. flowfile_core/configs/flow_logger.py +15 -14
  162. flowfile_core/configs/node_store/__init__.py +72 -4
  163. flowfile_core/configs/node_store/nodes.py +155 -172
  164. flowfile_core/configs/node_store/user_defined_node_registry.py +108 -27
  165. flowfile_core/configs/settings.py +28 -15
  166. flowfile_core/database/connection.py +7 -6
  167. flowfile_core/database/init_db.py +96 -2
  168. flowfile_core/database/models.py +3 -1
  169. flowfile_core/fileExplorer/__init__.py +17 -0
  170. flowfile_core/fileExplorer/funcs.py +123 -57
  171. flowfile_core/fileExplorer/utils.py +10 -11
  172. flowfile_core/flowfile/_extensions/real_time_interface.py +10 -8
  173. flowfile_core/flowfile/analytics/analytics_processor.py +27 -24
  174. flowfile_core/flowfile/analytics/graphic_walker.py +11 -12
  175. flowfile_core/flowfile/analytics/utils.py +1 -1
  176. flowfile_core/flowfile/code_generator/code_generator.py +391 -279
  177. flowfile_core/flowfile/connection_manager/_connection_manager.py +6 -5
  178. flowfile_core/flowfile/connection_manager/models.py +1 -1
  179. flowfile_core/flowfile/database_connection_manager/db_connections.py +60 -44
  180. flowfile_core/flowfile/database_connection_manager/models.py +1 -1
  181. flowfile_core/flowfile/extensions.py +17 -12
  182. flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +34 -32
  183. flowfile_core/flowfile/flow_data_engine/create/funcs.py +152 -103
  184. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +526 -477
  185. flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +2 -2
  186. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +92 -52
  187. flowfile_core/flowfile/flow_data_engine/flow_file_column/polars_type.py +12 -11
  188. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +6 -6
  189. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +26 -30
  190. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +43 -32
  191. flowfile_core/flowfile/flow_data_engine/join/__init__.py +1 -1
  192. flowfile_core/flowfile/flow_data_engine/join/utils.py +11 -9
  193. flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +15 -11
  194. flowfile_core/flowfile/flow_data_engine/pivot_table.py +5 -7
  195. flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +95 -82
  196. flowfile_core/flowfile/flow_data_engine/read_excel_tables.py +66 -65
  197. flowfile_core/flowfile/flow_data_engine/sample_data.py +27 -21
  198. flowfile_core/flowfile/flow_data_engine/subprocess_operations/__init__.py +1 -1
  199. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +13 -11
  200. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +360 -191
  201. flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
  202. flowfile_core/flowfile/flow_data_engine/utils.py +101 -67
  203. flowfile_core/flowfile/flow_graph.py +1011 -561
  204. flowfile_core/flowfile/flow_graph_utils.py +31 -49
  205. flowfile_core/flowfile/flow_node/flow_node.py +332 -232
  206. flowfile_core/flowfile/flow_node/models.py +54 -41
  207. flowfile_core/flowfile/flow_node/schema_callback.py +14 -19
  208. flowfile_core/flowfile/graph_tree/graph_tree.py +41 -41
  209. flowfile_core/flowfile/handler.py +82 -32
  210. flowfile_core/flowfile/manage/compatibility_enhancements.py +493 -47
  211. flowfile_core/flowfile/manage/io_flowfile.py +391 -0
  212. flowfile_core/flowfile/node_designer/__init__.py +15 -13
  213. flowfile_core/flowfile/node_designer/_type_registry.py +34 -37
  214. flowfile_core/flowfile/node_designer/custom_node.py +162 -36
  215. flowfile_core/flowfile/node_designer/ui_components.py +136 -35
  216. flowfile_core/flowfile/schema_callbacks.py +77 -54
  217. flowfile_core/flowfile/setting_generator/__init__.py +0 -1
  218. flowfile_core/flowfile/setting_generator/setting_generator.py +6 -5
  219. flowfile_core/flowfile/setting_generator/settings.py +72 -55
  220. flowfile_core/flowfile/sources/external_sources/base_class.py +12 -10
  221. flowfile_core/flowfile/sources/external_sources/custom_external_sources/external_source.py +27 -17
  222. flowfile_core/flowfile/sources/external_sources/custom_external_sources/sample_users.py +9 -9
  223. flowfile_core/flowfile/sources/external_sources/factory.py +0 -1
  224. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +45 -31
  225. flowfile_core/flowfile/sources/external_sources/sql_source/sql_source.py +198 -73
  226. flowfile_core/flowfile/sources/external_sources/sql_source/utils.py +250 -196
  227. flowfile_core/flowfile/util/calculate_layout.py +9 -13
  228. flowfile_core/flowfile/util/execution_orderer.py +25 -17
  229. flowfile_core/flowfile/util/node_skipper.py +4 -4
  230. flowfile_core/flowfile/utils.py +19 -21
  231. flowfile_core/main.py +26 -19
  232. flowfile_core/routes/auth.py +284 -11
  233. flowfile_core/routes/cloud_connections.py +25 -25
  234. flowfile_core/routes/logs.py +21 -29
  235. flowfile_core/routes/public.py +3 -3
  236. flowfile_core/routes/routes.py +77 -43
  237. flowfile_core/routes/secrets.py +25 -27
  238. flowfile_core/routes/user_defined_components.py +483 -4
  239. flowfile_core/run_lock.py +0 -1
  240. flowfile_core/schemas/__init__.py +4 -6
  241. flowfile_core/schemas/analysis_schemas/graphic_walker_schemas.py +55 -55
  242. flowfile_core/schemas/cloud_storage_schemas.py +59 -55
  243. flowfile_core/schemas/input_schema.py +398 -154
  244. flowfile_core/schemas/output_model.py +50 -35
  245. flowfile_core/schemas/schemas.py +207 -67
  246. flowfile_core/schemas/transform_schema.py +1360 -435
  247. flowfile_core/schemas/yaml_types.py +117 -0
  248. flowfile_core/secret_manager/secret_manager.py +17 -13
  249. flowfile_core/{flowfile/node_designer/data_types.py → types.py} +33 -3
  250. flowfile_core/utils/arrow_reader.py +7 -6
  251. flowfile_core/utils/excel_file_manager.py +3 -3
  252. flowfile_core/utils/fileManager.py +7 -7
  253. flowfile_core/utils/fl_executor.py +8 -10
  254. flowfile_core/utils/utils.py +4 -4
  255. flowfile_core/utils/validate_setup.py +5 -4
  256. flowfile_frame/__init__.py +107 -50
  257. flowfile_frame/adapters.py +2 -9
  258. flowfile_frame/adding_expr.py +73 -32
  259. flowfile_frame/cloud_storage/frame_helpers.py +27 -23
  260. flowfile_frame/cloud_storage/secret_manager.py +12 -26
  261. flowfile_frame/config.py +2 -5
  262. flowfile_frame/expr.py +311 -218
  263. flowfile_frame/expr.pyi +160 -159
  264. flowfile_frame/expr_name.py +23 -23
  265. flowfile_frame/flow_frame.py +581 -489
  266. flowfile_frame/flow_frame.pyi +123 -104
  267. flowfile_frame/flow_frame_methods.py +236 -252
  268. flowfile_frame/group_frame.py +50 -20
  269. flowfile_frame/join.py +2 -2
  270. flowfile_frame/lazy.py +129 -87
  271. flowfile_frame/lazy_methods.py +83 -30
  272. flowfile_frame/list_name_space.py +55 -50
  273. flowfile_frame/selectors.py +148 -68
  274. flowfile_frame/series.py +9 -7
  275. flowfile_frame/utils.py +19 -21
  276. flowfile_worker/__init__.py +12 -4
  277. flowfile_worker/configs.py +11 -19
  278. flowfile_worker/create/__init__.py +14 -27
  279. flowfile_worker/create/funcs.py +143 -94
  280. flowfile_worker/create/models.py +139 -68
  281. flowfile_worker/create/pl_types.py +14 -15
  282. flowfile_worker/create/read_excel_tables.py +34 -41
  283. flowfile_worker/create/utils.py +22 -19
  284. flowfile_worker/external_sources/s3_source/main.py +18 -51
  285. flowfile_worker/external_sources/s3_source/models.py +34 -27
  286. flowfile_worker/external_sources/sql_source/main.py +8 -5
  287. flowfile_worker/external_sources/sql_source/models.py +13 -9
  288. flowfile_worker/flow_logger.py +10 -8
  289. flowfile_worker/funcs.py +214 -155
  290. flowfile_worker/main.py +11 -17
  291. flowfile_worker/models.py +35 -28
  292. flowfile_worker/process_manager.py +2 -3
  293. flowfile_worker/routes.py +121 -93
  294. flowfile_worker/secrets.py +9 -6
  295. flowfile_worker/spawner.py +80 -49
  296. flowfile_worker/utils.py +3 -2
  297. shared/__init__.py +2 -7
  298. shared/storage_config.py +25 -13
  299. test_utils/postgres/commands.py +3 -2
  300. test_utils/postgres/fixtures.py +9 -9
  301. test_utils/s3/commands.py +1 -1
  302. test_utils/s3/data_generator.py +3 -4
  303. test_utils/s3/demo_data_generator.py +4 -7
  304. test_utils/s3/fixtures.py +7 -5
  305. tools/migrate/README.md +56 -0
  306. tools/migrate/__init__.py +12 -0
  307. tools/migrate/__main__.py +118 -0
  308. tools/migrate/legacy_schemas.py +682 -0
  309. tools/migrate/migrate.py +610 -0
  310. tools/migrate/tests/__init__.py +0 -0
  311. tools/migrate/tests/conftest.py +21 -0
  312. tools/migrate/tests/test_migrate.py +622 -0
  313. tools/migrate/tests/test_migration_e2e.py +1009 -0
  314. tools/migrate/tests/test_node_migrations.py +843 -0
  315. flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +0 -86
  316. flowfile/web/static/assets/CustomNode-74a37f74.css +0 -32
  317. flowfile/web/static/assets/DatabaseManager-30fa27e5.css +0 -64
  318. flowfile/web/static/assets/Filter-812dcbca.js +0 -164
  319. flowfile/web/static/assets/Filter-f62091b3.css +0 -20
  320. flowfile/web/static/assets/ManualInput-3246a08d.css +0 -96
  321. flowfile/web/static/assets/PivotValidation-891ddfb0.css +0 -13
  322. flowfile/web/static/assets/PivotValidation-c46cd420.css +0 -13
  323. flowfile/web/static/assets/SliderInput-b8fb6a8c.css +0 -4
  324. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +0 -13
  325. flowfile/web/static/assets/outputCsv-9cc59e0b.css +0 -2499
  326. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
  327. flowfile/web/static/assets/secretApi-538058f3.js +0 -46
  328. flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
  329. flowfile-0.4.1.dist-info/RECORD +0 -376
  330. flowfile_core/flowfile/manage/open_flowfile.py +0 -143
  331. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
  332. /flowfile_core/flowfile/manage/manage_flowfile.py → /tools/__init__.py +0 -0
@@ -1,95 +1,129 @@
1
- import polars as pl
2
1
  import os
3
2
 
4
- from flowfile_worker.create.models import ReceivedCsvTable, ReceivedParquetTable, ReceivedExcelTable
3
+ import polars as pl
4
+
5
+ from flowfile_worker.create.models import (
6
+ InputCsvTable,
7
+ InputExcelTable,
8
+ InputJsonTable,
9
+ InputParquetTable,
10
+ ReceivedTable,
11
+ )
12
+ from flowfile_worker.create.read_excel_tables import df_from_calamine_xlsx, df_from_openpyxl
5
13
  from flowfile_worker.create.utils import create_fake_data
6
- from flowfile_worker.create.read_excel_tables import df_from_openpyxl, df_from_calamine_xlsx
7
14
 
8
15
 
9
- def create_from_path_json(received_table: ReceivedCsvTable):
16
+ def create_from_path_json(received_table: ReceivedTable):
17
+ if not isinstance(received_table.table_settings, InputJsonTable):
18
+ raise ValueError("Received table settings are not of type InputJsonTable")
19
+ input_table_settings: InputJsonTable = received_table.table_settings
10
20
  f = received_table.abs_file_path
11
21
  gbs_to_load = os.path.getsize(f) / 1024 / 1000 / 1000
12
22
  low_mem = gbs_to_load > 10
13
- if received_table.encoding.upper() == 'UTF8' or received_table.encoding.upper() == 'UTF-8':
23
+ if input_table_settings.encoding.upper() == "UTF8" or input_table_settings.encoding.upper() == "UTF-8":
14
24
  try:
15
- df = pl.scan_csv(f,
16
- low_memory=low_mem,
17
- try_parse_dates=True,
18
- separator=received_table.delimiter,
19
- has_header=received_table.has_headers,
20
- skip_rows=received_table.starting_from_line,
21
- encoding='utf8',
22
- infer_schema_length=received_table.infer_schema_length)
25
+ df = pl.scan_csv(
26
+ f,
27
+ low_memory=low_mem,
28
+ try_parse_dates=True,
29
+ separator=input_table_settings.delimiter,
30
+ has_header=input_table_settings.has_headers,
31
+ skip_rows=input_table_settings.starting_from_line,
32
+ encoding="utf8",
33
+ infer_schema_length=input_table_settings.infer_schema_length,
34
+ )
23
35
  df.head(1).collect()
24
36
  return df
25
37
  except:
26
38
  try:
27
- df = pl.scan_csv(f, low_memory=low_mem,
28
- separator=received_table.delimiter,
29
- has_header=received_table.has_headers,
30
- skip_rows=received_table.starting_from_line,
31
- encoding='utf8-lossy',
32
- ignore_errors=True)
39
+ df = pl.scan_csv(
40
+ f,
41
+ low_memory=low_mem,
42
+ separator=input_table_settings.delimiter,
43
+ has_header=input_table_settings.has_headers,
44
+ skip_rows=input_table_settings.starting_from_line,
45
+ encoding="utf8-lossy",
46
+ ignore_errors=True,
47
+ )
33
48
  return df
34
49
  except:
35
- df = pl.scan_csv(f, low_memory=low_mem,
36
- separator=received_table.delimiter,
37
- has_header=received_table.has_headers,
38
- skip_rows=received_table.starting_from_line,
39
- encoding='utf8',
40
- ignore_errors=True)
50
+ df = pl.scan_csv(
51
+ f,
52
+ low_memory=low_mem,
53
+ separator=input_table_settings.delimiter,
54
+ has_header=input_table_settings.has_headers,
55
+ skip_rows=input_table_settings.starting_from_line,
56
+ encoding="utf8",
57
+ ignore_errors=True,
58
+ )
41
59
  return df
42
60
  else:
43
- df = pl.read_csv(f, low_memory=low_mem,
44
- separator=received_table.delimiter,
45
- has_header=received_table.has_headers,
46
- skip_rows=received_table.starting_from_line,
47
- encoding=received_table.encoding,
48
- ignore_errors=True)
61
+ df = pl.read_csv(
62
+ f,
63
+ low_memory=low_mem,
64
+ separator=input_table_settings.delimiter,
65
+ has_header=input_table_settings.has_headers,
66
+ skip_rows=input_table_settings.starting_from_line,
67
+ encoding=input_table_settings.encoding,
68
+ ignore_errors=True,
69
+ )
49
70
  return df
50
71
 
51
72
 
52
- def create_from_path_csv(received_table: ReceivedCsvTable) -> pl.DataFrame:
73
+ def create_from_path_csv(received_table: ReceivedTable) -> pl.DataFrame:
53
74
  f = received_table.abs_file_path
75
+ if not isinstance(received_table.table_settings, InputCsvTable):
76
+ raise ValueError("Received table settings are not of type InputCsvTable")
77
+ input_table_settings: InputCsvTable = received_table.table_settings
54
78
  gbs_to_load = os.path.getsize(f) / 1024 / 1000 / 1000
55
79
  low_mem = gbs_to_load > 10
56
- if received_table.encoding.upper() == 'UTF8' or received_table.encoding.upper() == 'UTF-8':
80
+ if input_table_settings.encoding.upper() == "UTF8" or input_table_settings.encoding.upper() == "UTF-8":
57
81
  try:
58
- df = pl.scan_csv(f,
59
- low_memory=low_mem,
60
- try_parse_dates=True,
61
- separator=received_table.delimiter,
62
- has_header=received_table.has_headers,
63
- skip_rows=received_table.starting_from_line,
64
- encoding='utf8',
65
- infer_schema_length=received_table.infer_schema_length)
82
+ df = pl.scan_csv(
83
+ f,
84
+ low_memory=low_mem,
85
+ try_parse_dates=True,
86
+ separator=input_table_settings.delimiter,
87
+ has_header=input_table_settings.has_headers,
88
+ skip_rows=input_table_settings.starting_from_line,
89
+ encoding="utf8",
90
+ infer_schema_length=input_table_settings.infer_schema_length,
91
+ )
66
92
  df.head(1).collect()
67
93
  return df
68
94
  except:
69
95
  try:
70
- df = pl.scan_csv(f, low_memory=low_mem,
71
- separator=received_table.delimiter,
72
- has_header=received_table.has_headers,
73
- skip_rows=received_table.starting_from_line,
74
- encoding='utf8-lossy',
75
- ignore_errors=True)
96
+ df = pl.scan_csv(
97
+ f,
98
+ low_memory=low_mem,
99
+ separator=input_table_settings.delimiter,
100
+ has_header=input_table_settings.has_headers,
101
+ skip_rows=input_table_settings.starting_from_line,
102
+ encoding="utf8-lossy",
103
+ ignore_errors=True,
104
+ )
76
105
  return df
77
106
  except:
78
- df = pl.scan_csv(f, low_memory=low_mem,
79
- separator=received_table.delimiter,
80
- has_header=received_table.has_headers,
81
- skip_rows=received_table.starting_from_line,
82
- encoding='utf8',
83
- ignore_errors=True)
107
+ df = pl.scan_csv(
108
+ f,
109
+ low_memory=low_mem,
110
+ separator=input_table_settings.delimiter,
111
+ has_header=input_table_settings.has_headers,
112
+ skip_rows=input_table_settings.starting_from_line,
113
+ encoding="utf8",
114
+ ignore_errors=True,
115
+ )
84
116
  return df
85
117
  else:
86
- df = pl.read_csv(f,
87
- low_memory=low_mem,
88
- separator=received_table.delimiter,
89
- has_header=received_table.has_headers,
90
- skip_rows=received_table.starting_from_line,
91
- encoding=received_table.encoding,
92
- ignore_errors=True)
118
+ df = pl.read_csv(
119
+ f,
120
+ low_memory=low_mem,
121
+ separator=input_table_settings.delimiter,
122
+ has_header=input_table_settings.has_headers,
123
+ skip_rows=input_table_settings.starting_from_line,
124
+ encoding=input_table_settings.encoding,
125
+ ignore_errors=True,
126
+ )
93
127
  return df
94
128
 
95
129
 
@@ -97,50 +131,65 @@ def create_random(number_of_records: int = 1000) -> pl.LazyFrame:
97
131
  return create_fake_data(number_of_records).lazy()
98
132
 
99
133
 
100
- def create_from_path_parquet(received_table: ReceivedParquetTable):
134
+ def create_from_path_parquet(received_table: ReceivedTable):
135
+ if not isinstance(received_table.table_settings, InputParquetTable):
136
+ raise ValueError("Received table settings are not of type InputParquetTable")
101
137
  low_mem = (os.path.getsize(received_table.abs_file_path) / 1024 / 1000 / 1000) > 2
102
138
  return pl.scan_parquet(source=received_table.abs_file_path, low_memory=low_mem)
103
139
 
104
140
 
105
- def create_from_path_excel(received_table: ReceivedExcelTable):
106
- if received_table.type_inference:
107
- engine = 'openpyxl'
108
- elif received_table.start_row > 0 and received_table.start_column == 0:
109
- engine = 'calamine' if received_table.has_headers else 'xlsx2csv'
110
- elif received_table.start_column > 0 or received_table.start_row > 0:
111
- engine = 'openpyxl'
141
+ def create_from_path_excel(received_table: ReceivedTable):
142
+ if not isinstance(received_table.table_settings, InputExcelTable):
143
+ raise ValueError("Received table settings are not of type InputExcelTable")
144
+ input_table_settings: InputExcelTable = received_table.table_settings
145
+
146
+ if input_table_settings.type_inference:
147
+ engine = "openpyxl"
148
+ elif input_table_settings.start_row > 0 and input_table_settings.start_column == 0:
149
+ engine = "calamine" if input_table_settings.has_headers else "xlsx2csv"
150
+ elif input_table_settings.start_column > 0 or input_table_settings.start_row > 0:
151
+ engine = "openpyxl"
112
152
  else:
113
- engine = 'calamine'
153
+ engine = "calamine"
114
154
 
115
- sheet_name = received_table.sheet_name
155
+ sheet_name = input_table_settings.sheet_name
116
156
 
117
- if engine == 'calamine':
118
- df = df_from_calamine_xlsx(file_path=received_table.abs_file_path, sheet_name=sheet_name,
119
- start_row=received_table.start_row, end_row=received_table.end_row)
120
- if received_table.end_column > 0:
121
- end_col_index = received_table.end_column
122
- cols_to_select = [df.columns[i] for i in range(received_table.start_column, end_col_index)]
157
+ if engine == "calamine":
158
+ df = df_from_calamine_xlsx(
159
+ file_path=received_table.abs_file_path,
160
+ sheet_name=sheet_name,
161
+ start_row=input_table_settings.start_row,
162
+ end_row=input_table_settings.end_row,
163
+ )
164
+ if input_table_settings.end_column > 0:
165
+ end_col_index = input_table_settings.end_column
166
+ cols_to_select = [df.columns[i] for i in range(input_table_settings.start_column, end_col_index)]
123
167
  df = df.select(cols_to_select)
124
168
 
125
- elif engine == 'xlsx2csv':
126
- csv_options = {'has_header': received_table.has_headers, 'skip_rows': received_table.start_row}
127
- df = pl.read_excel(source=received_table.abs_file_path,
128
- read_options=csv_options,
129
- engine='xlsx2csv',
130
- sheet_name=received_table.sheet_name)
131
- end_col_index = received_table.end_column if received_table.end_column > 0 else len(df.columns)
132
- cols_to_select = [df.columns[i] for i in range(received_table.start_column, end_col_index)]
169
+ elif engine == "xlsx2csv":
170
+ csv_options = {"has_header": input_table_settings.has_headers, "skip_rows": input_table_settings.start_row}
171
+ df = pl.read_excel(
172
+ source=received_table.abs_file_path,
173
+ read_options=csv_options,
174
+ engine="xlsx2csv",
175
+ sheet_name=input_table_settings.sheet_name,
176
+ )
177
+ end_col_index = input_table_settings.end_column if input_table_settings.end_column > 0 else len(df.columns)
178
+ cols_to_select = [df.columns[i] for i in range(input_table_settings.start_column, end_col_index)]
133
179
  df = df.select(cols_to_select)
134
- if 0 < received_table.end_row < len(df):
135
- df = df.head(received_table.end_row)
180
+ if 0 < input_table_settings.end_row < len(df):
181
+ df = df.head(input_table_settings.end_row)
136
182
 
137
183
  else:
138
- max_col = received_table.end_column if received_table.end_column > 0 else None
139
- max_row = received_table.end_row + 1 if received_table.end_row > 0 else None
140
- df = df_from_openpyxl(file_path=received_table.abs_file_path,
141
- sheet_name=received_table.sheet_name,
142
- min_row=received_table.start_row + 1,
143
- min_col=received_table.start_column + 1,
144
- max_row=max_row,
145
- max_col=max_col, has_headers=received_table.has_headers)
184
+ max_col = input_table_settings.end_column if input_table_settings.end_column > 0 else None
185
+ max_row = input_table_settings.end_row + 1 if input_table_settings.end_row > 0 else None
186
+ df = df_from_openpyxl(
187
+ file_path=received_table.abs_file_path,
188
+ sheet_name=input_table_settings.sheet_name,
189
+ min_row=input_table_settings.start_row + 1,
190
+ min_col=input_table_settings.start_column + 1,
191
+ max_row=max_row,
192
+ max_col=max_col,
193
+ has_headers=input_table_settings.has_headers,
194
+ )
146
195
  return df
@@ -1,7 +1,8 @@
1
- from pydantic import BaseModel, Field, model_validator
2
- from typing import List, Optional
3
1
  import os
4
2
  from pathlib import Path
3
+ from typing import Annotated, Literal
4
+
5
+ from pydantic import BaseModel, Field, field_validator, model_validator
5
6
 
6
7
 
7
8
  class MinimalFieldInfo(BaseModel):
@@ -9,78 +10,148 @@ class MinimalFieldInfo(BaseModel):
9
10
  data_type: str
10
11
 
11
12
 
12
- class ReceivedTableBase(BaseModel):
13
- id: Optional[int] = None
14
- name: str
15
- path: str
16
- directory: Optional[str] = None
17
- analysis_file_available: Optional[bool] = False
18
- status: Optional[str] = None
19
- file_type: Optional[str] = None
20
- fields: List[MinimalFieldInfo] = Field(default_factory=list)
21
- abs_file_path: Optional[str] = None
13
+ class InputTableBase(BaseModel):
14
+ """Base settings for input file operations."""
22
15
 
23
- @classmethod
24
- def create_from_path(cls, path: str):
25
- filename = os.path.basename(path)
26
- return cls(name=filename, path=path)
16
+ file_type: str # Will be overridden with Literal in subclasses
27
17
 
28
- @property
29
- def file_path(self) -> str:
30
- if self.name not in self.path:
31
- return os.path.join(self.path, self.name)
32
- return self.path
33
18
 
34
- @model_validator(mode="after")
35
- def set_abs_file_path(cls, values):
36
- abs_file_path = getattr(values, "abs_file_path", None)
37
- if abs_file_path is None:
38
- path = getattr(values, "path", None)
39
- if not path:
40
- raise ValueError("Field 'path' is required to compute abs_file_path")
41
- setattr(values, "abs_file_path", str(Path(path).absolute()))
42
- return values
43
-
44
-
45
- class ReceivedCsvTable(ReceivedTableBase):
46
- file_type: Optional[str] = 'csv'
47
- reference: Optional[str] = ''
48
- starting_from_line: Optional[int] = 0
49
- delimiter: Optional[str] = ','
50
- has_headers: Optional[bool] = True
51
- encoding: Optional[str] = 'utf-8'
52
- parquet_ref: Optional[str] = None
53
- row_delimiter: Optional[str] = '\n'
54
- quote_char: Optional[str] = '"'
55
- infer_schema_length: Optional[int] = 10_000
56
- truncate_ragged_lines: Optional[bool] = False
57
- ignore_errors: Optional[bool] = False
58
-
59
-
60
- class ReceivedJsonTable(ReceivedCsvTable):
61
- pass
62
-
63
-
64
- class ReceivedParquetTable(ReceivedTableBase):
65
- file_type: Optional[str] = 'parquet'
66
-
67
-
68
- class ReceivedExcelTable(ReceivedTableBase):
69
- sheet_name: Optional[str] = None
70
- start_row: Optional[int] = 0 # optional
71
- start_column: Optional[int] = 0 # optional
72
- end_row: Optional[int] = 0 # optional
73
- end_column: Optional[int] = 0 # optional
74
- has_headers: Optional[bool] = True # optional
75
- type_inference: Optional[bool] = False # optional
19
+ class InputCsvTable(InputTableBase):
20
+ """Defines settings for reading a CSV file."""
21
+
22
+ file_type: Literal["csv"] = "csv"
23
+ reference: str = ""
24
+ starting_from_line: int = 0
25
+ delimiter: str = ","
26
+ has_headers: bool = True
27
+ encoding: str = "utf-8"
28
+ parquet_ref: str | None = None
29
+ row_delimiter: str = "\n"
30
+ quote_char: str = '"'
31
+ infer_schema_length: int = 10_000
32
+ truncate_ragged_lines: bool = False
33
+ ignore_errors: bool = False
34
+
35
+
36
+ class InputJsonTable(InputCsvTable):
37
+ """Defines settings for reading a JSON file."""
38
+
39
+ file_type: Literal["json"] = "json"
40
+
41
+
42
+ class InputParquetTable(InputTableBase):
43
+ """Defines settings for reading a Parquet file."""
44
+
45
+ file_type: Literal["parquet"] = "parquet"
46
+
47
+
48
+ class InputExcelTable(InputTableBase):
49
+ """Defines settings for reading an Excel file."""
50
+
51
+ file_type: Literal["excel"] = "excel"
52
+ sheet_name: str | None = None
53
+ start_row: int = 0
54
+ start_column: int = 0
55
+ end_row: int = 0
56
+ end_column: int = 0
57
+ has_headers: bool = True
58
+ type_inference: bool = False
76
59
 
60
+ @model_validator(mode="after")
77
61
  def validate_range_values(self):
78
- # Validate that start and end rows/columns are non-negative integers
62
+ """Validates that the Excel cell range is logical."""
79
63
  for attribute in [self.start_row, self.start_column, self.end_row, self.end_column]:
80
64
  if not isinstance(attribute, int) or attribute < 0:
81
65
  raise ValueError("Row and column indices must be non-negative integers")
66
+ if (self.end_row > 0 and self.start_row > self.end_row) or (
67
+ self.end_column > 0 and self.start_column > self.end_column
68
+ ):
69
+ raise ValueError("Start row/column must not be greater than end row/column")
70
+ return self
71
+
72
+
73
+ # Create the discriminated union (similar to OutputTableSettings)
74
+ InputTableSettings = Annotated[
75
+ InputCsvTable | InputJsonTable | InputParquetTable | InputExcelTable, Field(discriminator="file_type")
76
+ ]
77
+
78
+
79
+ # Now create the main ReceivedTable model
80
+ class ReceivedTable(BaseModel):
81
+ """Model for defining a table received from an external source."""
82
+
83
+ # Metadata fields
84
+ id: int | None = None
85
+ name: str | None = None
86
+ path: str # This can be an absolute or relative path
87
+ directory: str | None = None
88
+ analysis_file_available: bool = False
89
+ status: str | None = None
90
+ fields: list[MinimalFieldInfo] = Field(default_factory=list)
91
+ abs_file_path: str | None = None
92
+
93
+ file_type: Literal["csv", "json", "parquet", "excel"]
94
+
95
+ table_settings: InputTableSettings
82
96
 
83
- # Validate that start is before end if end is specified (non-zero)
84
- if (0 < self.end_row < self.start_row) or \
85
- (0 < self.end_column < self.start_column):
86
- raise ValueError("Start row/column must not be greater than end row/column if specified")
97
+ @classmethod
98
+ def create_from_path(cls, path: str, file_type: Literal["csv", "json", "parquet", "excel"] = "csv"):
99
+ """Creates an instance from a file path string."""
100
+ filename = Path(path).name
101
+
102
+ # Create appropriate table_settings based on file_type
103
+ settings_map = {
104
+ "csv": InputCsvTable(),
105
+ "json": InputJsonTable(),
106
+ "parquet": InputParquetTable(),
107
+ "excel": InputExcelTable(),
108
+ }
109
+
110
+ return cls(
111
+ name=filename, path=path, file_type=file_type, table_settings=settings_map.get(file_type, InputCsvTable())
112
+ )
113
+
114
+ @property
115
+ def file_path(self) -> str:
116
+ """Constructs the full file path from the directory and name."""
117
+ if self.name and self.name not in self.path:
118
+ return os.path.join(self.path, self.name)
119
+ else:
120
+ return self.path
121
+
122
+ def set_absolute_filepath(self):
123
+ """Resolves the path to an absolute file path."""
124
+ base_path = Path(self.path).expanduser()
125
+ if not base_path.is_absolute():
126
+ base_path = Path.cwd() / base_path
127
+ if self.name and self.name not in base_path.name:
128
+ base_path = base_path / self.name
129
+ self.abs_file_path = str(base_path.resolve())
130
+
131
+ @field_validator("table_settings", mode="before")
132
+ @classmethod
133
+ def validate_table_settings(cls, v, info):
134
+ """Ensures table_settings matches the file_type."""
135
+ if v is None:
136
+ file_type = info.data.get("file_type", "csv")
137
+ # Create default based on file_type
138
+ settings_map = {
139
+ "csv": InputCsvTable(),
140
+ "json": InputJsonTable(),
141
+ "parquet": InputParquetTable(),
142
+ "excel": InputExcelTable(),
143
+ }
144
+ return settings_map.get(file_type, InputCsvTable())
145
+
146
+ # If it's a dict, add file_type if missing
147
+ if isinstance(v, dict) and "file_type" not in v:
148
+ v["file_type"] = info.data.get("file_type", "csv")
149
+
150
+ return v
151
+
152
+ @model_validator(mode="after")
153
+ def populate_abs_file_path(self):
154
+ """Ensures the absolute file path is populated after validation."""
155
+ if not self.abs_file_path:
156
+ self.set_absolute_filepath()
157
+ return self
@@ -1,21 +1,20 @@
1
1
  import polars as pl
2
2
 
3
-
4
3
  dtype_to_pl = {
5
- 'int': pl.Int64,
6
- 'integer': pl.Int64,
7
- 'char': pl.String,
8
- 'fixed decimal': pl.Float32,
9
- 'double': pl.Float64,
10
- 'float': pl.Float64,
11
- 'bool': pl.Boolean,
12
- 'byte': pl.UInt8,
13
- 'bit': pl.Binary,
14
- 'date': pl.Date,
15
- 'datetime': pl.Datetime,
16
- 'string': pl.String,
17
- 'str': pl.String,
18
- 'time': pl.Time,
4
+ "int": pl.Int64,
5
+ "integer": pl.Int64,
6
+ "char": pl.String,
7
+ "fixed decimal": pl.Float32,
8
+ "double": pl.Float64,
9
+ "float": pl.Float64,
10
+ "bool": pl.Boolean,
11
+ "byte": pl.UInt8,
12
+ "bit": pl.Binary,
13
+ "date": pl.Date,
14
+ "datetime": pl.Datetime,
15
+ "string": pl.String,
16
+ "str": pl.String,
17
+ "time": pl.Time,
19
18
  }
20
19
 
21
20
  dtype_to_pl_str = {k: v.__name__ for k, v in dtype_to_pl.items()}