uptimely-core 0.1.0__tar.gz → 0.2.0.dev0__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 (139) hide show
  1. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/PKG-INFO +20 -9
  2. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/pyproject.toml +3 -3
  3. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/readme.md +17 -6
  4. uptimely_core-0.2.0.dev0/src/uptimely/__init__.py +40 -0
  5. uptimely_core-0.2.0.dev0/src/uptimely/analytics/__init__.py +65 -0
  6. uptimely_core-0.2.0.dev0/src/uptimely/analytics/api.py +82 -0
  7. uptimely_core-0.2.0.dev0/src/uptimely/analytics/catalog.py +41 -0
  8. uptimely_core-0.2.0.dev0/src/uptimely/analytics/feature_graph/__init__.py +14 -0
  9. uptimely_core-0.1.0/src/uptimely/analytics/feature_graph.py → uptimely_core-0.2.0.dev0/src/uptimely/analytics/feature_graph/graph.py +22 -24
  10. {uptimely_core-0.1.0/src/uptimely/analytics → uptimely_core-0.2.0.dev0/src/uptimely/analytics/feature_graph}/models.py +7 -10
  11. uptimely_core-0.2.0.dev0/src/uptimely/analytics/json/__init__.py +1 -0
  12. uptimely_core-0.2.0.dev0/src/uptimely/analytics/json/codec.py +42 -0
  13. uptimely_core-0.2.0.dev0/src/uptimely/analytics/model/__init__.py +48 -0
  14. uptimely_core-0.2.0.dev0/src/uptimely/analytics/model/analytics.py +86 -0
  15. {uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_model → uptimely_core-0.2.0.dev0/src/uptimely/analytics/model}/common.py +4 -4
  16. {uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_model → uptimely_core-0.2.0.dev0/src/uptimely/analytics/model}/dataset.py +21 -9
  17. uptimely_core-0.2.0.dev0/src/uptimely/analytics/model/functions.py +57 -0
  18. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/__init__.py +7 -0
  19. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/analytics.py +60 -0
  20. {uptimely_core-0.1.0/src/uptimely/spec/python_spec → uptimely_core-0.2.0.dev0/src/uptimely/analytics/python}/bind.py +29 -21
  21. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/conversion.py +159 -0
  22. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/dataset.py +142 -0
  23. {uptimely_core-0.1.0/src/uptimely/spec/python_spec → uptimely_core-0.2.0.dev0/src/uptimely/analytics/python}/dimension.py +3 -3
  24. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/entity.py +35 -0
  25. {uptimely_core-0.1.0/src/uptimely/spec/python_spec → uptimely_core-0.2.0.dev0/src/uptimely/analytics/python}/feature.py +3 -3
  26. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/mappings.py +87 -0
  27. uptimely_core-0.2.0.dev0/src/uptimely/analytics/python/ref.py +139 -0
  28. {uptimely_core-0.1.0/src/uptimely/spec/python_spec → uptimely_core-0.2.0.dev0/src/uptimely/analytics/python}/row_context.py +1 -1
  29. uptimely_core-0.1.0/src/uptimely/spec/json_spec/bindings.py → uptimely_core-0.2.0.dev0/src/uptimely/analytics/references.py +40 -27
  30. uptimely_core-0.2.0.dev0/src/uptimely/analytics/resolution.py +78 -0
  31. {uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_model → uptimely_core-0.2.0.dev0/src/uptimely/analytics}/resolved.py +15 -2
  32. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/__init__.py +20 -0
  33. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/analytics.py +40 -0
  34. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/context.py +174 -0
  35. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/dependencies.py +23 -0
  36. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/dimensions.py +111 -0
  37. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/errors.py +13 -0
  38. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/functions.py +150 -0
  39. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/mappings.py +434 -0
  40. uptimely_core-0.2.0.dev0/src/uptimely/analytics/validation/prepared.py +155 -0
  41. uptimely_core-0.2.0.dev0/src/uptimely/cli/analytics.py +97 -0
  42. uptimely_core-0.2.0.dev0/src/uptimely/cli/cli.py +39 -0
  43. uptimely_core-0.2.0.dev0/src/uptimely/cli/common.py +49 -0
  44. uptimely_core-0.2.0.dev0/src/uptimely/cli/engine.py +99 -0
  45. uptimely_core-0.1.0/src/uptimely/cli/cli.py → uptimely_core-0.2.0.dev0/src/uptimely/cli/examples.py +8 -147
  46. uptimely_core-0.2.0.dev0/src/uptimely/cli/explorer.py +192 -0
  47. uptimely_core-0.2.0.dev0/src/uptimely/cli/plan.py +64 -0
  48. uptimely_core-0.2.0.dev0/src/uptimely/compiler/__init__.py +12 -0
  49. uptimely_core-0.1.0/src/uptimely/compile/node.py → uptimely_core-0.2.0.dev0/src/uptimely/compiler/callable_node.py +9 -30
  50. uptimely_core-0.2.0.dev0/src/uptimely/compiler/compiler.py +201 -0
  51. {uptimely_core-0.1.0/src/uptimely/compile → uptimely_core-0.2.0.dev0/src/uptimely/compiler}/execution_graph.py +67 -52
  52. uptimely_core-0.2.0.dev0/src/uptimely/compiler/plan/__init__.py +6 -0
  53. {uptimely_core-0.1.0/src/uptimely → uptimely_core-0.2.0.dev0/src/uptimely/compiler}/plan/models.py +65 -56
  54. uptimely_core-0.2.0.dev0/src/uptimely/contracts.py +56 -0
  55. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/engine/__init__.py +2 -1
  56. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/engine/backend.py +1 -1
  57. uptimely_core-0.2.0.dev0/src/uptimely/engine/bindings.py +80 -0
  58. uptimely_core-0.2.0.dev0/src/uptimely/engine/config.py +35 -0
  59. uptimely_core-0.2.0.dev0/src/uptimely/engine/dataframe.py +119 -0
  60. uptimely_core-0.2.0.dev0/src/uptimely/engine/engine.py +178 -0
  61. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/engine/result.py +4 -1
  62. uptimely_core-0.2.0.dev0/src/uptimely/engine/state.py +33 -0
  63. uptimely_core-0.2.0.dev0/src/uptimely/entrypoint.py +172 -0
  64. uptimely_core-0.1.0/src/uptimely/spec/render.py → uptimely_core-0.2.0.dev0/src/uptimely/explorer/analytics_render.py +89 -73
  65. uptimely_core-0.2.0.dev0/src/uptimely/explorer/explorer.py +127 -0
  66. uptimely_core-0.1.0/src/uptimely/analytics/render.py → uptimely_core-0.2.0.dev0/src/uptimely/explorer/graph_render.py +4 -4
  67. {uptimely_core-0.1.0/src/uptimely/docs → uptimely_core-0.2.0.dev0/src/uptimely/explorer}/lineage.py +37 -37
  68. uptimely_core-0.1.0/src/uptimely/compile/render.py → uptimely_core-0.2.0.dev0/src/uptimely/explorer/plan_render.py +68 -67
  69. uptimely_core-0.2.0.dev0/src/uptimely/mcp/__init__.py +5 -0
  70. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/mcp/server.py +90 -74
  71. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/render.py +9 -4
  72. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/row_context.py +6 -2
  73. {uptimely_core-0.1.0/src/uptimely/spec → uptimely_core-0.2.0.dev0/src/uptimely}/storage.py +6 -1
  74. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/datasets.html.j2 +5 -0
  75. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/dimensions.html.j2 +5 -0
  76. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/entities.html.j2 +5 -0
  77. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/features.html.j2 +5 -0
  78. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/functions.html.j2 +5 -0
  79. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/index.html.j2 +7 -7
  80. uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics/overview.html.j2 +5 -0
  81. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/section_header.html.j2 +1 -1
  82. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/sections/dataset_list.html.j2 +9 -11
  83. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/sections/dimensions.html.j2 +1 -1
  84. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/sections/entities.html.j2 +5 -7
  85. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/sections/entity_list.html.j2 +1 -1
  86. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/sections/functions.html.j2 +2 -2
  87. {uptimely_core-0.1.0/src/uptimely/templates/spec → uptimely_core-0.2.0.dev0/src/uptimely/templates/analytics}/sections/overview.html.j2 +4 -4
  88. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/base.html.j2 +8 -8
  89. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/execution/data_lineage.html.j2 +15 -15
  90. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/icons.html.j2 +1 -1
  91. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/index.html.j2 +11 -13
  92. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/plan/callable_graph.html.j2 +4 -5
  93. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/plan/callable_renderer_2d.js.j2 +1 -1
  94. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/plan/index.html.j2 +12 -12
  95. uptimely_core-0.1.0/src/uptimely/__init__.py +0 -27
  96. uptimely_core-0.1.0/src/uptimely/analytics/__init__.py +0 -18
  97. uptimely_core-0.1.0/src/uptimely/compile/__init__.py +0 -16
  98. uptimely_core-0.1.0/src/uptimely/compile/compiler.py +0 -196
  99. uptimely_core-0.1.0/src/uptimely/compile/entrypoint.py +0 -89
  100. uptimely_core-0.1.0/src/uptimely/docs/docs.py +0 -120
  101. uptimely_core-0.1.0/src/uptimely/engine/engine.py +0 -405
  102. uptimely_core-0.1.0/src/uptimely/mcp/__init__.py +0 -5
  103. uptimely_core-0.1.0/src/uptimely/plan/__init__.py +0 -6
  104. uptimely_core-0.1.0/src/uptimely/spec/__init__.py +0 -50
  105. uptimely_core-0.1.0/src/uptimely/spec/json_spec/__init__.py +0 -1
  106. uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_functions.py +0 -294
  107. uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_model/__init__.py +0 -63
  108. uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_model/functions.py +0 -65
  109. uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_model/specification.py +0 -116
  110. uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_parser.py +0 -341
  111. uptimely_core-0.1.0/src/uptimely/spec/json_spec/json_serializer.py +0 -32
  112. uptimely_core-0.1.0/src/uptimely/spec/python_spec/__init__.py +0 -7
  113. uptimely_core-0.1.0/src/uptimely/spec/python_spec/dataset.py +0 -195
  114. uptimely_core-0.1.0/src/uptimely/spec/python_spec/entity.py +0 -45
  115. uptimely_core-0.1.0/src/uptimely/spec/python_spec/mappings.py +0 -112
  116. uptimely_core-0.1.0/src/uptimely/spec/python_spec/ref.py +0 -69
  117. uptimely_core-0.1.0/src/uptimely/spec/python_spec/spec.py +0 -74
  118. uptimely_core-0.1.0/src/uptimely/spec/validation.py +0 -488
  119. uptimely_core-0.1.0/src/uptimely/templates/spec/datasets.html.j2 +0 -5
  120. uptimely_core-0.1.0/src/uptimely/templates/spec/dimensions.html.j2 +0 -5
  121. uptimely_core-0.1.0/src/uptimely/templates/spec/entities.html.j2 +0 -5
  122. uptimely_core-0.1.0/src/uptimely/templates/spec/features.html.j2 +0 -5
  123. uptimely_core-0.1.0/src/uptimely/templates/spec/functions.html.j2 +0 -5
  124. uptimely_core-0.1.0/src/uptimely/templates/spec/overview.html.j2 +0 -5
  125. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/LICENSE +0 -0
  126. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/cli/__init__.py +0 -0
  127. {uptimely_core-0.1.0/src/uptimely → uptimely_core-0.2.0.dev0/src/uptimely/compiler}/plan/serialization.py +0 -0
  128. {uptimely_core-0.1.0/src/uptimely/docs → uptimely_core-0.2.0.dev0/src/uptimely/explorer}/__init__.py +0 -0
  129. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/py.typed +0 -0
  130. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/base.html.j2 +0 -0
  131. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/feature_graph.html.j2 +0 -0
  132. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/feature_graph_shared.html.j2 +0 -0
  133. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/feature_renderer_2d.js.j2 +0 -0
  134. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/feature_renderer_3d.js.j2 +0 -0
  135. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/view_controller.js.j2 +0 -0
  136. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/graph/view_controls.html.j2 +0 -0
  137. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/plan/graph.html.j2 +0 -0
  138. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/plan/overview.html.j2 +0 -0
  139. {uptimely_core-0.1.0 → uptimely_core-0.2.0.dev0}/src/uptimely/templates/plan/stages.html.j2 +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uptimely-core
