moose-lib 0.6.129__tar.gz → 0.6.131__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.
- {moose_lib-0.6.129 → moose_lib-0.6.131}/PKG-INFO +1 -1
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/data_models.py +44 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib.egg-info/PKG-INFO +1 -1
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib.egg-info/SOURCES.txt +2 -1
- moose_lib-0.6.131/tests/test_simple_aggregate.py +112 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/README.md +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/blocks.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/clients/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/clients/redis_client.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/commons.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/config/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/config/config_file.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/config/runtime.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/_registry.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/consumption.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/ingest_api.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/ingest_pipeline.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/life_cycle.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/materialized_view.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/olap_table.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/registry.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/sql_resource.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/stream.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/types.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/view.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2/workflow.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/dmv2_serializer.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/internal.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/main.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/query_builder.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/query_param.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/streaming/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/streaming/streaming_function_runner.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/utilities/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib/utilities/sql.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib.egg-info/dependency_links.txt +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib.egg-info/requires.txt +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/moose_lib.egg-info/top_level.txt +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/setup.cfg +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/setup.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/__init__.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/conftest.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/test_moose.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/test_olap_table_versioning.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/test_query_builder.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/test_redis_client.py +0 -0
- {moose_lib-0.6.129 → moose_lib-0.6.131}/tests/test_s3queue_config.py +0 -0
|
@@ -78,6 +78,46 @@ class AggregateFunction:
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
def simple_aggregated[T](
|
|
82
|
+
agg_func: str,
|
|
83
|
+
arg_type: Type[T]
|
|
84
|
+
) -> Type[T]:
|
|
85
|
+
"""Helper to create a SimpleAggregateFunction type annotation.
|
|
86
|
+
|
|
87
|
+
SimpleAggregateFunction is a ClickHouse type for storing aggregated values directly
|
|
88
|
+
instead of intermediate states. It's more efficient for functions like sum, max, min, etc.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
agg_func: The aggregation function name (e.g., "sum", "max", "anyLast")
|
|
92
|
+
arg_type: The argument type for the function (also the result type)
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
An Annotated type with SimpleAggregateFunction metadata
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
```python
|
|
99
|
+
from moose_lib import simple_aggregated
|
|
100
|
+
|
|
101
|
+
row_count: simple_aggregated("sum", int)
|
|
102
|
+
max_value: simple_aggregated("max", float)
|
|
103
|
+
last_status: simple_aggregated("anyLast", str)
|
|
104
|
+
```
|
|
105
|
+
"""
|
|
106
|
+
return Annotated[arg_type, SimpleAggregateFunction(agg_func=agg_func, arg_type=arg_type)]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclasses.dataclass(frozen=True)
|
|
110
|
+
class SimpleAggregateFunction:
|
|
111
|
+
agg_func: str
|
|
112
|
+
arg_type: type | GenericAlias | _BaseGenericAlias
|
|
113
|
+
|
|
114
|
+
def to_dict(self):
|
|
115
|
+
return {
|
|
116
|
+
"functionName": self.agg_func,
|
|
117
|
+
"argumentType": py_type_to_column_type(self.arg_type, [])[2]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
81
121
|
def enum_value_serializer(value: int | str):
|
|
82
122
|
if isinstance(value, int):
|
|
83
123
|
return {"Int": value}
|
|
@@ -352,6 +392,10 @@ def _to_columns(model: type[BaseModel]) -> list[Column]:
|
|
|
352
392
|
annotations.append(
|
|
353
393
|
("aggregationFunction", md.to_dict())
|
|
354
394
|
)
|
|
395
|
+
if isinstance(md, SimpleAggregateFunction):
|
|
396
|
+
annotations.append(
|
|
397
|
+
("simpleAggregationFunction", md.to_dict())
|
|
398
|
+
)
|
|
355
399
|
if md == "LowCardinality":
|
|
356
400
|
annotations.append(
|
|
357
401
|
("LowCardinality", True)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
from moose_lib import simple_aggregated, Key
|
|
5
|
+
from moose_lib.data_models import SimpleAggregateFunction, _to_columns
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_simple_aggregated_helper():
|
|
9
|
+
"""Test that simple_aggregated helper creates correct annotation"""
|
|
10
|
+
annotated_type = simple_aggregated('sum', int)
|
|
11
|
+
|
|
12
|
+
# Check that it's annotated
|
|
13
|
+
assert hasattr(annotated_type, '__metadata__')
|
|
14
|
+
metadata = annotated_type.__metadata__[0]
|
|
15
|
+
|
|
16
|
+
# Check metadata is SimpleAggregateFunction instance
|
|
17
|
+
assert isinstance(metadata, SimpleAggregateFunction)
|
|
18
|
+
assert metadata.agg_func == 'sum'
|
|
19
|
+
assert metadata.arg_type == int
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_simple_aggregate_function_to_dict():
|
|
23
|
+
"""Test that SimpleAggregateFunction.to_dict() creates correct structure"""
|
|
24
|
+
func = SimpleAggregateFunction(agg_func='sum', arg_type=int)
|
|
25
|
+
result = func.to_dict()
|
|
26
|
+
|
|
27
|
+
assert result['functionName'] == 'sum'
|
|
28
|
+
assert 'argumentType' in result
|
|
29
|
+
# Python int maps to "Int" by default (not "Int64")
|
|
30
|
+
assert result['argumentType'] == 'Int'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_simple_aggregate_function_to_dict_with_different_types():
|
|
34
|
+
"""Test SimpleAggregateFunction.to_dict() with various types"""
|
|
35
|
+
# Test with float
|
|
36
|
+
func_float = SimpleAggregateFunction(agg_func='max', arg_type=float)
|
|
37
|
+
result_float = func_float.to_dict()
|
|
38
|
+
assert result_float['functionName'] == 'max'
|
|
39
|
+
assert result_float['argumentType'] == 'Float64'
|
|
40
|
+
|
|
41
|
+
# Test with str
|
|
42
|
+
func_str = SimpleAggregateFunction(agg_func='anyLast', arg_type=str)
|
|
43
|
+
result_str = func_str.to_dict()
|
|
44
|
+
assert result_str['functionName'] == 'anyLast'
|
|
45
|
+
assert result_str['argumentType'] == 'String'
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_dataclass_with_simple_aggregated():
|
|
49
|
+
"""Test that BaseModel with simple_aggregated field converts correctly"""
|
|
50
|
+
class TestModel(BaseModel):
|
|
51
|
+
date_stamp: Key[datetime.datetime]
|
|
52
|
+
table_name: Key[str]
|
|
53
|
+
row_count: simple_aggregated('sum', int)
|
|
54
|
+
|
|
55
|
+
columns = _to_columns(TestModel)
|
|
56
|
+
|
|
57
|
+
# Find the row_count column
|
|
58
|
+
row_count_col = next(c for c in columns if c.name == 'row_count')
|
|
59
|
+
|
|
60
|
+
# Check basic type - Python int maps to "Int"
|
|
61
|
+
assert row_count_col.data_type == 'Int'
|
|
62
|
+
|
|
63
|
+
# Check annotation
|
|
64
|
+
simple_agg_annotation = next(
|
|
65
|
+
(a for a in row_count_col.annotations if a[0] == 'simpleAggregationFunction'),
|
|
66
|
+
None
|
|
67
|
+
)
|
|
68
|
+
assert simple_agg_annotation is not None
|
|
69
|
+
assert simple_agg_annotation[1]['functionName'] == 'sum'
|
|
70
|
+
assert simple_agg_annotation[1]['argumentType'] == 'Int'
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_multiple_simple_aggregated_fields():
|
|
74
|
+
"""Test BaseModel with multiple SimpleAggregateFunction fields"""
|
|
75
|
+
class StatsModel(BaseModel):
|
|
76
|
+
timestamp: Key[datetime.datetime]
|
|
77
|
+
total_count: simple_aggregated('sum', int)
|
|
78
|
+
max_value: simple_aggregated('max', int)
|
|
79
|
+
min_value: simple_aggregated('min', int)
|
|
80
|
+
last_seen: simple_aggregated('anyLast', datetime.datetime)
|
|
81
|
+
|
|
82
|
+
columns = _to_columns(StatsModel)
|
|
83
|
+
|
|
84
|
+
# Test sum
|
|
85
|
+
sum_col = next(c for c in columns if c.name == 'total_count')
|
|
86
|
+
sum_annotation = next(
|
|
87
|
+
a for a in sum_col.annotations if a[0] == 'simpleAggregationFunction'
|
|
88
|
+
)
|
|
89
|
+
assert sum_annotation[1]['functionName'] == 'sum'
|
|
90
|
+
|
|
91
|
+
# Test max
|
|
92
|
+
max_col = next(c for c in columns if c.name == 'max_value')
|
|
93
|
+
max_annotation = next(
|
|
94
|
+
a for a in max_col.annotations if a[0] == 'simpleAggregationFunction'
|
|
95
|
+
)
|
|
96
|
+
assert max_annotation[1]['functionName'] == 'max'
|
|
97
|
+
|
|
98
|
+
# Test min
|
|
99
|
+
min_col = next(c for c in columns if c.name == 'min_value')
|
|
100
|
+
min_annotation = next(
|
|
101
|
+
a for a in min_col.annotations if a[0] == 'simpleAggregationFunction'
|
|
102
|
+
)
|
|
103
|
+
assert min_annotation[1]['functionName'] == 'min'
|
|
104
|
+
|
|
105
|
+
# Test anyLast with datetime
|
|
106
|
+
last_col = next(c for c in columns if c.name == 'last_seen')
|
|
107
|
+
assert last_col.data_type == 'DateTime'
|
|
108
|
+
last_annotation = next(
|
|
109
|
+
a for a in last_col.annotations if a[0] == 'simpleAggregationFunction'
|
|
110
|
+
)
|
|
111
|
+
assert last_annotation[1]['functionName'] == 'anyLast'
|
|
112
|
+
assert last_annotation[1]['argumentType'] == 'DateTime'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|