uptimely-core 0.2.0.dev0__tar.gz → 0.2.0.dev1__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 (129) hide show
  1. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/PKG-INFO +7 -4
  2. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/pyproject.toml +1 -1
  3. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/readme.md +6 -3
  4. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/__init__.py +13 -13
  5. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/cli/cli.py +9 -10
  6. uptimely_core-0.2.0.dev1/src/uptimely/cli/common.py +38 -0
  7. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/cli/engine.py +24 -40
  8. uptimely_core-0.2.0.dev1/src/uptimely/cli/explorer.py +92 -0
  9. uptimely_core-0.2.0.dev1/src/uptimely/cli/plan.py +18 -0
  10. uptimely_core-0.2.0.dev1/src/uptimely/cli/project.py +62 -0
  11. uptimely_core-0.2.0.dev1/src/uptimely/cli/spec.py +39 -0
  12. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/__init__.py +1 -1
  13. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/callable_node.py +2 -2
  14. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/compiler.py +31 -31
  15. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/execution_graph.py +21 -21
  16. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/plan/models.py +1 -1
  17. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/contracts.py +1 -1
  18. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/config.py +1 -1
  19. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/engine.py +8 -39
  20. uptimely_core-0.2.0.dev1/src/uptimely/explorer/explorer.py +120 -0
  21. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/explorer/graph_render.py +21 -5
  22. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/explorer/lineage.py +21 -15
  23. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/explorer/plan_render.py +8 -1
  24. uptimely_core-0.2.0.dev0/src/uptimely/explorer/analytics_render.py → uptimely_core-0.2.0.dev1/src/uptimely/explorer/spec_render.py +32 -58
  25. uptimely_core-0.2.0.dev1/src/uptimely/mcp/__init__.py +5 -0
  26. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/mcp/server.py +130 -104
  27. uptimely_core-0.2.0.dev1/src/uptimely/project/__init__.py +67 -0
  28. uptimely_core-0.2.0.dev1/src/uptimely/project/build.py +623 -0
  29. uptimely_core-0.2.0.dev1/src/uptimely/project/paths.py +27 -0
  30. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/render.py +3 -3
  31. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/row_context.py +1 -1
  32. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/__init__.py +10 -10
  33. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/api.py +17 -17
  34. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/catalog.py +8 -8
  35. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/feature_graph/graph.py +14 -16
  36. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/feature_graph/models.py +1 -1
  37. uptimely_core-0.2.0.dev1/src/uptimely/spec/json/__init__.py +1 -0
  38. uptimely_core-0.2.0.dev1/src/uptimely/spec/json/codec.py +41 -0
  39. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/model/__init__.py +6 -6
  40. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/model/common.py +4 -4
  41. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/model/dataset.py +2 -2
  42. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/model/functions.py +2 -2
  43. uptimely_core-0.2.0.dev0/src/uptimely/analytics/model/analytics.py → uptimely_core-0.2.0.dev1/src/uptimely/spec/model/spec.py +7 -7
  44. uptimely_core-0.2.0.dev1/src/uptimely/spec/python/__init__.py +7 -0
  45. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/conversion.py +27 -28
  46. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/dataset.py +16 -18
  47. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/dimension.py +3 -3
  48. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/entity.py +3 -3
  49. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/feature.py +3 -3
  50. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/mappings.py +4 -4
  51. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/ref.py +1 -1
  52. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/row_context.py +1 -1
  53. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/analytics.py → uptimely_core-0.2.0.dev1/src/uptimely/spec/python/spec.py +12 -12
  54. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/references.py +9 -11
  55. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/resolution.py +5 -5
  56. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/resolved.py +4 -4
  57. uptimely_core-0.2.0.dev1/src/uptimely/spec/validation/__init__.py +20 -0
  58. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/context.py +12 -12
  59. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/dependencies.py +1 -1
  60. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/dimensions.py +10 -10
  61. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/errors.py +2 -2
  62. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/functions.py +6 -6
  63. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/mappings.py +16 -16
  64. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/validation/prepared.py +11 -11
  65. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/analytics.py → uptimely_core-0.2.0.dev1/src/uptimely/spec/validation/spec.py +4 -6
  66. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/storage.py +1 -1
  67. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/base.html.j2 +14 -5
  68. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/execution/data_lineage.html.j2 +2 -2
  69. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/icons.html.j2 +1 -1
  70. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/index.html.j2 +10 -10
  71. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/plan/index.html.j2 +1 -1
  72. uptimely_core-0.2.0.dev1/src/uptimely/templates/spec/datasets.html.j2 +5 -0
  73. uptimely_core-0.2.0.dev1/src/uptimely/templates/spec/dimensions.html.j2 +5 -0
  74. uptimely_core-0.2.0.dev1/src/uptimely/templates/spec/entities.html.j2 +5 -0
  75. uptimely_core-0.2.0.dev1/src/uptimely/templates/spec/features.html.j2 +5 -0
  76. uptimely_core-0.2.0.dev1/src/uptimely/templates/spec/functions.html.j2 +5 -0
  77. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/index.html.j2 +7 -7
  78. uptimely_core-0.2.0.dev1/src/uptimely/templates/spec/overview.html.j2 +5 -0
  79. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/section_header.html.j2 +1 -1
  80. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/sections/dataset_list.html.j2 +1 -1
  81. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/sections/dimensions.html.j2 +1 -1
  82. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/sections/entities.html.j2 +1 -1
  83. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/sections/entity_list.html.j2 +1 -1
  84. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/sections/functions.html.j2 +1 -1
  85. {uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics → uptimely_core-0.2.0.dev1/src/uptimely/templates/spec}/sections/overview.html.j2 +4 -4
  86. uptimely_core-0.2.0.dev0/src/uptimely/analytics/json/__init__.py +0 -1
  87. uptimely_core-0.2.0.dev0/src/uptimely/analytics/json/codec.py +0 -42
  88. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/__init__.py +0 -7
  89. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/__init__.py +0 -20
  90. uptimely_core-0.2.0.dev0/src/uptimely/cli/analytics.py +0 -97
  91. uptimely_core-0.2.0.dev0/src/uptimely/cli/common.py +0 -49
  92. uptimely_core-0.2.0.dev0/src/uptimely/cli/explorer.py +0 -192
  93. uptimely_core-0.2.0.dev0/src/uptimely/cli/plan.py +0 -64
  94. uptimely_core-0.2.0.dev0/src/uptimely/explorer/explorer.py +0 -127
  95. uptimely_core-0.2.0.dev0/src/uptimely/mcp/__init__.py +0 -5
  96. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/datasets.html.j2 +0 -5
  97. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/dimensions.html.j2 +0 -5
  98. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/entities.html.j2 +0 -5
  99. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/features.html.j2 +0 -5
  100. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/functions.html.j2 +0 -5
  101. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/overview.html.j2 +0 -5
  102. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/LICENSE +0 -0
  103. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/cli/__init__.py +0 -0
  104. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/cli/examples.py +0 -0
  105. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/plan/__init__.py +0 -0
  106. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/compiler/plan/serialization.py +0 -0
  107. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/__init__.py +0 -0
  108. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/backend.py +0 -0
  109. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/bindings.py +0 -0
  110. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/dataframe.py +0 -0
  111. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/result.py +0 -0
  112. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/engine/state.py +0 -0
  113. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/entrypoint.py +0 -0
  114. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/explorer/__init__.py +0 -0
  115. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/py.typed +0 -0
  116. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/feature_graph/__init__.py +0 -0
  117. {uptimely_core-0.2.0.dev0/src/uptimely/analytics → uptimely_core-0.2.0.dev1/src/uptimely/spec}/python/bind.py +0 -0
  118. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/base.html.j2 +0 -0
  119. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/feature_graph.html.j2 +0 -0
  120. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/feature_graph_shared.html.j2 +0 -0
  121. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/feature_renderer_2d.js.j2 +0 -0
  122. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/feature_renderer_3d.js.j2 +0 -0
  123. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/view_controller.js.j2 +0 -0
  124. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/graph/view_controls.html.j2 +0 -0
  125. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/plan/callable_graph.html.j2 +0 -0
  126. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/plan/callable_renderer_2d.js.j2 +0 -0
  127. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/plan/graph.html.j2 +0 -0
  128. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/plan/overview.html.j2 +0 -0
  129. {uptimely_core-0.2.0.dev0 → uptimely_core-0.2.0.dev1}/src/uptimely/templates/plan/stages.html.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uptimely-core