3
- Version: 0.1.0
4
- Summary: Declarative numerical analytics with complex dependencies at scale
3
+ Version: 0.2.0.dev0
4
+ Summary: Manage data-driven numerical analytics with complex dependencies at scale
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
7
7
  Keywords: analytics,declarative,industrial,feature-engineering,polars
@@ -24,15 +24,18 @@ Requires-Dist: numpy (>=1.26,<3.0)
24
24
  Requires-Dist: polars (>=1.0,<2.0)
25
25
  Requires-Dist: pydantic (>=2.10,<3.0)
26
26
  Project-URL: Documentation, https://uptimely-ai.github.io/uptimely-core
27
- Project-URL: Homepage, https://github.com/uptimely-ai/uptimely-core
27
+ Project-URL: Homepage, http://uptimely.ai/core/
28
28
  Project-URL: Repository, https://github.com/uptimely-ai/uptimely-core
29
29
  Description-Content-Type: text/markdown
30
30
 
31
31
  # Uptimely Core
32
32
 
33
- Uptimely Core is an open source Python package for declarative numerical analytics with complex dependencies at scale.
33
+ Uptimely Core is an open source Python package for declarative numerical
34
+ analytics with complex dependencies at scale.
34
35
 
35
- It describes analytics workflows in a specification similar to OpenAPI, uses a declarative model inspired by Kubernetes. The framework generates plans, graphs, and documentation from that specification.
36
+ Analytics definitions describe workflows in JSON, using a declarative model
37
+ inspired by OpenAPI and Kubernetes. The compiler generates plans from the
38
+ analytics; the package can also render dependency graphs and documentation.
36
39
 
