datajunction 0.0.182__tar.gz → 0.0.184__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.
- {datajunction-0.0.182 → datajunction-0.0.184}/Makefile +1 -1
- {datajunction-0.0.182 → datajunction-0.0.184}/PKG-INFO +2 -3
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/__about__.py +1 -1
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/_internal.py +2 -12
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/cli.py +137 -41
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/deployment.py +26 -2
- datajunction-0.0.184/datajunction/skills/datajunction-api.md +232 -0
- datajunction-0.0.184/datajunction/skills/datajunction-query.md +248 -0
- datajunction-0.0.184/datajunction/skills/datajunction-repo.md +692 -0
- datajunction-0.0.184/datajunction/skills/datajunction-semantic-model.md +379 -0
- datajunction-0.0.184/datajunction/skills/datajunction.md +253 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/pyproject.toml +1 -1
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_cli.py +151 -25
- datajunction-0.0.184/tests/test_skills.py +193 -0
- datajunction-0.0.182/datajunction/skills/datajunction.md +0 -1823
- {datajunction-0.0.182 → datajunction-0.0.184}/.coveragerc +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/.gitignore +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/.pre-commit-config.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/LICENSE.txt +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/README.md +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/claude_desktop_config.example.json +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/__init__.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/_base.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/admin.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/builder.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/client.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/compile.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/exceptions.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/mcp/__init__.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/mcp/cli.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/mcp/config.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/models.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/nodes.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/rendering.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/init_system_nodes.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/date.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/deployment.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/dimension_link.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/distinct_node_authors.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/distinct_node_editors.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/is_active.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/materialization.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/median_deployment_duration_seconds.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/median_dim_links_per_node.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/median_dimension_indegree.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/median_downstream_count.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/median_revisions_per_node.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/median_upstream_count.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/namespace.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/node_revision.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/node_type.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/nodes.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_branches.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_deployments.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_materializations.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_namespaces.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_nodes.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_orphan_nodes.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/number_of_unused_dimensions.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/repo.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/seed/nodes/user.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/datajunction/tags.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/setup.cfg +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/__init__.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/conftest.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/roads/companies.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/roads/companies_dim.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/roads/contractor.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/roads/contractors.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/roads/us_state.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/deploy0/roads/us_states.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/avg_length_of_employment.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/avg_repair_price.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/avg_time_to_dispatch.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/contractor.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/contractors.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/date.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/date_dim.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/dispatcher.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/dispatchers.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/hard_hat.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/hard_hat_state.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/hard_hats.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/local_hard_hats.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/municipality.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/municipality_dim.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/municipality_municipality_type.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/municipality_type.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/national_level_agg.transform.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/num_repair_orders.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/regional_level_agg.transform.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/regional_repair_efficiency.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/repair_order.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/repair_order_details.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/repair_order_transform.transform.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/repair_orders.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/repair_orders_cube.cube.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/repair_type.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/total_repair_cost.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/total_repair_order_discounts.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/us_region.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/us_state.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project1/roads/us_states.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project10/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/avg_length_of_employment.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/avg_repair_price.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/avg_time_to_dispatch.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/contractor.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/contractors.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/date.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/date_dim.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/dispatcher.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/dispatchers.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/hard_hat.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/hard_hat_state.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/hard_hats.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/local_hard_hats.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/municipality.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/municipality_dim.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/municipality_municipality_type.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/municipality_type.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/national_level_agg.transform.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/num_repair_orders.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/regional_level_agg.transform.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/regional_repair_efficiency.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/repair_order.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/repair_order_details.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/repair_order_transform.transform.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/repair_orders.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/repair_orders_cube.cube.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/repair_type.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/total_repair_cost.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/total_repair_order_discounts.metric.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/us_region.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/us_state.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project11/us_states.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/roads/companies.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/roads/companies_dim.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/roads/contractor.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/roads/contractors.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/roads/us_state.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project12/roads/us_states.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project2/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project2/some_node.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project3/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project3/some_node.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project4/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project4/very/very/deeply/nested/namespace/some_node.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project5/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project5/some_node.a.b.c.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project6/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project6/roads/contractor.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project6/roads/contractors.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project7/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project7/roads/contractor.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project7/roads/contractors.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project8/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/dj.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/roads/companies.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/roads/companies_dim.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/roads/contractor.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/roads/contractors.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/roads/us_state.dimension.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples/project9/roads/us_states.source.yaml +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/examples.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/mcp/README.md +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/mcp/__init__.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/mcp/test_cli.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test__internal.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_admin.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_base.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_builder.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_client.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_compile.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_deploy.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_generated_client.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_integration.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tests/test_models.py +0 -0
- {datajunction-0.0.182 → datajunction-0.0.184}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datajunction
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.184
|
|
4
4
|
Summary: DataJunction client library for connecting to a DataJunction server
|
|
5
5
|
Project-URL: repository, https://github.com/DataJunction/dj
|
|
6
6
|
Author-email: DataJunction Authors <yian.shang@gmail.com>
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
14
14
|
Requires-Python: <4.0,>=3.10
|
|
15
15
|
Requires-Dist: alive-progress>=3.1.2
|
|
16
16
|
Requires-Dist: httpx>=0.27.0
|
|
17
|
+
Requires-Dist: pandas>=2.0.2
|
|
17
18
|
Requires-Dist: pytest-xdist>=3.5.0
|
|
18
19
|
Requires-Dist: pyyaml>=6.0.1
|
|
19
20
|
Requires-Dist: requests<3.0.0,>=2.28.2
|
|
@@ -22,8 +23,6 @@ Provides-Extra: mcp
|
|
|
22
23
|
Requires-Dist: mcp>=1.0.0; extra == 'mcp'
|
|
23
24
|
Requires-Dist: pydantic-settings>=2.10.1; extra == 'mcp'
|
|
24
25
|
Requires-Dist: pydantic>=2.0; extra == 'mcp'
|
|
25
|
-
Provides-Extra: pandas
|
|
26
|
-
Requires-Dist: pandas>=2.0.2; extra == 'pandas'
|
|
27
26
|
Description-Content-Type: text/markdown
|
|
28
27
|
|
|
29
28
|
# DataJunction Python Client
|
|
@@ -4,21 +4,11 @@
|
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
6
|
import platform
|
|
7
|
-
import warnings
|
|
8
7
|
from dataclasses import asdict, dataclass
|
|
9
8
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, TypedDict, Union
|
|
10
9
|
from urllib.parse import urljoin
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
import pandas as pd
|
|
14
|
-
except ImportError: # pragma: no cover
|
|
15
|
-
warnings.warn(
|
|
16
|
-
(
|
|
17
|
-
"Optional dependency `pandas` not found, data retrieval"
|
|
18
|
-
"disabled. You can install pandas by running `pip install pandas`."
|
|
19
|
-
),
|
|
20
|
-
ImportWarning,
|
|
21
|
-
)
|
|
11
|
+
import pandas as pd
|
|
22
12
|
import requests
|
|
23
13
|
from requests.adapters import CaseInsensitiveDict, HTTPAdapter
|
|
24
14
|
|
|
@@ -271,7 +261,7 @@ class DJClient:
|
|
|
271
261
|
@staticmethod
|
|
272
262
|
def process_results(results) -> "pd.DataFrame":
|
|
273
263
|
"""
|
|
274
|
-
Return a pandas dataframe of the results
|
|
264
|
+
Return a pandas dataframe of the results
|
|
275
265
|
"""
|
|
276
266
|
if "results" in results and results["results"]:
|
|
277
267
|
columns = results["results"][0]["columns"]
|
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Optional
|
|
8
|
+
from typing import Any, Optional
|
|
9
9
|
|
|
10
10
|
from rich import box
|
|
11
11
|
from rich.console import Console, Group
|
|
@@ -601,10 +601,15 @@ class DJCLI:
|
|
|
601
601
|
)
|
|
602
602
|
return
|
|
603
603
|
|
|
604
|
-
# Handle error responses
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
604
|
+
# Handle error responses and raw dict responses.
|
|
605
|
+
# data() / node_data() should return a DataFrame via process_results,
|
|
606
|
+
# but if the raw query response dict is returned instead, try to
|
|
607
|
+
# process it here rather than crashing on result.columns.
|
|
608
|
+
if isinstance(result, dict):
|
|
609
|
+
if "message" in result:
|
|
610
|
+
console.print(f"[bold red]ERROR:[/bold red] {result['message']}")
|
|
611
|
+
return
|
|
612
|
+
result = self.builder_client.process_results(result)
|
|
608
613
|
|
|
609
614
|
# result should be a DataFrame (already limited by API)
|
|
610
615
|
if format == "json":
|
|
@@ -958,6 +963,27 @@ class DJCLI:
|
|
|
958
963
|
default="GITHUB_TOKEN",
|
|
959
964
|
help="Name of the env var holding the GitHub token (default: GITHUB_TOKEN)",
|
|
960
965
|
)
|
|
966
|
+
codeowners_parser.add_argument(
|
|
967
|
+
"--default-owner",
|
|
968
|
+
type=str,
|
|
969
|
+
default=None,
|
|
970
|
+
help=(
|
|
971
|
+
"Owner for a leading `* <owner>` rule. Unmatched files and any "
|
|
972
|
+
"--exclude'd directories fall through to it (e.g. '@org/team')."
|
|
973
|
+
),
|
|
974
|
+
)
|
|
975
|
+
codeowners_parser.add_argument(
|
|
976
|
+
"--exclude",
|
|
977
|
+
action="append",
|
|
978
|
+
default=None,
|
|
979
|
+
dest="exclude_dirs",
|
|
980
|
+
metavar="DIR",
|
|
981
|
+
help=(
|
|
982
|
+
"Directory (relative to <directory>) whose nodes get no per-file "
|
|
983
|
+
"owners — they fall through to --default-owner. Repeatable. Use for "
|
|
984
|
+
"machine-generated trees like 'nodes/generated'."
|
|
985
|
+
),
|
|
986
|
+
)
|
|
961
987
|
|
|
962
988
|
# `dj pull <namespace> <directory>`
|
|
963
989
|
pull_parser = subparsers.add_parser(
|
|
@@ -1489,6 +1515,8 @@ class DJCLI:
|
|
|
1489
1515
|
output=args.output,
|
|
1490
1516
|
github_api_url=args.github_api_url,
|
|
1491
1517
|
github_token_env=args.github_token_env,
|
|
1518
|
+
default_owner=args.default_owner,
|
|
1519
|
+
exclude_dirs=args.exclude_dirs,
|
|
1492
1520
|
)
|
|
1493
1521
|
console = Console()
|
|
1494
1522
|
console.print(
|
|
@@ -1620,52 +1648,115 @@ class DJCLI:
|
|
|
1620
1648
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
1621
1649
|
|
|
1622
1650
|
console.print(
|
|
1623
|
-
"[bold]📚 Installing DJ
|
|
1651
|
+
"[bold]📚 Installing DJ skills[/bold]\n",
|
|
1624
1652
|
)
|
|
1625
1653
|
|
|
1626
|
-
# Load bundled skill from package
|
|
1627
|
-
dir_name = "datajunction"
|
|
1628
|
-
skill_dir = output_dir / dir_name
|
|
1629
|
-
skill_file = skill_dir / "SKILL.md"
|
|
1630
|
-
|
|
1631
|
-
console.print(f"Installing [cyan]{dir_name}[/cyan]...")
|
|
1632
|
-
|
|
1633
|
-
# Find bundled skill file
|
|
1634
1654
|
from datajunction import __version__ as dj_version
|
|
1635
1655
|
from importlib.resources import files
|
|
1636
1656
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1657
|
+
# All bundled DJ skills. ``datajunction`` is the core concepts
|
|
1658
|
+
# skill (always loaded); the others are audience-specific
|
|
1659
|
+
# extensions that compose on top of it.
|
|
1660
|
+
bundled_skills: list[dict[str, Any]] = [
|
|
1661
|
+
{
|
|
1662
|
+
"name": "datajunction",
|
|
1663
|
+
"filename": "datajunction.md",
|
|
1664
|
+
"description": "Core DataJunction concepts and vocabulary",
|
|
1665
|
+
"keywords": [
|
|
1666
|
+
"DataJunction",
|
|
1667
|
+
"DJ",
|
|
1668
|
+
"semantic layer",
|
|
1669
|
+
"dimension link",
|
|
1670
|
+
"star schema",
|
|
1671
|
+
"node types",
|
|
1672
|
+
],
|
|
1673
|
+
},
|
|
1674
|
+
{
|
|
1675
|
+
"name": "datajunction-query",
|
|
1676
|
+
"filename": "datajunction-query.md",
|
|
1677
|
+
"description": "Querying DJ metrics via MCP tools and APIs",
|
|
1678
|
+
"keywords": [
|
|
1679
|
+
"query metric",
|
|
1680
|
+
"generate SQL",
|
|
1681
|
+
"get metric data",
|
|
1682
|
+
"search_nodes",
|
|
1683
|
+
"build_metric_sql",
|
|
1684
|
+
"common dimensions",
|
|
1685
|
+
],
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
"name": "datajunction-semantic-model",
|
|
1689
|
+
"filename": "datajunction-semantic-model.md",
|
|
1690
|
+
"description": "DJ semantic modeling: query-to-nodes decomposition, ratio decomposition, naming, ownership",
|
|
1691
|
+
"keywords": [
|
|
1692
|
+
"semantic modeling",
|
|
1693
|
+
"decompose query",
|
|
1694
|
+
"ratio metric",
|
|
1695
|
+
"derived metric",
|
|
1696
|
+
"base metric",
|
|
1697
|
+
"metric naming",
|
|
1698
|
+
"namespace organization",
|
|
1699
|
+
"node ownership",
|
|
1700
|
+
],
|
|
1701
|
+
},
|
|
1702
|
+
{
|
|
1703
|
+
"name": "datajunction-repo",
|
|
1704
|
+
"filename": "datajunction-repo.md",
|
|
1705
|
+
"description": "Authoring DJ nodes via YAML in a git-backed repository",
|
|
1706
|
+
"keywords": [
|
|
1707
|
+
"YAML nodes",
|
|
1708
|
+
"repo-backed namespace",
|
|
1709
|
+
"feature branch",
|
|
1710
|
+
"git workflow",
|
|
1711
|
+
"cube YAML",
|
|
1712
|
+
"metric YAML",
|
|
1713
|
+
"temporal partition",
|
|
1714
|
+
],
|
|
1715
|
+
},
|
|
1716
|
+
{
|
|
1717
|
+
"name": "datajunction-api",
|
|
1718
|
+
"filename": "datajunction-api.md",
|
|
1719
|
+
"description": "Direct REST API authoring of DJ nodes for exploration / prototyping",
|
|
1720
|
+
"keywords": [
|
|
1721
|
+
"DJ API",
|
|
1722
|
+
"REST API",
|
|
1723
|
+
"curl",
|
|
1724
|
+
"POST nodes",
|
|
1725
|
+
"API authoring",
|
|
1726
|
+
"prototyping",
|
|
1727
|
+
],
|
|
1728
|
+
},
|
|
1729
|
+
]
|
|
1730
|
+
|
|
1731
|
+
missing: list[str] = []
|
|
1732
|
+
for skill in bundled_skills:
|
|
1733
|
+
dir_name = skill["name"]
|
|
1734
|
+
skill_dir = output_dir / dir_name
|
|
1735
|
+
skill_file = skill_dir / "SKILL.md"
|
|
1736
|
+
|
|
1737
|
+
console.print(f"Installing [cyan]{dir_name}[/cyan]...")
|
|
1738
|
+
|
|
1739
|
+
try:
|
|
1740
|
+
skill_file_path = files("datajunction").joinpath(
|
|
1741
|
+
f"skills/{skill['filename']}",
|
|
1742
|
+
)
|
|
1743
|
+
bundled_skill = skill_file_path.read_text(encoding="utf-8")
|
|
1744
|
+
except FileNotFoundError: # pragma: no cover
|
|
1745
|
+
missing.append(skill["filename"])
|
|
1746
|
+
continue
|
|
1643
1747
|
|
|
1644
|
-
# Create directory
|
|
1645
1748
|
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
1646
1749
|
|
|
1647
|
-
# Write SKILL.md
|
|
1648
1750
|
with open(skill_file, "w") as f:
|
|
1649
1751
|
f.write(bundled_skill)
|
|
1650
1752
|
|
|
1651
|
-
# Write metadata
|
|
1652
1753
|
metadata_file = skill_dir / "metadata.json"
|
|
1653
1754
|
with open(metadata_file, "w") as f:
|
|
1654
1755
|
metadata = {
|
|
1655
|
-
"name": "
|
|
1756
|
+
"name": skill["name"],
|
|
1656
1757
|
"version": dj_version,
|
|
1657
|
-
"description": "
|
|
1658
|
-
"keywords": [
|
|
1659
|
-
"DataJunction",
|
|
1660
|
-
"DJ",
|
|
1661
|
-
"semantic layer",
|
|
1662
|
-
"dimension link",
|
|
1663
|
-
"metric",
|
|
1664
|
-
"SQL generation",
|
|
1665
|
-
"YAML nodes",
|
|
1666
|
-
"git workflow",
|
|
1667
|
-
"repo-backed namespace",
|
|
1668
|
-
],
|
|
1758
|
+
"description": skill["description"],
|
|
1759
|
+
"keywords": skill["keywords"],
|
|
1669
1760
|
"metadata": {
|
|
1670
1761
|
"source": "bundled",
|
|
1671
1762
|
"dj_version": dj_version,
|
|
@@ -1681,13 +1772,14 @@ class DJCLI:
|
|
|
1681
1772
|
f" [dim]└─ metadata.json (v{dj_version})[/dim]\n",
|
|
1682
1773
|
)
|
|
1683
1774
|
|
|
1775
|
+
if missing: # pragma: no cover
|
|
1684
1776
|
console.print(
|
|
1685
|
-
f"[
|
|
1777
|
+
f"[red]✗ Bundled skills not found: {', '.join(missing)}. "
|
|
1778
|
+
f"Please ensure datajunction is properly installed.[/red]",
|
|
1686
1779
|
)
|
|
1687
|
-
|
|
1688
|
-
except FileNotFoundError: # pragma: no cover
|
|
1780
|
+
else:
|
|
1689
1781
|
console.print(
|
|
1690
|
-
"[
|
|
1782
|
+
f"[bold green]✓ Skills installed to {output_dir}[/bold green]\n",
|
|
1691
1783
|
)
|
|
1692
1784
|
|
|
1693
1785
|
# Install subagent if requested
|
|
@@ -1704,9 +1796,13 @@ name: dj
|
|
|
1704
1796
|
description: >
|
|
1705
1797
|
DataJunction semantic layer expert. Use proactively for any DataJunction
|
|
1706
1798
|
or DJ work — querying metrics, exploring nodes and dimensions, building
|
|
1707
|
-
SQL, understanding lineage, and semantic layer design.
|
|
1799
|
+
SQL, understanding lineage, authoring metrics, and semantic layer design.
|
|
1708
1800
|
skills:
|
|
1709
1801
|
- datajunction
|
|
1802
|
+
- datajunction-query
|
|
1803
|
+
- datajunction-semantic-model
|
|
1804
|
+
- datajunction-repo
|
|
1805
|
+
- datajunction-api
|
|
1710
1806
|
model: inherit
|
|
1711
1807
|
---
|
|
1712
1808
|
"""
|
|
@@ -304,6 +304,8 @@ class DeploymentService:
|
|
|
304
304
|
output: str | Path = ".github/CODEOWNERS",
|
|
305
305
|
github_api_url: str | None = None,
|
|
306
306
|
github_token_env: str = "GITHUB_TOKEN",
|
|
307
|
+
default_owner: str | None = None,
|
|
308
|
+
exclude_dirs: list[str] | None = None,
|
|
307
309
|
) -> int:
|
|
308
310
|
"""
|
|
309
311
|
Generate a CODEOWNERS file from the owners fields in DJ node YAML files.
|
|
@@ -317,9 +319,24 @@ class DeploymentService:
|
|
|
317
319
|
email addresses in owners fields are resolved to GitHub usernames via the
|
|
318
320
|
search API. Unresolvable emails are emitted as-is with a warning comment.
|
|
319
321
|
|
|
320
|
-
|
|
322
|
+
``default_owner``, when set, emits a leading ``* <default_owner>`` rule so
|
|
323
|
+
unmatched files (and any excluded directories) fall through to it. Because
|
|
324
|
+
CODEOWNERS is last-match-wins, the per-file rules below it still take
|
|
325
|
+
precedence for the files they name.
|
|
326
|
+
|
|
327
|
+
``exclude_dirs`` lists directories (relative to base_dir) whose nodes are
|
|
328
|
+
NOT given per-file owners — they fall through to ``default_owner`` instead.
|
|
329
|
+
This is for machine-generated trees (e.g. ``nodes/generated``) that have no
|
|
330
|
+
individual human owner and should be team-owned as a block.
|
|
331
|
+
|
|
332
|
+
Returns the number of per-file entries written (excludes the default rule).
|
|
321
333
|
"""
|
|
322
334
|
base = Path(base_dir).resolve()
|
|
335
|
+
excluded_dirs = [(base / d).resolve() for d in (exclude_dirs or [])]
|
|
336
|
+
|
|
337
|
+
def _is_excluded(path: Path) -> bool:
|
|
338
|
+
return any(d == path or d in path.parents for d in excluded_dirs)
|
|
339
|
+
|
|
323
340
|
_token = os.getenv(github_token_env)
|
|
324
341
|
# Only resolve if both API URL and token are available
|
|
325
342
|
lookup: tuple[str, str] | None = (
|
|
@@ -353,6 +370,8 @@ class DeploymentService:
|
|
|
353
370
|
for path in sorted(base.rglob("*.yaml")):
|
|
354
371
|
if path.name == "dj.yaml":
|
|
355
372
|
continue
|
|
373
|
+
if _is_excluded(path):
|
|
374
|
+
continue
|
|
356
375
|
try:
|
|
357
376
|
node = DeploymentService.read_yaml_file(path)
|
|
358
377
|
except Exception: # skip unreadable / non-dict files
|
|
@@ -376,7 +395,12 @@ class DeploymentService:
|
|
|
376
395
|
)
|
|
377
396
|
for w in warnings:
|
|
378
397
|
lines.append(f"# {w}")
|
|
379
|
-
lines
|
|
398
|
+
lines.append("")
|
|
399
|
+
# Default rule first so per-file rules below win (last-match-wins).
|
|
400
|
+
if default_owner:
|
|
401
|
+
lines.append(f"* {default_owner}")
|
|
402
|
+
lines.append("")
|
|
403
|
+
lines += [*entries, ""]
|
|
380
404
|
content = "\n".join(lines)
|
|
381
405
|
|
|
382
406
|
output_path = Path(output)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: datajunction-api
|
|
3
|
+
description: |
|
|
4
|
+
Activate this skill when authoring DataJunction (DJ) nodes via the REST
|
|
5
|
+
API directly (curl, HTTP clients) — typically for exploration, ad-hoc
|
|
6
|
+
prototyping, or namespaces that aren't repo-backed. For modeling
|
|
7
|
+
decisions and the decomposition workflow, invoke `datajunction-semantic-model`.
|
|
8
|
+
For repo-backed YAML authoring (the production path), invoke
|
|
9
|
+
`datajunction-repo`. For concepts, invoke `datajunction`.
|
|
10
|
+
Keywords:
|
|
11
|
+
- DJ API, REST API, curl
|
|
12
|
+
- POST nodes/metric, POST nodes/dimension
|
|
13
|
+
- create metric, create dimension, create cube
|
|
14
|
+
- API approach, direct API changes
|
|
15
|
+
- prototyping, exploration
|
|
16
|
+
user-invocable: false
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# DataJunction Direct API Authoring
|
|
20
|
+
|
|
21
|
+
Direct REST API authoring for DJ nodes. Use this skill for quick exploration, prototyping, or working in namespaces that don't use the repo-backed workflow.
|
|
22
|
+
|
|
23
|
+
For the modeling work upstream of any authoring (decomposition, naming, ratio decomposition, etc.), see `datajunction-semantic-model`. For the production-path equivalent of these patterns in YAML, see `datajunction-repo`.
|
|
24
|
+
|
|
25
|
+
## When to Use the API Approach
|
|
26
|
+
|
|
27
|
+
**✅ MUST use repo workflow instead** (`datajunction-repo`) for:
|
|
28
|
+
- Namespaces configured as repo-backed and read-only (`git_only: true`) — direct API changes are rejected
|
|
29
|
+
|
|
30
|
+
**✅ Should use repo workflow** for:
|
|
31
|
+
- Production changes (review required)
|
|
32
|
+
- Multi-node changes (related metrics/dimensions)
|
|
33
|
+
- Team environments (multiple contributors)
|
|
34
|
+
- Audit-trail requirements
|
|
35
|
+
- Complex refactoring
|
|
36
|
+
|
|
37
|
+
**✅ API workflow is appropriate** for:
|
|
38
|
+
- Quick exploration and prototyping
|
|
39
|
+
- Ad-hoc analysis
|
|
40
|
+
- Single-user, non-production namespaces
|
|
41
|
+
- Temporary metrics
|
|
42
|
+
- Non-production experiments
|
|
43
|
+
- Only when the target namespace is NOT read-only repo-backed
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Checking if a Namespace Is Repo-Backed
|
|
48
|
+
|
|
49
|
+
Before authoring via API, verify the target namespace allows it.
|
|
50
|
+
|
|
51
|
+
**Best approach — `get_node_details` MCP tool** (`datajunction-query` skill):
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
get_node_details(name="finance.total_revenue")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The response will include git repository information:
|
|
58
|
+
```
|
|
59
|
+
Git Repository:
|
|
60
|
+
Repo: owner/dj-finance
|
|
61
|
+
Branch: main
|
|
62
|
+
Default Branch: main
|
|
63
|
+
→ This namespace is repo-backed (use git workflow for changes)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Alternative — REST API** (shows read-only status):
|
|
67
|
+
```bash
|
|
68
|
+
curl -b ~/.dj/cookies.txt -X GET $DJ_URL/namespaces/finance/git
|
|
69
|
+
|
|
70
|
+
# Response:
|
|
71
|
+
{
|
|
72
|
+
"github_repo_path": "owner/dj-finance",
|
|
73
|
+
"git_branch": "main",
|
|
74
|
+
"default_branch": "main",
|
|
75
|
+
"git_path": "nodes/",
|
|
76
|
+
"git_only": true ← If true, namespace is read-only (API changes blocked)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Decision tree:**
|
|
81
|
+
- **If git info is present AND `git_only: true`**: MUST use repo workflow (API changes will fail)
|
|
82
|
+
- **If git info is present AND `git_only: false`**: Can use either workflow
|
|
83
|
+
- **If git info is null**: Use API workflow (direct POST/PATCH)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Creating a Metric
|
|
88
|
+
|
|
89
|
+
### Metric Structure
|
|
90
|
+
|
|
91
|
+
```sql
|
|
92
|
+
SELECT <aggregation_expression> AS <metric_name>
|
|
93
|
+
FROM <single_node>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Metrics select a **single expression** from a **single source, transform, or dimension node**. They cannot contain WHERE clauses — use CASE WHEN instead. See `datajunction-semantic-model` for the modeling rationale.
|
|
97
|
+
|
|
98
|
+
### Metric Metadata Fields
|
|
99
|
+
|
|
100
|
+
**Required:**
|
|
101
|
+
- `name` — Fully qualified metric name (e.g., `finance.total_revenue`)
|
|
102
|
+
- `query` — SQL aggregation expression
|
|
103
|
+
|
|
104
|
+
**Recommended:**
|
|
105
|
+
- `description` — Human-readable description
|
|
106
|
+
- `metric_metadata.direction` — `higher_is_better` / `lower_is_better` / `neutral`
|
|
107
|
+
- `metric_metadata.unit` — `dollar` / `unitless` (**⚠️ NOT `count`** — server rejects)
|
|
108
|
+
- `mode` — `draft` / `published`
|
|
109
|
+
- `required_dimensions` — Dimensions required for this metric to make sense
|
|
110
|
+
- `owners` — List of email addresses (prefer team emails)
|
|
111
|
+
|
|
112
|
+
### Examples
|
|
113
|
+
|
|
114
|
+
**COUNT**:
|
|
115
|
+
```bash
|
|
116
|
+
curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
|
|
117
|
+
-H 'Content-Type: application/json' \
|
|
118
|
+
-d '{
|
|
119
|
+
"name": "finance.num_transactions",
|
|
120
|
+
"description": "Total number of transactions",
|
|
121
|
+
"query": "SELECT COUNT(transaction_id) AS num_transactions FROM finance.transactions",
|
|
122
|
+
"owners": ["data-platform-team@company.com"],
|
|
123
|
+
"mode": "published"
|
|
124
|
+
}'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**SUM**:
|
|
128
|
+
```bash
|
|
129
|
+
curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
|
|
130
|
+
-H 'Content-Type: application/json' \
|
|
131
|
+
-d '{
|
|
132
|
+
"name": "finance.total_revenue",
|
|
133
|
+
"description": "Total revenue from all transactions",
|
|
134
|
+
"query": "SELECT SUM(amount_usd) AS total_revenue FROM finance.transactions",
|
|
135
|
+
"metric_metadata": {
|
|
136
|
+
"direction": "higher_is_better",
|
|
137
|
+
"unit": "dollar"
|
|
138
|
+
},
|
|
139
|
+
"owners": ["finance-data-team@company.com"],
|
|
140
|
+
"mode": "published"
|
|
141
|
+
}'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Conditional aggregation** (CASE WHEN, not WHERE):
|
|
145
|
+
```bash
|
|
146
|
+
curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
|
|
147
|
+
-H 'Content-Type: application/json' \
|
|
148
|
+
-d '{
|
|
149
|
+
"name": "finance.completed_revenue",
|
|
150
|
+
"description": "Revenue from completed non-refund transactions",
|
|
151
|
+
"query": "
|
|
152
|
+
SELECT SUM(
|
|
153
|
+
CASE
|
|
154
|
+
WHEN status = '\''completed'\'' AND refund_flag = false
|
|
155
|
+
THEN amount_usd
|
|
156
|
+
ELSE 0
|
|
157
|
+
END
|
|
158
|
+
) AS completed_revenue
|
|
159
|
+
FROM finance.transactions
|
|
160
|
+
",
|
|
161
|
+
"metric_metadata": {
|
|
162
|
+
"direction": "higher_is_better",
|
|
163
|
+
"unit": "dollar"
|
|
164
|
+
},
|
|
165
|
+
"owners": ["finance-data-team@company.com"],
|
|
166
|
+
"mode": "published"
|
|
167
|
+
}'
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Ratio over base metrics** (decompose first, then derive — see `datajunction-semantic-model`):
|
|
171
|
+
```bash
|
|
172
|
+
# Step 1: create the base metrics (one curl each)
|
|
173
|
+
curl -X POST $DJ_URL/nodes/metric/ -d '{
|
|
174
|
+
"name": "finance.clicks",
|
|
175
|
+
"query": "SELECT COUNT_IF(event = '\''click'\'') FROM finance.events",
|
|
176
|
+
"owners": ["marketing@company.com"],
|
|
177
|
+
"mode": "published"
|
|
178
|
+
}'
|
|
179
|
+
|
|
180
|
+
curl -X POST $DJ_URL/nodes/metric/ -d '{
|
|
181
|
+
"name": "finance.impressions",
|
|
182
|
+
"query": "SELECT COUNT_IF(event = '\''impression'\'') FROM finance.events",
|
|
183
|
+
"owners": ["marketing@company.com"],
|
|
184
|
+
"mode": "published"
|
|
185
|
+
}'
|
|
186
|
+
|
|
187
|
+
# Step 2: derived ratio metric referencing the base metrics
|
|
188
|
+
curl -X POST $DJ_URL/nodes/metric/ -d '{
|
|
189
|
+
"name": "finance.conversion_rate",
|
|
190
|
+
"description": "Click-through rate as percentage",
|
|
191
|
+
"query": "SELECT finance.clicks * 100.0 / NULLIF(finance.impressions, 0)",
|
|
192
|
+
"metric_metadata": {
|
|
193
|
+
"direction": "higher_is_better",
|
|
194
|
+
"unit": "unitless"
|
|
195
|
+
},
|
|
196
|
+
"owners": ["marketing@company.com"],
|
|
197
|
+
"mode": "published"
|
|
198
|
+
}'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
DJ automatically handles divide-by-zero, but `NULLIF()` is extra safety.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Creating Other Node Types via API
|
|
206
|
+
|
|
207
|
+
Same pattern as metrics — POST JSON to the appropriate endpoint:
|
|
208
|
+
|
|
209
|
+
- `POST /nodes/source/` — source nodes (catalog/schema/table refs)
|
|
210
|
+
- `POST /nodes/dimension/` — dimension nodes
|
|
211
|
+
- `POST /nodes/transform/` — transform nodes
|
|
212
|
+
- `POST /nodes/cube/` — cubes (metric + dimension combinations)
|
|
213
|
+
|
|
214
|
+
For the YAML-equivalent shapes of each, see `datajunction-repo` — the field set is the same, just expressed in JSON instead of YAML.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Updating and Deleting Nodes
|
|
219
|
+
|
|
220
|
+
**Update** (PATCH):
|
|
221
|
+
```bash
|
|
222
|
+
curl -b ~/.dj/cookies.txt -X PATCH $DJ_URL/nodes/finance.total_revenue/ \
|
|
223
|
+
-H 'Content-Type: application/json' \
|
|
224
|
+
-d '{"description": "Updated description"}'
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Deactivate** (soft delete):
|
|
228
|
+
```bash
|
|
229
|
+
curl -b ~/.dj/cookies.txt -X DELETE $DJ_URL/nodes/finance.total_revenue/
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Deactivated nodes can be revived. For hard-delete (irreversible), use the `dj` CLI: `dj delete-node finance.total_revenue --hard`.
|