3
- Version: 0.2.0.dev0
3
+ Version: 0.2.0.dev1
4
4
  Summary: Manage data-driven numerical analytics with complex dependencies at scale
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -63,7 +63,9 @@ With pip:
63
63
 
64
64
  ```sh
65
65
  pip install uptimely-core
66
- uptimely examples download
66
+ uptimely examples download --output examples
67
+ uptimely project build --project-root examples/pump_analytics
68
+ uptimely engine run --project-root examples/pump_analytics
67
69
  uptimely explorer serve --project-root examples/pump_analytics
68
70
  ```
69
71
 
@@ -71,11 +73,12 @@ With Poetry, from your Poetry project:
71
73
 
72
74
  ```sh
73
75
  poetry add uptimely-core
74
- poetry run uptimely examples download
76
+ poetry run uptimely examples download --output examples
77
+ poetry run uptimely project build --project-root examples/pump_analytics
78
+ poetry run uptimely engine run --project-root examples/pump_analytics
75
79
  poetry run uptimely explorer serve --project-root examples/pump_analytics
76
80
  ```
77
81
 
78
- More use cases in [examples](examples/README.md). Explore the examples files, generated resources and modify for the needs of your own project.
79
82
 
80
83
  ## Documentation
81
84
 
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "uptimely-core"
7
- version = "0.2.0.dev0"
7
+ version = "0.2.0.dev1"
8
8
  description = "Manage data-driven numerical analytics with complex dependencies at scale"