37
40
  Ships with simple engine and plans to integrate to common execution backends.
38
41
 
@@ -56,15 +59,23 @@ Read more at [uptimely.ai website](https://uptimely.ai).
56
59
  Python 3.13 or newer is required. Install the package, download the examples
57
60
  and run.
58
61
 
62
+ With pip:
63
+
59
64
  ```sh
60
65
  pip install uptimely-core
61
66
  uptimely examples download
62
- python examples/run_all.py
67
+ uptimely explorer serve --project-root examples/pump_analytics
63
68
  ```
64
69
 
65
- Read more from [examples](examples/README.md).
70
+ With Poetry, from your Poetry project:
71
+
72
+ ```sh
73
+ poetry add uptimely-core
74
+ poetry run uptimely examples download
75
+ poetry run uptimely explorer serve --project-root examples/pump_analytics
76
+ ```
66
77
 
67
- Explore the examples files, generated resources and modify for the needs of your own project.
78
+ More use cases in [examples](examples/README.md). Explore the examples files, generated resources and modify for the needs of your own project.
68
79
 
69
80
  ## Documentation
70
81
 
@@ -77,7 +88,7 @@ Explore the [documentation site](https://uptimely-ai.github.io/uptimely-core/).
77
88
  | [`docs/`](docs/index.md) | Architecture, specification, and development documentation. |
78
89
  | [`examples/`](examples/README.md) | Runnable examples and generated artifacts. |
79
90
  | [`scripts/`](scripts) | Code snippets for repository management |
80
- | [`src/uptimely/`](src/uptimely/) | Package implementation for specifications, compilation, dependencies, documentation, and engine. |
91
+ | [`src/uptimely/`](src/uptimely/) | Package implementation for analytics, compilation, dependencies, documentation, and execution. |
81
92
  | [`tests/`](tests/) | Automated tests for the package and examples. |
82
93
 
83
94
  ## Development
@@ -4,8 +4,8 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [project]
6
6
  name = "uptimely-core"
7
- version = "0.1.0"
8
- description = "Declarative numerical analytics with complex dependencies at scale"
7
+ version = "0.2.0.dev0"
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"
11
11
  license-files = ["LICENSE"]
@@ -39,7 +39,7 @@ uptimely = "uptimely.cli.cli:main"
39
39
  uptimely-mcp = "uptimely.mcp.server:main"
40
40
 
41
41
  [project.urls]
42
- Homepage = "https://github.com/uptimely-ai/uptimely-core"
42
+ Homepage = "http://uptimely.ai/core/"
43
43
  Repository = "https://github.com/uptimely-ai/uptimely-core"
44
44
  Documentation = "https://uptimely-ai.github.io/uptimely-core"
45
45
 
@@ -1,8 +1,11 @@
1
1
  # Uptimely Core
2
2
 
3
- Uptimely Core is an open source Python package for declarative numerical analytics with complex dependencies at scale.
3
+ Uptimely Core is an open source Python package for declarative numerical
4
+ analytics with complex dependencies at scale.
4
5
 
5
- It describes analytics workflows in a specification similar to OpenAPI, uses a declarative model inspired by Kubernetes. The framework generates plans, graphs, and documentation from that specification.
6
+ Analytics definitions describe workflows in JSON, using a declarative model
7
+ inspired by OpenAPI and Kubernetes. The compiler generates plans from the
8
+ analytics; the package can also render dependency graphs and documentation.
6
9
 
7
10
  Ships with simple engine and plans to integrate to common execution backends.
8
11
 
@@ -26,15 +29,23 @@ Read more at [uptimely.ai website](https://uptimely.ai).
26
29
  Python 3.13 or newer is required. Install the package, download the examples
27
30
  and run.
28
31
 
32
+ With pip:
33
+
29
34
  ```sh
30
35
  pip install uptimely-core
31
36
  uptimely examples download
32
- python examples/run_all.py
37
+ uptimely explorer serve --project-root examples/pump_analytics
33
38
  ```
34
39
 
35
- Read more from [examples](examples/README.md).
40
+ With Poetry, from your Poetry project:
41
+
42
+ ```sh
43
+ poetry add uptimely-core
44
+ poetry run uptimely examples download
45
+ poetry run uptimely explorer serve --project-root examples/pump_analytics
46
+ ```
36
47
 
37
- Explore the examples files, generated resources and modify for the needs of your own project.
48
+ More use cases in [examples](examples/README.md). Explore the examples files, generated resources and modify for the needs of your own project.
38
49
 
39
50
  ## Documentation
40
51
 
@@ -47,7 +58,7 @@ Explore the [documentation site](https://uptimely-ai.github.io/uptimely-core/).
47
58
  | [`docs/`](docs/index.md) | Architecture, specification, and development documentation. |
48
59
  | [`examples/`](examples/README.md) | Runnable examples and generated artifacts. |
49
60
  | [`scripts/`](scripts) | Code snippets for repository management |
50
- | [`src/uptimely/`](src/uptimely/) | Package implementation for specifications, compilation, dependencies, documentation, and engine. |
61
+ | [`src/uptimely/`](src/uptimely/) | Package implementation for analytics, compilation, dependencies, documentation, and execution. |
51
62
  | [`tests/`](tests/) | Automated tests for the package and examples. |
52
63
 
53
64
  ## Development
@@ -0,0 +1,40 @@
1
+ """Public package namespace for uptimely."""
2
+
3
+ from .analytics import (
4
+ Analytics,
5
+ Call,
6
+ Dimension,
7
+ Entity,
8
+ FeatureGraph,
9
+ Function,
10
+ PreparedAnalytics,
11
+ PythonAnalytics,
12
+ SubGraph,
13
+ from_python,
14
+ load_analytics,
15
+ prepare_analytics,
16
+ write_document,
17
+ )
18
+ from .compiler import CallableGraph, CallableNode, Compiler, ExecutionPlan
19
+ from .engine import Engine
20
+
21
+ __all__ = [
22
+ "Analytics",
23
+ "Call",
24
+ "CallableGraph",
25
+ "CallableNode",
26
+ "Compiler",
27
+ "Dimension",
28
+ "Entity",
29
+ "ExecutionPlan",
30
+ "Function",
31
+ "PythonAnalytics",
32
+ "PreparedAnalytics",
33
+ "from_python",
34
+ "load_analytics",
35
+ "prepare_analytics",
36
+ "write_document",
37
+ "SubGraph",
38
+ "FeatureGraph",
39
+ "Engine",
40
+ ]
@@ -0,0 +1,65 @@
1
+ """Public package namespace for OpenAnalytics analytics."""
2
+
3
+ from uptimely.row_context import RowContext
4
+
5
+ from .api import from_python, load_analytics, prepare_analytics
6
+ from .feature_graph import FeatureGraph, SubGraph
7
+ from .json.codec import write_document
8
+ from .model import (
9
+ Analytics,
10
+ Argument,
11
+ Binding,
12
+ Dataset,
13
+ Dimension,
14
+ Entity,
15
+ Feature,
16
+ FunctionCatalog,
17
+ Mapping,
18
+ Order,
19
+ RowScope,
20
+ )
21
+ from .python import (
22
+ PythonAnalytics,
23
+ bind,
24
+ dataset,
25
+ dimension,
26
+ entity,
27
+ feature,
28
+ mapping,
29
+ ref,
30
+ row_context,
31
+ )
32
+ from .resolved import Call, Function, PreparedAnalytics
33
+
34
+ __all__ = [
35
+ "Argument",
36
+ "Binding",
37
+ "Call",
38
+ "Dataset",
39
+ "Dimension",
40
+ "Entity",
41
+ "Feature",
42
+ "FeatureGraph",
43
+ "Function",
44
+ "FunctionCatalog",
45
+ "Mapping",
46
+ "Order",
47
+ "PythonAnalytics",
48
+ "RowContext",
49
+ "RowScope",
50
+ "Analytics",
51
+ "PreparedAnalytics",
52
+ "from_python",
53
+ "load_analytics",
54
+ "prepare_analytics",
55
+ "write_document",
56
+ "SubGraph",
57
+ "bind",
58
+ "dataset",
59
+ "dimension",
60
+ "entity",
61
+ "feature",
62
+ "mapping",
63
+ "ref",
64
+ "row_context",
65
+ ]
@@ -0,0 +1,82 @@
1
+ """Build and load validated analytics through explicit application operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import pathlib
6
+
7
+ from . import model, resolution, resolved, validation
8
+ from .json import codec
9
+ from .python import conversion
10
+
11
+
12
+ def prepare_analytics(document: model.Analytics) -> resolved.PreparedAnalytics:
13
+ """Resolve and validate a document without adding derived state to it.
14
+
15
+ Preparation uses declarative metadata, not executable Python signatures.
16
+ Rebuild the result after changing the document; it is not an automatic cache.
17
+
18
+ Args:
19
+ document: An analytics document that conforms to the document model.
20
+
21
+ Returns:
22
+ The document paired with its resolved functions and calls.
23
+
24
+ Raises:
25
+ ValueError: Analytics references, mappings, or dependencies are invalid.
26
+ """
27
+ functions, calls = resolution.resolve_functions(document)
28
+ validation.validate_prepared(document, functions, calls)
29
+ return resolved.PreparedAnalytics(document=document, functions=functions, calls=calls)
30
+
31
+
32
+ def load_analytics(path: str | pathlib.Path) -> model.Analytics:
33
+ """Load and semantically validate an analytics JSON file or directory.
34
+
35
+ Args:
36
+ path: A JSON document or a directory containing ``analytics.json``.
37
+
38
+ Returns:
39
+ A validated analytics document without attached derived state.
40
+
41
+ Raises:
42
+ OSError: The JSON document cannot be read.
43
+ ValueError: The document or its analytics declarations are invalid.
44
+ """
45
+ document = model.Analytics.model_validate(codec.load_document(path))
46
+ prepare_analytics(document)
47
+ return document
48
+
49
+
50
+ def from_python(
51
+ datasets: conversion.Datasets | None = None,
52
+ functions: conversion.Functions | None = None,
53
+ name: str | None = None,
54
+ dimensions: conversion.Dimensions | None = None,
55
+ *,
56
+ entities: conversion.Entities | None = None,
57
+ ) -> model.Analytics:
58
+ """Convert Python declarations and semantically validate the resulting model.
59
+
60
+ Args:
61
+ datasets: Dataset declarations wrapped in a single entity.
62
+ functions: Explicit catalog, or infer it from Python declarations.
63
+ name: Optional analytics name and synthetic entity identifier.
64
+ dimensions: Global dimension declarations.
65
+ entities: Explicit entity declarations, mutually exclusive with datasets.
66
+
67
+ Returns:
68
+ A validated analytics document without attached derived state.
69
+
70
+ Raises:
71
+ TypeError: Declarations have unsupported types.
72
+ ValueError: Declarations conflict or analytics semantics are invalid.
73
+ """
74
+ document = conversion.to_model(
75
+ datasets=datasets,
76
+ functions=functions,
77
+ name=name,
78
+ dimensions=dimensions,
79
+ entities=entities,
80
+ )
81
+ prepare_analytics(document)
82
+ return document
@@ -0,0 +1,41 @@
1
+ """Shared function-catalog construction, merging, and serialization."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import collections.abc
6
+
7
+ from uptimely import contracts
8
+
9
+ from . import model as analytics_model
10
+
11
+
12
+ def function_template(
13
+ function_id: str,
14
+ operation: contracts.FunctionOperation,
15
+ entrypoint: str,
16
+ args: dict[str, analytics_model.Argument],
17
+ ) -> analytics_model.FunctionTemplate:
18
+ """Create a function template for the analytics document from its declarations."""
19
+ return analytics_model.FunctionTemplate(
20
+ id=function_id,
21
+ operation=operation,
22
+ returns=contracts.OPERATION_RETURN_KINDS[operation],
23
+ entrypoint=entrypoint,
24
+ args=args,
25
+ )
26
+
27
+
28
+ def merge_function_catalogs(
29
+ catalogs: collections.abc.Iterable[analytics_model.FunctionCatalog],
30
+ ) -> dict[str, analytics_model.FunctionTemplate]:
31
+ """Merge identical definitions and reject conflicting templates."""
32
+ functions: dict[str, analytics_model.FunctionTemplate] = {}
33
+ for catalog in catalogs:
34
+ for function_id, function in catalog.root.items():
35
+ current = functions.get(function_id)
36
+ if current is None:
37
+ functions[function_id] = function
38
+ continue
39
+ if current != function:
40
+ raise ValueError(f"Conflicting function definition for '{function_id}'")
41
+ return functions
@@ -0,0 +1,14 @@
1
+ """Feature dependency graph models and visualization."""
2
+
3
+ from .graph import FeatureGraph, GraphBase, Node, NodeMeta, SubGraph
4
+ from .models import FeatureNode, FunctionNode
5
+
6
+ __all__ = [
7
+ "FeatureGraph",
8
+ "FeatureNode",
9
+ "FunctionNode",
10
+ "GraphBase",
11
+ "Node",
12
+ "NodeMeta",
13
+ "SubGraph",
14
+ ]
@@ -5,9 +5,9 @@ import re
5
5
  import graphable as graphable_
6
6
  from graphable import views
7
7
 
8
- from uptimely import spec
9
- from uptimely.spec.json_spec import bindings
10
-
8
+ from .. import api, resolved
9
+ from .. import model as analytics_model
10
+ from .. import references as bindings
11
11
  from . import models
12
12
 
13
13
  Node = graphable_.graphable.Graphable
@@ -19,7 +19,7 @@ class GraphBase:
19
19
 
20
20
  graph: graphable_.graph.Graph
21
21
  _node_index: dict[str, Node]
22
- _function_catalog: dict[str, spec.Function]
22
+ _function_catalog: dict[str, resolved.Function]
23
23
  _explicit_output_names: list[str] | None
24
24
 
25
25
  @staticmethod
@@ -142,29 +142,30 @@ class GraphBase:
142
142
  class FeatureGraph(GraphBase):
143
143
  """Global dependency graph built from all feature mappings."""
144
144
 
145
- def __init__(self, specs: spec.Specification) -> None:
145
+ def __init__(self, analytics: analytics_model.Analytics) -> None:
146
146
  """Load analytics function calls and build the complete dependency graph."""
147
+ prepared = api.prepare_analytics(analytics)
147
148
  self.functions = [
148
- function for function in specs.resolved_functions if function.role == "analytics"
149
+ function for function in prepared.functions if function.role == "analytics"
149
150
  ]
150
- self._graph_operations: list[tuple[spec.Function, str, str, list[str], list[str]]] = []
151
+ self._graph_operations: list[tuple[resolved.Function, str, str, list[str], list[str]]] = []
151
152
  vectorize_functions = {function.id: function for function in self.functions}
152
153
 
153
- for entity_id, entity in specs.entities.items():
154
+ for entity_id, entity in analytics.entities.items():
154
155
  for dataset in entity.datasets:
155
156
  for operation_id, mapping in dataset.mappings.items():
156
- operation_type = bindings.mapping_category(mapping, specs.functions)
157
- template = getattr(specs.functions, operation_type)[mapping.binding.function]
157
+ operation = bindings.mapping_operation(mapping, analytics.functions)
158
+ template = analytics.functions[mapping.binding.function]
158
159
  function = (
159
160
  vectorize_functions.get(mapping.binding.function)
160
- if operation_type == "vectorize"
161
- else spec.Function(
161
+ if operation == "vectorize"
162
+ else resolved.Function(
162
163
  id=mapping.binding.function,
163
164
  type="python_function",
164
165
  return_type="dataframe",
165
166
  info={"description": getattr(template, "description", None)},
166
167
  entrypoint=template.entrypoint,
167
- role=operation_type,
168
+ role=operation,
168
169
  )
169
170
  )
170
171
  if function is None:
@@ -183,13 +184,13 @@ class FeatureGraph(GraphBase):
183
184
  [
184
185
  feature_id
185
186
  for feature_id in mapping.output_features
186
- if feature_id not in specs.dimensions
187
+ if feature_id not in analytics.dimensions
187
188
  ],
188
189
  list(dict.fromkeys(dependencies)),
189
190
  )
190
191
  )
191
192
 
192
- self.specification = specs
193
+ self.analytics = analytics
193
194
  self._function_catalog = {function.id: function for function in self.functions}
194
195
  self._node_index: dict[str, Node] = {}
195
196
  self._explicit_output_names = None
@@ -199,7 +200,7 @@ class FeatureGraph(GraphBase):
199
200
  self,
200
201
  function_name: str,
201
202
  dependencies: list[str],
202
- function: spec.Function | None = None,
203
+ function: resolved.Function | None = None,
203
204
  ) -> set[str]:
204
205
  """Return direct parent feature names for a bound feature."""
205
206
  function = function or self._function_catalog.get(function_name)
@@ -235,7 +236,7 @@ class FeatureGraph(GraphBase):
235
236
  output
236
237
  for _function, _entity, _operation_id, outputs, _dependencies in self._graph_operations
237
238
  for output in outputs
238
- if output not in self.specification.dimensions
239
+ if output not in self.analytics.dimensions
239
240
  }
240
241
  dependency_names = {
241
242
  parent
@@ -263,20 +264,17 @@ class FeatureGraph(GraphBase):
263
264
  ),
264
265
  )
