sibi-flux 2026.1.6__tar.gz → 2026.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/PKG-INFO +1 -1
  2. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/pyproject.toml +1 -1
  3. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/cli.py +174 -40
  4. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/README.md +0 -0
  5. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_dst/__init__.py +0 -0
  6. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/__init__.py +0 -0
  7. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/__init__.py +0 -0
  8. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/base.py +0 -0
  9. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/parquet.py +0 -0
  10. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/parquet_engine/__init__.py +0 -0
  11. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/parquet_engine/executor.py +0 -0
  12. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/parquet_engine/manifest.py +0 -0
  13. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/artifacts/parquet_engine/planner.py +0 -0
  14. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/cli.py +0 -0
  15. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/config/__init__.py +0 -0
  16. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/config/manager.py +0 -0
  17. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/config/settings.py +0 -0
  18. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/core/__init__.py +0 -0
  19. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/core/managed_resource/__init__.py +0 -0
  20. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/core/managed_resource/_managed_resource.py +0 -0
  21. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/core/type_maps/__init__.py +0 -0
  22. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dask_cluster/__init__.py +0 -0
  23. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dask_cluster/async_core.py +0 -0
  24. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dask_cluster/client_manager.py +0 -0
  25. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dask_cluster/core.py +0 -0
  26. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dask_cluster/exceptions.py +0 -0
  27. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dask_cluster/utils.py +0 -0
  28. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/__init__.py +0 -0
  29. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/_data_cube.py +0 -0
  30. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/config_engine.py +0 -0
  31. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/field_factory.py +0 -0
  32. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/field_mapper.py +0 -0
  33. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/field_registry.py +0 -0
  34. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/generator.py +0 -0
  35. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/orchestrator.py +0 -0
  36. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/datacube/router.py +0 -0
  37. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dataset/__init__.py +0 -0
  38. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dataset/_dataset.py +0 -0
  39. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/dataset/hybrid_loader.py +0 -0
  40. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_enricher/__init__.py +0 -0
  41. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_enricher/async_enricher.py +0 -0
  42. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_enricher/attacher.py +0 -0
  43. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_enricher/merger.py +0 -0
  44. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_enricher/specs.py +0 -0
  45. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_enricher/types.py +0 -0
  46. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/__init__.py +0 -0
  47. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/_df_helper.py +0 -0
  48. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/__init__.py +0 -0
  49. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/_params.py +0 -0
  50. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/_strategies.py +0 -0
  51. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/http/__init__.py +0 -0
  52. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/http/_http_config.py +0 -0
  53. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/parquet/__init__.py +0 -0
  54. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/parquet/_parquet_options.py +0 -0
  55. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/__init__.py +0 -0
  56. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/_db_connection.py +0 -0
  57. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/_db_gatekeeper.py +0 -0
  58. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/_io_dask.py +0 -0
  59. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/_load_from_db.py +0 -0
  60. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/_model_registry.py +0 -0
  61. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/sqlalchemy/_sql_model_builder.py +0 -0
  62. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/backends/utils.py +0 -0
  63. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/core/__init__.py +0 -0
  64. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/core/_defaults.py +0 -0
  65. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/core/_filter_handler.py +0 -0
  66. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/core/_params_config.py +0 -0
  67. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_helper/core/_query_config.py +0 -0
  68. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_validator/__init__.py +0 -0
  69. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/df_validator/_df_validator.py +0 -0
  70. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/__init__.py +0 -0
  71. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/app.py +0 -0
  72. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/core.py +0 -0
  73. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/cube_extender.py +0 -0
  74. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/cube_proposer.py +0 -0
  75. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/discovery_updater.py +0 -0
  76. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/env.py +0 -0
  77. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/env_engine.py +0 -0
  78. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/env_generator.py +0 -0
  79. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/rule_generator.py +0 -0
  80. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/templates/__init__.py +0 -0
  81. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/templates/discovery_params.yaml +0 -0
  82. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/templates/gen_dc.py +0 -0
  83. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/init/templates/property_template.yaml +0 -0
  84. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/logger/__init__.py +0 -0
  85. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/logger/_logger.py +0 -0
  86. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/mcp/__init__.py +0 -0
  87. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/mcp/client.py +0 -0
  88. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/mcp/router.py +0 -0
  89. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/orchestration/__init__.py +0 -0
  90. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/orchestration/_artifact_orchestrator.py +0 -0
  91. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/orchestration/_pipeline_executor.py +0 -0
  92. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/osmnx_helper/__init__.py +0 -0
  93. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/osmnx_helper/_pbf_handler.py +0 -0
  94. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/osmnx_helper/graph_loader.py +0 -0
  95. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/osmnx_helper/utils.py +0 -0
  96. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/__init__.py +0 -0
  97. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/readers/__init__.py +0 -0
  98. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/readers/base.py +0 -0
  99. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/readers/parquet.py +0 -0
  100. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/saver/__init__.py +0 -0
  101. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/saver/_parquet_saver.py +0 -0
  102. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/parquet/saver/_write_gatekeeper.py +0 -0
  103. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/pipelines/__init__.py +0 -0
  104. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/pipelines/base.py +0 -0
  105. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/pipelines/template.py +0 -0
  106. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/py.typed +0 -0
  107. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/readers/__init__.py +0 -0
  108. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/readers/base.py +0 -0
  109. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/storage/__init__.py +0 -0
  110. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/storage/_fs_registry.py +0 -0
  111. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/storage/_storage_manager.py +0 -0
  112. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/storage/factory.py +0 -0
  113. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/__init__.py +0 -0
  114. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/clickhouse_writer/__init__.py +0 -0
  115. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/clickhouse_writer/_clickhouse_writer.py +0 -0
  116. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/common.py +0 -0
  117. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/dask_utils.py +0 -0
  118. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/data_utils/__init__.py +0 -0
  119. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/data_utils/_data_utils.py +0 -0
  120. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/dataframe_utils.py +0 -0
  121. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/date_utils/__init__.py +0 -0
  122. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/date_utils/_business_days.py +0 -0
  123. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/date_utils/_date_utils.py +0 -0
  124. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/date_utils/_file_age_checker.py +0 -0
  125. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/file_utils.py +0 -0
  126. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/filepath_generator/__init__.py +0 -0
  127. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/filepath_generator/_filepath_generator.py +0 -0
  128. {sibi_flux-2026.1.6 → sibi_flux-2026.1.8}/src/sibi_flux/utils/retry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sibi-flux