9
9
  authors = [{ name = "Mikael Ahonen", email = "mikael.ahonen@uptimely.ai" }]
10
10
  license = "MIT"
@@ -33,7 +33,9 @@ With pip:
33
33
 
34
34
  ```sh
35
35
  pip install uptimely-core
36
- uptimely examples download
36
+ uptimely examples download --output examples
37
+ uptimely project build --project-root examples/pump_analytics
38
+ uptimely engine run --project-root examples/pump_analytics
37
39
  uptimely explorer serve --project-root examples/pump_analytics
38
40
  ```
39
41
 
@@ -41,11 +43,12 @@ With Poetry, from your Poetry project:
41
43
 
42
44
  ```sh
43
45
  poetry add uptimely-core
44
- poetry run uptimely examples download
46
+ poetry run uptimely examples download --output examples
47
+ poetry run uptimely project build --project-root examples/pump_analytics
48
+ poetry run uptimely engine run --project-root examples/pump_analytics
45
49
  poetry run uptimely explorer serve --project-root examples/pump_analytics
46
50
  ```
47
51
 
48
- More use cases in [examples](examples/README.md). Explore the examples files, generated resources and modify for the needs of your own project.
49
52
 
50
53
  ## Documentation
51
54
 
@@ -1,25 +1,25 @@
1
1
  """Public package namespace for uptimely."""
2
2
 