265
266
 
266
- for entity_id, entity in self.specification.entities.items():
267
+ for entity_id, entity in self.analytics.entities.items():
267
268
  for dataset in entity.datasets:
268
269
  for mapping in dataset.mappings.values():
269
- operation_type = bindings.mapping_category(
270
- mapping, self.specification.functions
271
- )
270
+ operation = bindings.mapping_operation(mapping, self.analytics.functions)
272
271
  for name in mapping.output_features:
273
272
  feature = dataset.features[name]
274
273
  register_feature_metadata(
275
274
  name,
276
275
  feature.type,
277
276
  entity_id,
278
- is_source_input=operation_type != "collapse"
279
- and operation_type != "vectorize",
277
+ is_source_input=operation != "collapse" and operation != "vectorize",
280
278
  )
281
279
  for function, entity, _operation_id, outputs, _dependencies in self._graph_operations:
282
280
  for output in outputs:
@@ -293,7 +291,7 @@ class FeatureGraph(GraphBase):
293
291
 
294
292
  def feature_meta(
295
293
  name: str,
296
- function: spec.Function | None = None,
294
+ function: resolved.Function | None = None,
297
295
  entity: str | None = None,
298
296
  data_type: str | None = None,
299
297
  ) -> models.FeatureNode:
@@ -1,11 +1,8 @@
1
1
  import dataclasses