3
- Version: 2026.1.6
3
+ Version: 2026.1.8
4
4
  Summary: Sibi Toolkit: A collection of tools for Data Analysis/Engineering.
5
5
  Author: Luis Valverde
6
6
  Author-email: Luis Valverde <lvalverdeb@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sibi-flux"
3
- version = "2026.1.6"
3
+ version = "2026.1.8"
4
4
  description = "Sibi Toolkit: A collection of tools for Data Analysis/Engineering."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -478,6 +478,35 @@ def sync(
478
478
  is_append = False
479
479
  existing_content = ""
480
480
 
481
+ # --- Registry Collection (Always run, even if skipped) ---
482
+ # Structure: {conf_obj: {table_name: {class_name: ..., path: ...}}}
483
+ for item in items:
484
+ t_name = item[0]
485
+ conf_obj = item[1]
486
+ cls_n = item[4]
487
+ # Calculate path relative to project root
488
+ try:
489
+ if "project_root" not in locals():
490
+ project_root = config_path.parent.parent.parent
491
+ rel_path = file_path.relative_to(project_root)
492
+ except Exception:
493
+ rel_path = file_path
494
+
495
+ if conf_obj not in generated_registry:
496
+ generated_registry[conf_obj] = {}
497
+
498
+ # Preserve custom_name from existing registry if present
499
+ existing_meta = registry.get_table_details(t_name)
500
+ custom_name = existing_meta.get("custom_name")
501
+
502
+ entry_data = {
503
+ "class_name": cls_n,
504
+ "path": str(rel_path),
505
+ "custom_name": custom_name,
506
+ }
507
+
508
+ generated_registry[conf_obj][t_name] = entry_data
509
+
481
510
  if file_path.exists() and not force:
482
511
  with open(file_path, "r") as f:
483
512
  existing_content = f.read()
@@ -524,12 +553,8 @@ def sync(
524
553
  )
525
554
 
526
555
  if not classes_code:
527
- if not is_append:
528
- summary_table.add_row(
529
- file_path_str, "0", "[red]Failed (No Classes Generated)[/red]"
530
- )
531
- else:
532
- summary_table.add_row(file_path_str, "0", "[red]Failed to Append[/red]")
556
+ status_msg = "[red]Failed (No Classes Generated)[/red]" if not is_append else "[red]Failed to Append[/red]"
557
+ summary_table.add_row(file_path_str, "0", status_msg)
533
558
  continue
534
559
 
535
560
  if not is_append:
@@ -558,36 +583,6 @@ def sync(
558
583
  )
559
584
  summary_table.add_row(file_path_str, str(len(items)), status_msg)
560
585
 
561
- # --- Registry Collection ---
562
- # Collect metadata for generated datacubes
563
- # Structure: {conf_obj: {table_name: {class_name: ..., path: ...}}}
564
- for item in items:
565
- t_name = item[0]
566
- conf_obj = item[1]
567
- cls_n = item[4]
568
- # Calculate path relative to project root
569
- try:
570
- if "project_root" not in locals():
571
- project_root = config_path.parent.parent.parent
572
- rel_path = file_path.relative_to(project_root)
573
- except Exception:
574
- rel_path = file_path
575
-
576
- if conf_obj not in generated_registry:
577
- generated_registry[conf_obj] = {}
578
-
579
- # Preserve custom_name from existing registry if present
580
- existing_meta = registry.get_table_details(t_name)
581
- custom_name = existing_meta.get("custom_name")
582
-
583
- entry_data = {
584
- "class_name": cls_n,
585
- "path": str(rel_path),
586
- "custom_name": custom_name,
587
- }
588
-
589
- generated_registry[conf_obj][t_name] = entry_data
590
-
591
586
  console.print(summary_table)
592
587
 
593
588
  # --- Write Datacube Registry ---
@@ -616,8 +611,9 @@ def sync(
616
611
  with open(reg_file, "w") as f:
617
612
  yaml.dump(reg_data, f, sort_keys=False)
618
613
 
614
+ total_tables = sum(len(tables) for tables in generated_registry.values())
619
615
  console.print(
620
- f"[green]Updated Datacube Registry at {reg_rel_path} ({len(generated_registry)} entries)[/green]"
616
+ f"[green]Updated Datacube Registry at {reg_rel_path} ({total_tables} tables across {len(generated_registry)} groups)[/green]"
621
617
  )
622
618
  except Exception as e:
623
619
  console.print(f"[red]Failed to write Datacube Registry: {e}[/red]")
@@ -815,12 +811,29 @@ def discover(
815
811
  )
816
812
  continue
817
813
 
814
+ # Resolve Registry Path (Target)
815
+ reg_rel_path = params.get("paths", {}).get("repositories", {}).get(
816
+ "global_datacube_registry_file"
817
+ ) or params.get("global_datacube_registry_file")
818
+
819
+ real_registry_path = str(config_path) # Fallback to config if not defined (legacy behavior)
820
+ if reg_rel_path:
821
+ if Path(reg_rel_path).is_absolute():
822
+ real_registry_path = reg_rel_path
823
+ else:
824
+ try:
825
+ # Anchor to project root
826
+ prj_root = config_path.parent.parent.parent
827
+ real_registry_path = str(prj_root / reg_rel_path)
828
+ except Exception:
829
+ real_registry_path = str(config_path.parent / reg_rel_path)
830
+
818
831
  orchestrator = DiscoveryOrchestrator(
819
832
  field_registry=field_registry,
820
833
  params=context.params,
821
834
  rules_path=str(rules_path),
822
835
  whitelist_path=str(whitelist_path),
823
- registry_path=str(config_path),
836
+ registry_path=real_registry_path,
824
837
  db_connection_str=db_conn_str,
825
838
  db_config=db_config,
826
839
  )
@@ -1001,8 +1014,21 @@ def scan(
1001
1014
  raise typer.Exit(code=1)
1002
1015
 
1003
1016
  # Resolve global output file
1004
- global_tables_file = params.get("all_tables_file") or "all_tables.yaml"
1005
- global_tables_path = config_path.parent / global_tables_file
1017
+ # First check nested discovery block (standard), then root (fallback)
1018
+ discovery_conf = params.get("discovery", {})
1019
+ global_tables_file = (
1020
+ discovery_conf.get("all_tables_file")
1021
+ or params.get("all_tables_file")
1022
+ or "all_tables.yaml"
1023
+ )
1024
+
1025
+ # If using absolute path (resolved from context), use it directly
1026
+ # Otherwise treat as relative to config file location
1027
+ path_obj = Path(global_tables_file)
1028
+ if path_obj.is_absolute():
1029
+ global_tables_path = path_obj
1030
+ else:
1031
+ global_tables_path = config_path.parent / global_tables_file
1006
1032
 
1007
1033
  # Load existing data to preserve config for DBs not being scanned
1008
1034
  all_tables_data = {}
@@ -1480,6 +1506,114 @@ def map(
1480
1506
  return
1481
1507
 
1482
1508
 
1509
+ @app.command()
1510
+ def workflow(
1511
+ config_file: Optional[Path] = typer.Option(None, "--config"),
1512
+ db_url_map: Optional[str] = typer.Option(None, "--db-urls"),
1513
+ env_file: Optional[Path] = typer.Option(None, "--env-file", "-e"),
1514
+ db_name: Optional[str] = typer.Option(
1515
+ None, "--db", help="Target specific database (filtering map/sync)"
1516
+ ),
1517
+ app_name: Optional[str] = typer.Option(
1518
+ None, "--app-name", help="Run create_cubes for the specified application"
1519
+ ),
1520
+ force: bool = typer.Option(False, "--force", "-f", help="Force rebuild/overwrite"),
1521
+ dry_run: bool = typer.Option(
1522
+ False, "--dry-run", help="Preview changes without executing"
1523
+ ),
1524
+ ) -> None:
1525
+ """
1526
+ Executes the full Datacube workflow: Scan -> Propose -> Discover -> Map -> Sync -> (Optional) Create Cubes.
1527
+ """
1528
+ console.rule("[bold magenta]Starting Datacube Workflow[/]")
1529
+
1530
+ # Resolve env_file: CLI > Params > Default (Consistent with discover/sync)
1531
+ if env_file:
1532
+ env_path = env_file
1533
+ elif context.params and context.params.get("defaults", {}).get("env_file"):
1534
+ env_path = Path(context.params.get("defaults", {})["env_file"])
1535
+ else:
1536
+ env_path = Path(".env.linux")
1537
+ load_environment(env_path, logger=console.print)
1538
+
1539
+ # Ensure context is configured with params from the config file
1540
+ config_path = config_file or context.default_config
1541
+ if config_path and config_path.exists():
1542
+ # Load and resolve config to ensure context.params is populated
1543
+ # This is critical for 'discover' to find 'databases' list
1544
+ try:
1545
+ config_data = _load_and_resolve_config(config_path)
1546
+ context.params = config_data
1547
+ context.default_config = config_path
1548
+ # We don't have easy access to other set_context_defaults args here,
1549
+ # but params is what 'discover' needs most.
1550
+ console.print(f"[dim]Loaded workflow context from {config_path}[/dim]")
1551
+ except Exception as e:
1552
+ console.print(f"[yellow]Warning: Failed to load context from {config_path}: {e}[/yellow]")
1553
+
1554
+ # Step 0: Scan & Propose Rules (Critical for new projects/drift)
1555
+ console.print(f"\n[bold cyan]Step 0a: Scan (Introspection)[/]")
1556
+ scan(
1557
+ config_file=config_file,
1558
+ db_url_map=db_url_map,
1559
+ env_file=env_file,
1560
+ db_name=db_name,
1561
+ )
1562
+
1563
+ console.print(f"\n[bold cyan]Step 0b: Propose Rules[/]")
1564
+ propose_rules(
1565
+ config_file=config_file,
1566
+ dry_run=dry_run, # Preview rules if dry-running
1567
+ )
1568
+
1569
+ # Step 1: Discover
1570
+ # Note: Discover currently runs for all DBs in config.
1571
+ console.print(f"\n[bold cyan]Step 1: Discovery[/]")
1572
+ discover(
1573
+ config_file=config_file,
1574
+ db_conf="replica_db_conf", # Pass explicit string to avoid Typer OptionInfo default
1575
+ db_url_map=db_url_map,
1576
+ env_file=env_file,
1577
+ update=True, # Always update registry to enable chaining
1578
+ prune=False, # Safer default? Or should we expose prune?
1579
+ run_sync=False, # We handle sync explicitly later
1580
+ dry_run=dry_run,
1581
+ generate_fields=False, # We use map command for this
1582
+ force=False, # Discovery force is about fields usually?
1583
+ )
1584
+
1585
+ # Step 2: Map
1586
+ console.print(f"\n[bold cyan]Step 2: Map Generation[/]")
1587
+ map(
1588
+ config_file=config_file,
1589
+ db_url_map=db_url_map,
1590
+ env_file=env_file,
1591
+ db_name=db_name,
1592
+ force=force,
1593
+ )
1594
+
1595
+ # Step 3: Sync
1596
+ console.print(f"\n[bold cyan]Step 3: Sync (Code Generation)[/]")
1597
+ sync(
1598
+ config_file=config_file,
1599
+ db_url_map=db_url_map,
1600
+ env_file=env_file, # sync command has env_file
1601
+ force=force,
1602
+ dry_run=dry_run,
1603
+ )
1604
+
1605
+ # Step 4: Create Cubes (Optional)
1606
+ if app_name:
1607
+ console.print(f"\n[bold cyan]Step 4: Application Cubes ({app_name})[/]")
1608
+ if dry_run:
1609
+ console.print("[yellow]Skipping create_cubes in dry-run mode.[/yellow]")
1610
+ else:
1611
+ from sibi_flux.init.cube_extender import create_cubes
1612
+ create_cubes(app_name)
1613
+
1614
+ console.rule("[bold magenta]Workflow Completed[/]")
1615
+
1616
+
1483
1617
  @app.command()
1484
1618
  def init(
1485
1619
  config_file: Optional[Path] = typer.Option(None, "--config"),
File without changes