3
- from .analytics import (
4
- Analytics,
3
+ from .compiler import CallableGraph, CallableNode, Compiler, ExecutionPlan
4
+ from .engine import Engine
5
+ from .spec import (
5
6
  Call,
6
7
  Dimension,
7
8
  Entity,
8
9
  FeatureGraph,
9
10
  Function,
10
- PreparedAnalytics,
11
- PythonAnalytics,
11
+ PreparedSpec,
12
+ PythonSpec,
13
+ Spec,
12
14
  SubGraph,
13
15
  from_python,
14
- load_analytics,
15
- prepare_analytics,
16
+ load_spec,
17
+ prepare_spec,
16
18
  write_document,
17
19
  )
18
- from .compiler import CallableGraph, CallableNode, Compiler, ExecutionPlan
19
- from .engine import Engine
20
20
 
21
21
  __all__ = [
22
- "Analytics",
22
+ "Spec",
23
23
  "Call",
24
24
  "CallableGraph",
25
25
  "CallableNode",
@@ -28,11 +28,11 @@ __all__ = [
28
28
  "Entity",
29
29
  "ExecutionPlan",
30
30
  "Function",
31
- "PythonAnalytics",
32
- "PreparedAnalytics",
31
+ "PythonSpec",
32
+ "PreparedSpec",
33
33
  "from_python",
34
- "load_analytics",
35
- "prepare_analytics",
34
+ "load_spec",
35
+ "prepare_spec",
36
36
  "write_document",
37
37
  "SubGraph",
38
38
  "FeatureGraph",
@@ -2,10 +2,9 @@
2
2
 
3
3
  import argparse
4
4
 
5
- from uptimely.cli import analytics, engine, examples, explorer, plan
5
+ from uptimely.cli import engine, examples, explorer, project, spec
6
6
 
7
7
  download_examples = examples.download_examples
8
- build_explorer = explorer.build_explorer
9
8
  serve_explorer = explorer.serve_explorer
10
9
 
11
10
 
@@ -15,22 +14,22 @@ def main() -> None:
15
14
  commands = parser.add_subparsers(dest="command", required=True)
16
15
  examples.configure_parser(commands.add_parser("examples", help="Manage example projects"))
17
16
  explorer.configure_parser(
18
- commands.add_parser("explorer", help="Explore analytics, lineage, and execution plans")
17
+ commands.add_parser("explorer", help="Explore spec, lineage, and execution plans")
19
18
  )
20
- analytics.configure_parser(
21
- commands.add_parser("analytics", help="Export and inspect analytics")
19
+ spec.configure_parser(
20
+ commands.add_parser("spec", help="Inspect specs and feature dependencies")
22
21
  )
23
- plan.configure_parser(commands.add_parser("plan", help="Build execution plans"))
22
+ project.configure_parser(commands.add_parser("project", help="Build project resources"))
24
23
  engine.configure_parser(commands.add_parser("engine", help="Run analytics workflows"))
25
24
  args = parser.parse_args()
26
25
  if args.command == "examples":
27
26
  examples.run(args, parser)
28
27
  elif args.command == "explorer":
29
28
  explorer.run(args, parser)
30
- elif args.command == "analytics":
31
- analytics.run(args, parser)
32
- elif args.command == "plan":
33
- plan.run(args, parser)
29
+ elif args.command == "spec":
30
+ spec.run(args, parser)
31
+ elif args.command == "project":
32
+ project.run(args, parser)
34
33
  else:
35
34
  engine.run(args, parser)
36
35
 
@@ -0,0 +1,38 @@
1
+ """Shared project paths and spec loading for CLI commands."""
2
+
3
+ import argparse
4
+ import pathlib
5
+
6
+ from uptimely import spec
7
+ from uptimely.project.build import prepare_project_root
8
+
9
+
10
+ def configure_project_arguments(parser: argparse.ArgumentParser) -> None:
11
+ """Add spec, import-root, feature-selection, and diagnostic options."""
12
+ parser.add_argument(
13
+ "--spec",
14
+ type=pathlib.Path,
15
+ help="Spec JSON to inspect (default: spec from the active project build)",
16
+ )
17
+ parser.add_argument(
18
+ "--project-root",
19
+ type=pathlib.Path,
20
+ help="Root for importing function entrypoints and resolving default paths",
21
+ )
22
+ parser.add_argument(
23
+ "--features", nargs="+", help="Select output features and their dependencies"
24
+ )
25
+ parser.add_argument("--verbose", action="store_true", help="Print diagnostic details")
26
+
27
+
28
+ def project_path(
29
+ path: pathlib.Path | None, project_root: pathlib.Path | None, default: pathlib.Path
30
+ ) -> pathlib.Path:
31
+ """Resolve a default beneath the project root, preserving explicit paths."""
32
+ return path if path is not None else (project_root or pathlib.Path()) / default
33
+
34
+
35
+ def load_spec(path: pathlib.Path, project_root: pathlib.Path) -> spec.Spec:
36
+ """Load a spec document with project functions on the import path."""
37
+ prepare_project_root(project_root)
38
+ return spec.load_spec(path)
@@ -7,8 +7,8 @@ import typing
7
7
  import pydantic
8
8
 
9
9
  from uptimely import engine as engine_module
10
+ from uptimely import project
10
11
  from uptimely.cli import common, plan
11
- from uptimely.compiler import plan as plan_module
12
12
 
13
13
 
14
14
  class EngineSettings(pydantic.BaseModel):
@@ -23,68 +23,52 @@ class EngineSettings(pydantic.BaseModel):
23
23
  def configure_parser(parser: argparse.ArgumentParser) -> None:
24
24
  """Configure the engine run action."""
25
25
  actions = parser.add_subparsers(dest="action", required=True)
26
- execute = actions.add_parser("run", help="Compile and execute an analytics workflow")
27
- common.configure_project_arguments(execute)
26
+ execute = actions.add_parser("run", help="Execute the active project's saved build")
28
27
  execute.add_argument(
29
- "--compile",
30
- choices=["true", "false"],
31
- default="true",
32
- help="Compile analytics first (default: true); false loads --plan",
28
+ "--project-root",
29
+ type=pathlib.Path,
30
+ help="Project root containing the active build and function entrypoints",
33
31
  )
34
32
  execute.add_argument(
35
- "--plan",
36
- type=pathlib.Path,
37
- help=(
38
- "Saved plan JSON (default: _generated/compile/execution_plan.json under --project-root)"
39
- ),
33
+ "--verbose", action="store_true", help="Print execution stages and dataset details"
40
34
  )
41
35
  execute.add_argument(
42
36
  "--config",
43
37
  type=pathlib.Path,
44
38
  help=(
45
39
  "Backend settings and runtime bindings "
46
- "(default: config/engine.json under --project-root)"
40
+ f"(default: {project.ENGINE_CONFIG_FILE} under --project-root)"
47
41
  ),
48
42
  )
49
43
 
50
44
 
51
45
  def run(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
52
- """Execute a compiled or saved plan and report failures explicitly."""
53
- if args.compile == "false" and (args.analytics is not None or args.features is not None):
54
- parser.error("--analytics and --features require --compile true")
55
- if args.compile == "true" and args.plan is not None:
56
- parser.error("--plan requires --compile false")
46
+ """Execute the active saved plan and report failures explicitly."""
57
47
  root = args.project_root or pathlib.Path.cwd()
58
- config = common.project_path(args.config, args.project_root, common.ENGINE_CONFIG_PATH)
59
- analytics_definition = common.project_path(
60
- args.analytics, args.project_root, common.GENERATED_ANALYTICS_PATH
61
- )
62
- plan_path = common.project_path(args.plan, args.project_root, common.GENERATED_PLAN_PATH)
48
+ config = common.project_path(args.config, args.project_root, project.ENGINE_CONFIG_FILE)
63
49
  try:
64
50
  common.prepare_project_root(root)
65
- settings = EngineSettings.model_validate_json(config.read_text(encoding="utf-8"))
66
- execution_plan = (
67
- plan.compile_plan(analytics_definition, root, args.features)
68
- if args.compile == "true"
69
- else plan_module.ExecutionPlan.from_json(plan_path)
70
- )
71
- result = engine_module.Engine(
72
- execution_plan, config=settings.backend, bind=settings.bind
73
- ).execute()
74
- if result.failures:
75
- failures = "; ".join(result.failures.values())
76
- raise RuntimeError(f"Engine flow failed: {failures}")
51
+ with project.read_build_bundle(root) as bundle:
52
+ settings = EngineSettings.model_validate_json(config.read_text(encoding="utf-8"))
53
+ execution_plan = bundle.execution_plan
54
+ result = engine_module.Engine(
55
+ execution_plan, config=settings.backend, bind=settings.bind
56
+ ).execute()
57
+ if result.failures:
58
+ failures = "; ".join(result.failures.values())
59
+ raise RuntimeError(f"Engine flow failed: {failures}")
77
60
  except (OSError, ValueError, ImportError, AttributeError, TypeError, RuntimeError) as error:
78
61
  parser.exit(1, f"uptimely: error: {error}\n")
79
62
  calculated_features = sorted({feature_id for _, _, feature_id in result.calculated_outputs})
63
+ scope = (
64
+ "all features"
65
+ if bundle.manifest.features is None
66
+ else f"selected features: {', '.join(bundle.manifest.features)}"
67
+ )
80
68
  print("Engine completed the flow")
69
+ print(f"Build scope: {scope}")
81
70
  print(f"{len(execution_plan.callables)} callables")
82
71
  print("Calculated features: " + ", ".join(calculated_features))
83
- print(
84
- f"Analytics: {analytics_definition}"
85
- if args.compile == "true"
86
- else f"Execution plan: {plan_path}"
87
- )
88
72
  if args.verbose:
89
73
  plan.print_stages(execution_plan)
90
74
  print("\nDataset registry")
@@ -0,0 +1,92 @@
1
+ """Command-line tools for building and serving the analytics explorer."""
2
+
3
+ import argparse
4
+ import functools
5
+ import pathlib
6
+ from http import server as http_server
7
+
8
+ from uptimely import project
9
+
10
+
11
+ def serve_explorer(
12
+ directory: pathlib.Path,
13
+ host: str = "127.0.0.1",
14
+ port: int = 8001,
15
+ scope: str = "all features",
16
+ ) -> None:
17
+ """Serve a saved analytics explorer website for local preview.
18
+
19
+ Args:
20
+ directory: Saved website directory containing an index.html file.
21
+ host: Address to bind; defaults to localhost.
22
+ port: TCP port; defaults to 8001. Zero selects an available port.
23
+ scope: Human-readable scope of the saved execution plan.
24
+
25
+ Raises:
26
+ NotADirectoryError: The website directory does not exist or is not a directory.
27
+ FileNotFoundError: The website index is missing.
28
+ ValueError: The port is outside the valid TCP range.
29
+ OSError: The server cannot bind to the requested address.
30
+ """
31
+ directory = directory.resolve()
32
+ if not directory.is_dir():
33
+ raise NotADirectoryError(f"Website directory does not exist: {directory}")
34
+ index = directory / project.EXPLORER_INDEX_FILE.name
35
+ if not index.is_file():
36
+ raise FileNotFoundError(
37
+ f"Website index not found: {index}. Generate it with 'uptimely project build'."
38
+ )
39
+ if not 0 <= port <= 65535:
40
+ raise ValueError("Port must be between 0 and 65535.")
41
+ handler = functools.partial(http_server.SimpleHTTPRequestHandler, directory=str(directory))
42
+ with http_server.ThreadingHTTPServer((host, port), handler) as server:
43
+ print(
44
+ f"Serving analytics explorer from {directory} at "
45
+ f"http://{host}:{server.server_port}/ (build scope: {scope}; Ctrl+C to stop)",
46
+ flush=True,
47
+ )
48
+ try:
49
+ server.serve_forever()
50
+ except KeyboardInterrupt:
51
+ print("\nSpec explorer server stopped.")
52
+
53
+
54
+ def _port(value: str) -> int:
55
+ """Parse a valid TCP port for the CLI."""
56
+ try:
57
+ port = int(value)
58
+ except ValueError as error:
59
+ raise argparse.ArgumentTypeError("Port must be an integer.") from error
60
+ if not 0 <= port <= 65535:
61
+ raise argparse.ArgumentTypeError("Port must be between 0 and 65535.")
62
+ return port
63
+
64
+
65
+ def configure_parser(parser: argparse.ArgumentParser) -> None:
66
+ """Configure the Explorer serve action."""
67
+ actions = parser.add_subparsers(dest="action", required=True)
68
+ serve = actions.add_parser("serve", help="Serve the active build's saved website")
69
+ serve.add_argument(
70
+ "--project-root",
71
+ type=pathlib.Path,
72
+ help="Project root containing the active build",
73
+ )
74
+ serve.add_argument("--host", default="127.0.0.1", help="Bind address (default: 127.0.0.1)")
75
+ serve.add_argument(
76
+ "--port", type=_port, default=8001, help="TCP port (default: 8001; 0 selects a free port)"
77
+ )
78
+
79
+
80
+ def run(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
81
+ """Serve the active build's validated, saved Explorer website."""
82
+ project_root = (args.project_root or pathlib.Path.cwd()).resolve()
83
+ try:
84
+ with project.read_build_bundle(project_root) as bundle:
85
+ scope = (
86
+ "all features"
87
+ if bundle.manifest.features is None
88
+ else f"selected features: {', '.join(bundle.manifest.features)}"
89
+ )
90
+ serve_explorer(bundle.explorer_path, args.host, args.port, scope)
91
+ except (OSError, ValueError, RuntimeError) as error:
92
+ parser.exit(1, f"uptimely: error: {error}\n")
@@ -0,0 +1,18 @@
1
+ """Display execution plan stages in the CLI."""
2
+
3
+ from uptimely.compiler import plan as plan_module
4
+
5
+
6
+ def print_stages(plan: plan_module.ExecutionPlan) -> None:
7
+ """Print execution stages and their function dependencies."""
8
+ callables_by_id = {callable.id: callable for callable in plan.callables}
9
+ print("\nExecution stages")
10
+ for stage_number, stage in enumerate(plan.execution_stages, 1):
11
+ print(f"Stage {stage_number}")
12
+ for callable in stage:
13
+ dependencies = [
14
+ callables_by_id[dependency_id].function_id
15
+ for dependency_id in callable.dependency_ids
16
+ ]
17
+ suffix = f" <- [{', '.join(dependencies)}]" if dependencies else ""
18
+ print(f" {callable.function_id}({callable.entity_id}) -> {callable.output}{suffix}")
@@ -0,0 +1,62 @@
1
+ """Command-line orchestration for building generated project resources."""
2
+
3
+ import argparse
4
+ import pathlib
5
+
6
+ from uptimely.project import build as project_build
7
+
8
+
9
+ def configure_parser(parser: argparse.ArgumentParser) -> None:
10
+ """Configure the project build action."""
11
+ actions = parser.add_subparsers(dest="action", required=True)
12
+ build = actions.add_parser(
13
+ "build",
14
+ help="Build and publish a complete project bundle without running workflows",
15
+ )
16
+ source = build.add_mutually_exclusive_group()
17
+ source.add_argument(
18
+ "--entrypoint",
19
+ metavar="MODULE:ATTRIBUTE",
20
+ help="Importable module and attribute containing a PythonSpec instance",
21
+ )
22
+ source.add_argument(
23
+ "--spec",
24
+ type=pathlib.Path,
25
+ help="Source spec JSON path (overrides standard source discovery)",
26
+ )
27
+ build.add_argument(
28
+ "--project-root",
29
+ type=pathlib.Path,
30
+ help="Root added to the import path and used to resolve the output bundle",
31
+ )
32
+ build.add_argument(
33
+ "--features",
34
+ nargs="+",
35
+ metavar="FEATURE",
36
+ help="Select output features and their dependencies",
37
+ )
38
+
39
+
40
+ def run(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
41
+ """Build and publish a complete project bundle."""
42
+ root = args.project_root or pathlib.Path.cwd()
43
+ try:
44
+ bundle = project_build.build_project(
45
+ root,
46
+ entrypoint=args.entrypoint,
47
+ source=args.spec,
48
+ features=args.features,
49
+ )
50
+ except (
51
+ project_build.BuildLockError,
52
+ project_build.ProjectBuildError,
53
+ OSError,
54
+ ValueError,
55
+ ) as error:
56
+ parser.exit(1, f"uptimely: error: {error}\n")
57
+ scope = (
58
+ "all features" if bundle.manifest.features is None else ", ".join(bundle.manifest.features)
59
+ )
60
+ print(f"Build scope: {scope}")
61
+ print(f"Build bundle: {bundle.root}")
62
+ print(f"Compiled {len(bundle.execution_plan.callables)} callables")
@@ -0,0 +1,39 @@
1
+ """Command-line tools for inspecting spec features."""
2
+
3
+ import argparse
4
+ import pathlib
5
+
6
+ from uptimely import project
7
+ from uptimely.cli import common
8
+ from uptimely.spec import feature_graph
9
+
10
+
11
+ def configure_parser(parser: argparse.ArgumentParser) -> None:
12
+ """Configure spec feature-graph inspection."""
13
+ actions = parser.add_subparsers(dest="action", required=True)
14
+ graph = actions.add_parser("feature-graph", help="Inspect feature dependencies")
15
+ common.configure_project_arguments(graph)
16
+
17
+
18
+ def run(args: argparse.Namespace, parser: argparse.ArgumentParser) -> None:
19
+ """Print feature graph summaries and trees."""
20
+ project_root = args.project_root or pathlib.Path.cwd()
21
+ try:
22
+ if args.spec is None:
23
+ with project.read_build_bundle(project_root) as bundle:
24
+ spec_definition = common.load_spec(bundle.spec_path, project_root)
25
+ else:
26
+ spec_definition = common.load_spec(args.spec, project_root)
27
+ graph = feature_graph.FeatureGraph(spec_definition)
28
+ subset = graph.subset(args.features) if args.features is not None else None
29
+ print(f"Feature graph: {len(graph.feature_names)} features")
30
+ if subset is not None:
31
+ print(f"Selected feature subset: {len(subset.feature_names)} features")
32
+ if args.verbose:
33
+ print("\nFeature graph")
34
+ graph.print_graph()
35
+ if subset is not None:
36
+ print("\nSelected feature subset")
37
+ subset.print_graph()
38
+ except (OSError, ValueError, ImportError, AttributeError, TypeError, RuntimeError) as error:
39
+ parser.exit(1, f"uptimely: error: {error}\n")
@@ -1,4 +1,4 @@
1
- """Compile analytics into portable execution plans."""
1
+ """Compile spec into portable execution plans."""
2
2
 
3
3
  from .callable_node import CallableNode
4
4
  from .compiler import Compiler, ExecutionPlan
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import dataclasses
4
4
 
5
- from uptimely import analytics, render, row_context
5
+ from uptimely import render, row_context, spec
6
6
 
7
7
 
8
8
  @dataclasses.dataclass(frozen=True)
@@ -17,7 +17,7 @@ class CallableNode:
17
17
  mapping_id: str | None = None
18
18
  output_features: tuple[str, ...] = ()
19
19
  args: dict[str, object] = dataclasses.field(default_factory=dict, compare=False, hash=False)
20
- callable: analytics.Call | None = dataclasses.field(default=None, compare=False, hash=False)
20
+ callable: spec.Call | None = dataclasses.field(default=None, compare=False, hash=False)
21
21
  row_context: row_context.RowContext | None = dataclasses.field(
22
22
  default=None, compare=False, hash=False
23
23
  )