2
2
 
3
- from uptimely import spec
3
+ from uptimely import render
4
4
 
5
-
6
- def _filled_fields(**fields: object) -> dict:
7
- """Return fields with values useful for graph rendering."""
8
- return {key: value for key, value in fields.items() if value is not None}
5
+ from .. import resolved
9
6
 
10
7
 
11
8
  @dataclasses.dataclass(frozen=True)
@@ -15,7 +12,7 @@ class FunctionNode:
15
12
  name: str
16
13
  features: tuple[str, ...]
17
14
  entity: str | None
18
- function: spec.Function | None = dataclasses.field(default=None, compare=False, hash=False)
15
+ function: resolved.Function | None = dataclasses.field(default=None, compare=False, hash=False)
19
16
 
20
17
  @property
21
18
  def node_id(self) -> str:
@@ -29,9 +26,9 @@ class FunctionNode:
29
26
  def __repr__(self) -> str:
30
27
  return self.name
31
28
 
32
- def to_render_data(self, node_type: str = "function") -> dict:
29
+ def to_render_data(self, node_type: str = "function") -> dict[str, object]:
33
30
  """Return display metadata for rendering this function node."""
34
- return _filled_fields(
31
+ return render.filled_fields(
35
32
  node_type=node_type,
36
33
  is_function=True,
37
34
  label=self.name,
@@ -77,9 +74,9 @@ class FeatureNode:
77
74
  """Whether this feature is both produced and consumed by the graph."""
78
75
  return self.is_input and self.is_output
79
76
 
80
- def to_render_data(self, node_type: str) -> dict:
77
+ def to_render_data(self, node_type: str) -> dict[str, object]:
81
78
  """Return display metadata for rendering this feature node."""
82
- return _filled_fields(
79
+ return render.filled_fields(
83
80
  node_type=node_type,
84
81
  is_function=False,
85
82
  label=self.name,
@@ -0,0 +1 @@
1
+ """Analytics JSON document encoding and I/O."""
@@ -0,0 +1,42 @@
1
+ """JSON serialization helpers for analytics models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import pathlib
7
+ import typing
8
+
9
+ from uptimely import storage as storage_module
10
+
11
+ if typing.TYPE_CHECKING:
12
+ from .. import model as analytics_model
13
+
14
+
15
+ def load_document(path: str | pathlib.Path) -> dict[str, typing.Any]:
16
+ """Read one JSON document, rejecting unsupported multi-file definitions."""
17
+ if storage_module.Storage.is_dir(path):
18
+ path = storage_module.Storage.join(path, "analytics.json")
19
+ analytics_definition = json.loads(storage_module.Storage.read(path))
20
+ if analytics_definition.get("includes", []) or analytics_definition.get("external_specs", []):
21
+ raise ValueError("Analytics must be defined in a single JSON file")
22
+ return analytics_definition
23
+
24
+
25
+ def to_document(analytics_definition: analytics_model.Analytics) -> dict[str, typing.Any]:
26
+ """Encode document models using the analytics document's identifier rules."""
27
+ payload = analytics_definition.model_dump(exclude_none=False)
28
+ for entity_id, entity in analytics_definition.entities.items():
29
+ serialized_entity = payload["entities"][entity_id]
30
+ for index, dataset in enumerate(entity.datasets):
31
+ serialized_entity["datasets"][index]["id"] = dataset.id
32
+ return payload
33
+
34
+
35
+ def write_document(
36
+ analytics_definition: analytics_model.Analytics, path: str | pathlib.Path
37
+ ) -> str:
38
+ """Write an analytics document without mutating the source model."""
39
+ target = pathlib.Path(path)
40
+ target.parent.mkdir(parents=True, exist_ok=True)
41
+ target.write_text(json.dumps(to_document(analytics_definition), indent=2), encoding="utf-8")
42
+ return str(target)
@@ -0,0 +1,48 @@
1
+ """Analytics document models, independent of their authoring format."""
2
+
3
+ from .analytics import SUPPORTED_ANALYTICS_VERSION, Analytics
4
+ from .common import (
5
+ AnalyticsSource,
6
+ Argument,
7
+ Binding,
8
+ BindingArgument,
9
+ Dimension,
10
+ StrictModel,
11
+ )
12
+ from .dataset import (
13
+ Dataset,
14
+ Entity,
15
+ Feature,
16
+ Mapping,
17
+ MappingFeatures,
18
+ Order,
19
+ RowScope,
20
+ )
21
+ from .functions import (
22
+ FUNCTION_OPERATIONS,
23
+ FUNCTION_TYPES,
24
+ FunctionCatalog,
25
+ FunctionTemplate,
26
+ )
27
+
28
+ __all__ = [
29
+ "FUNCTION_OPERATIONS",
30
+ "FUNCTION_TYPES",
31
+ "SUPPORTED_ANALYTICS_VERSION",
32
+ "Analytics",
33
+ "AnalyticsSource",
34
+ "Argument",
35
+ "Binding",
36
+ "BindingArgument",
37
+ "Dataset",
38
+ "Dimension",
39
+ "Entity",
40
+ "Feature",
41
+ "FunctionCatalog",
42
+ "FunctionTemplate",
43
+ "Mapping",
44
+ "MappingFeatures",
45
+ "Order",
46
+ "RowScope",
47
+ "StrictModel",
48
+ ]