strawberry-django-aggregates 0.2.1__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.
- strawberry_django_aggregates-0.2.1/LICENSE +29 -0
- strawberry_django_aggregates-0.2.1/PKG-INFO +128 -0
- strawberry_django_aggregates-0.2.1/README.md +98 -0
- strawberry_django_aggregates-0.2.1/pyproject.toml +74 -0
- strawberry_django_aggregates-0.2.1/setup.cfg +4 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/__init__.py +146 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/builder.py +1865 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/compiler.py +2295 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/errors.py +73 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/fill.py +325 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/granularity.py +96 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/operators.py +174 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/ordering.py +267 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/pagination.py +138 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/relations.py +536 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates/types.py +1263 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates.egg-info/PKG-INFO +128 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates.egg-info/SOURCES.txt +38 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates.egg-info/dependency_links.txt +1 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates.egg-info/requires.txt +5 -0
- strawberry_django_aggregates-0.2.1/strawberry_django_aggregates.egg-info/top_level.txt +1 -0
- strawberry_django_aggregates-0.2.1/tests/test_aggregate_correctness.py +575 -0
- strawberry_django_aggregates-0.2.1/tests/test_bool_aliases.py +282 -0
- strawberry_django_aggregates-0.2.1/tests/test_bucket_range.py +310 -0
- strawberry_django_aggregates-0.2.1/tests/test_builder_integration.py +291 -0
- strawberry_django_aggregates-0.2.1/tests/test_comodel_ordering.py +300 -0
- strawberry_django_aggregates-0.2.1/tests/test_cursor_pagination.py +611 -0
- strawberry_django_aggregates-0.2.1/tests/test_determinism.py +67 -0
- strawberry_django_aggregates-0.2.1/tests/test_federation.py +153 -0
- strawberry_django_aggregates-0.2.1/tests/test_fill_temporal.py +752 -0
- strawberry_django_aggregates-0.2.1/tests/test_groupby_timezones.py +127 -0
- strawberry_django_aggregates-0.2.1/tests/test_having_aliases.py +89 -0
- strawberry_django_aggregates-0.2.1/tests/test_jsonb_groupby.py +378 -0
- strawberry_django_aggregates-0.2.1/tests/test_no_relation_traversal.py +216 -0
- strawberry_django_aggregates-0.2.1/tests/test_order_on_aggregates.py +99 -0
- strawberry_django_aggregates-0.2.1/tests/test_percentile.py +184 -0
- strawberry_django_aggregates-0.2.1/tests/test_postgres_only_ops.py +68 -0
- strawberry_django_aggregates-0.2.1/tests/test_relation_aggregate.py +412 -0
- strawberry_django_aggregates-0.2.1/tests/test_streaming.py +518 -0
- strawberry_django_aggregates-0.2.1/tests/test_week_start.py +525 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, the strawberry-django-aggregates maintainers.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: strawberry-django-aggregates
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Hasura-shape aggregations over Django querysets in Strawberry GraphQL — count, sum, avg, min, max, stddev, variance, array_agg + Odoo-grade group-by with timezone-correct date bucketing.
|
|
5
|
+
Author: strawberry-django-aggregates maintainers
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/Apexive/strawberry-django-aggregates
|
|
8
|
+
Project-URL: Documentation, https://github.com/Apexive/strawberry-django-aggregates/blob/main/docs/SPEC.md
|
|
9
|
+
Project-URL: Issues, https://github.com/Apexive/strawberry-django-aggregates/issues
|
|
10
|
+
Keywords: django,strawberry,graphql,aggregate,groupby,hasura,postgraphile,odoo,analytics
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Framework :: Django :: 6.0
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.14
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: django>=6.0
|
|
26
|
+
Requires-Dist: strawberry-graphql>=0.315.0
|
|
27
|
+
Requires-Dist: strawberry-graphql-django>=0.82.0
|
|
28
|
+
Provides-Extra: postgres
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# strawberry-django-aggregates
|
|
32
|
+
|
|
33
|
+
Hasura-shape aggregations over Django querysets in Strawberry GraphQL.
|
|
34
|
+
|
|
35
|
+
`count` · `count_distinct` · `sum` · `avg` · `min` · `max` · `stddev` · `variance` · `bool_and` · `bool_or` · `array_agg` · `string_agg` — composed with multi-level `group_by`, `having` filters, and ordering on aggregate aliases. Inspired by [Hasura](https://hasura.io/docs/latest/api-reference/graphql-api/query/#aggregateobject)'s `<table>_aggregate`, [PostGraphile](https://github.com/graphile/pg-aggregates)'s `pg-aggregates`, and [Odoo 18](https://github.com/odoo/odoo/blob/18.0/odoo/models.py)'s `_read_group`. Built for [`strawberry-django`](https://strawberry-django.readthedocs.io) over PostgreSQL and SQLite.
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from decimal import Decimal
|
|
39
|
+
from datetime import datetime
|
|
40
|
+
from strawberry import auto
|
|
41
|
+
import strawberry, strawberry_django
|
|
42
|
+
from strawberry_django_aggregates import AggregateBuilder
|
|
43
|
+
|
|
44
|
+
from .models import Order
|
|
45
|
+
|
|
46
|
+
@strawberry_django.type(Order)
|
|
47
|
+
class OrderType:
|
|
48
|
+
id: auto
|
|
49
|
+
customer: "CustomerType"
|
|
50
|
+
total: Decimal
|
|
51
|
+
status: str
|
|
52
|
+
created_at: datetime
|
|
53
|
+
|
|
54
|
+
# One call wires count/sum/avg/min/max + group_by + having into the schema:
|
|
55
|
+
order_aggs = AggregateBuilder(
|
|
56
|
+
model=Order,
|
|
57
|
+
aggregate_fields=["total"],
|
|
58
|
+
group_by_fields=["customer", "status", "created_at"],
|
|
59
|
+
).build()
|
|
60
|
+
|
|
61
|
+
@strawberry.type
|
|
62
|
+
class Query:
|
|
63
|
+
orders_aggregate = order_aggs.aggregate_field
|
|
64
|
+
orders_group_by = order_aggs.group_by_field
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Generates a fully-typed GraphQL surface:
|
|
68
|
+
|
|
69
|
+
```graphql
|
|
70
|
+
type Query {
|
|
71
|
+
ordersAggregate(filter: OrderFilter): OrderAggregate!
|
|
72
|
+
ordersGroupBy(
|
|
73
|
+
filter: OrderFilter
|
|
74
|
+
groupBy: [OrderGroupBySpec!]!
|
|
75
|
+
having: OrderHaving
|
|
76
|
+
orderBy: [OrderGroupOrder!]
|
|
77
|
+
pagination: OffsetPagination
|
|
78
|
+
): OrderGroupedResult!
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
type OrderAggregate {
|
|
82
|
+
count: Int!
|
|
83
|
+
countDistinct(field: OrderCountableField!): Int!
|
|
84
|
+
sum: OrderSumFields
|
|
85
|
+
avg: OrderAvgFields
|
|
86
|
+
min: OrderMinFields
|
|
87
|
+
max: OrderMaxFields
|
|
88
|
+
stddev: OrderStddevFields # Postgres only
|
|
89
|
+
variance: OrderVarianceFields # Postgres only
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
type OrderGrouped {
|
|
93
|
+
key: OrderGroupKey! # composite — every requested groupBy field present
|
|
94
|
+
count: Int!
|
|
95
|
+
sum: OrderSumFields
|
|
96
|
+
# ... no recursive subgroups field — flat results
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Features
|
|
101
|
+
|
|
102
|
+
- **Hasura-canonical schema shape.** `<Model>Aggregate { count, countDistinct, sum, avg, min, max, stddev, variance, boolAnd, boolOr, arrayAgg, stringAgg }`.
|
|
103
|
+
- **Odoo-grade group-by.** Multi-level via composite keys (flat result rows), dual date-granularity tracks (`date_trunc` returning `DateTime` AND `date_part::int` returning `Int`), timezone-correct bucketing.
|
|
104
|
+
- **HAVING with aggregate aliases.** `{ countGt: 5, sumTotalGt: 1000 }` — typed inputs generated per measure.
|
|
105
|
+
- **Ordering on aggregates.** `[{ field: "total:sum", direction: DESC }]` — fail-loud on unknown terms (Odoo's pre-17 silent-drop bug avoided).
|
|
106
|
+
- **Standalone backend primitive.** `compute_aggregation(qs, group_by, aggregates, having, order_by, ...)` is callable from any Python context — DRF view, Celery task, admin script, MCP tool — not just GraphQL resolvers.
|
|
107
|
+
- **Determinism.** Type generation produces byte-identical SDL for the same inputs.
|
|
108
|
+
- **No magic.** Every operator, every granularity, every type is whitelisted.
|
|
109
|
+
|
|
110
|
+
## Non-goals
|
|
111
|
+
|
|
112
|
+
- **Cross-database aggregation.** PostgreSQL + SQLite only. SQLite degrades gracefully on `array_agg`/`string_agg`/`stddev`/`variance` — those operators raise `OperatorNotSupportedError` at resolver entry.
|
|
113
|
+
- **Auto-traversal of one-to-many / many-to-many for measures.** This is the silent row-multiplication footgun [Odoo refuses to ship](https://github.com/odoo/odoo/blob/18.0/odoo/models.py); we follow. `array_agg` is the explicit escape hatch.
|
|
114
|
+
- **Permission integration.** The library expects a pre-scoped queryset — the caller has already applied `accessible_by(user)` or equivalent. This keeps the library compatible with django-guardian, django-rules, [django-rebac](#) (when shipped), or hand-rolled permission systems.
|
|
115
|
+
|
|
116
|
+
## Status
|
|
117
|
+
|
|
118
|
+
Beta (v0.2.1). The schema shape, operator vocabulary, and `compute_aggregation` signature are stable for early adopters, but minor-level iteration is still expected before a 1.0 stability commitment — see [`docs/SPEC.md`](./docs/SPEC.md) § 16. Runtime: Python 3.13, Django 6.0.
|
|
119
|
+
|
|
120
|
+
## Documentation
|
|
121
|
+
|
|
122
|
+
- Full contract: [`docs/SPEC.md`](./docs/SPEC.md) — operator catalog, granularity tracks, HAVING semantics, ordering rules, timezone handling, and the Odoo-derived footgun audit.
|
|
123
|
+
- Naming and wire vocabulary: [`docs/TERMINOLOGY.md`](./docs/TERMINOLOGY.md)
|
|
124
|
+
- Contributor quality gate: [`CONTRIBUTING.md`](./CONTRIBUTING.md)
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
BSD-3-Clause.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# strawberry-django-aggregates
|
|
2
|
+
|
|
3
|
+
Hasura-shape aggregations over Django querysets in Strawberry GraphQL.
|
|
4
|
+
|
|
5
|
+
`count` · `count_distinct` · `sum` · `avg` · `min` · `max` · `stddev` · `variance` · `bool_and` · `bool_or` · `array_agg` · `string_agg` — composed with multi-level `group_by`, `having` filters, and ordering on aggregate aliases. Inspired by [Hasura](https://hasura.io/docs/latest/api-reference/graphql-api/query/#aggregateobject)'s `<table>_aggregate`, [PostGraphile](https://github.com/graphile/pg-aggregates)'s `pg-aggregates`, and [Odoo 18](https://github.com/odoo/odoo/blob/18.0/odoo/models.py)'s `_read_group`. Built for [`strawberry-django`](https://strawberry-django.readthedocs.io) over PostgreSQL and SQLite.
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from decimal import Decimal
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from strawberry import auto
|
|
11
|
+
import strawberry, strawberry_django
|
|
12
|
+
from strawberry_django_aggregates import AggregateBuilder
|
|
13
|
+
|
|
14
|
+
from .models import Order
|
|
15
|
+
|
|
16
|
+
@strawberry_django.type(Order)
|
|
17
|
+
class OrderType:
|
|
18
|
+
id: auto
|
|
19
|
+
customer: "CustomerType"
|
|
20
|
+
total: Decimal
|
|
21
|
+
status: str
|
|
22
|
+
created_at: datetime
|
|
23
|
+
|
|
24
|
+
# One call wires count/sum/avg/min/max + group_by + having into the schema:
|
|
25
|
+
order_aggs = AggregateBuilder(
|
|
26
|
+
model=Order,
|
|
27
|
+
aggregate_fields=["total"],
|
|
28
|
+
group_by_fields=["customer", "status", "created_at"],
|
|
29
|
+
).build()
|
|
30
|
+
|
|
31
|
+
@strawberry.type
|
|
32
|
+
class Query:
|
|
33
|
+
orders_aggregate = order_aggs.aggregate_field
|
|
34
|
+
orders_group_by = order_aggs.group_by_field
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Generates a fully-typed GraphQL surface:
|
|
38
|
+
|
|
39
|
+
```graphql
|
|
40
|
+
type Query {
|
|
41
|
+
ordersAggregate(filter: OrderFilter): OrderAggregate!
|
|
42
|
+
ordersGroupBy(
|
|
43
|
+
filter: OrderFilter
|
|
44
|
+
groupBy: [OrderGroupBySpec!]!
|
|
45
|
+
having: OrderHaving
|
|
46
|
+
orderBy: [OrderGroupOrder!]
|
|
47
|
+
pagination: OffsetPagination
|
|
48
|
+
): OrderGroupedResult!
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type OrderAggregate {
|
|
52
|
+
count: Int!
|
|
53
|
+
countDistinct(field: OrderCountableField!): Int!
|
|
54
|
+
sum: OrderSumFields
|
|
55
|
+
avg: OrderAvgFields
|
|
56
|
+
min: OrderMinFields
|
|
57
|
+
max: OrderMaxFields
|
|
58
|
+
stddev: OrderStddevFields # Postgres only
|
|
59
|
+
variance: OrderVarianceFields # Postgres only
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type OrderGrouped {
|
|
63
|
+
key: OrderGroupKey! # composite — every requested groupBy field present
|
|
64
|
+
count: Int!
|
|
65
|
+
sum: OrderSumFields
|
|
66
|
+
# ... no recursive subgroups field — flat results
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
- **Hasura-canonical schema shape.** `<Model>Aggregate { count, countDistinct, sum, avg, min, max, stddev, variance, boolAnd, boolOr, arrayAgg, stringAgg }`.
|
|
73
|
+
- **Odoo-grade group-by.** Multi-level via composite keys (flat result rows), dual date-granularity tracks (`date_trunc` returning `DateTime` AND `date_part::int` returning `Int`), timezone-correct bucketing.
|
|
74
|
+
- **HAVING with aggregate aliases.** `{ countGt: 5, sumTotalGt: 1000 }` — typed inputs generated per measure.
|
|
75
|
+
- **Ordering on aggregates.** `[{ field: "total:sum", direction: DESC }]` — fail-loud on unknown terms (Odoo's pre-17 silent-drop bug avoided).
|
|
76
|
+
- **Standalone backend primitive.** `compute_aggregation(qs, group_by, aggregates, having, order_by, ...)` is callable from any Python context — DRF view, Celery task, admin script, MCP tool — not just GraphQL resolvers.
|
|
77
|
+
- **Determinism.** Type generation produces byte-identical SDL for the same inputs.
|
|
78
|
+
- **No magic.** Every operator, every granularity, every type is whitelisted.
|
|
79
|
+
|
|
80
|
+
## Non-goals
|
|
81
|
+
|
|
82
|
+
- **Cross-database aggregation.** PostgreSQL + SQLite only. SQLite degrades gracefully on `array_agg`/`string_agg`/`stddev`/`variance` — those operators raise `OperatorNotSupportedError` at resolver entry.
|
|
83
|
+
- **Auto-traversal of one-to-many / many-to-many for measures.** This is the silent row-multiplication footgun [Odoo refuses to ship](https://github.com/odoo/odoo/blob/18.0/odoo/models.py); we follow. `array_agg` is the explicit escape hatch.
|
|
84
|
+
- **Permission integration.** The library expects a pre-scoped queryset — the caller has already applied `accessible_by(user)` or equivalent. This keeps the library compatible with django-guardian, django-rules, [django-rebac](#) (when shipped), or hand-rolled permission systems.
|
|
85
|
+
|
|
86
|
+
## Status
|
|
87
|
+
|
|
88
|
+
Beta (v0.2.1). The schema shape, operator vocabulary, and `compute_aggregation` signature are stable for early adopters, but minor-level iteration is still expected before a 1.0 stability commitment — see [`docs/SPEC.md`](./docs/SPEC.md) § 16. Runtime: Python 3.13, Django 6.0.
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
- Full contract: [`docs/SPEC.md`](./docs/SPEC.md) — operator catalog, granularity tracks, HAVING semantics, ordering rules, timezone handling, and the Odoo-derived footgun audit.
|
|
93
|
+
- Naming and wire vocabulary: [`docs/TERMINOLOGY.md`](./docs/TERMINOLOGY.md)
|
|
94
|
+
- Contributor quality gate: [`CONTRIBUTING.md`](./CONTRIBUTING.md)
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
BSD-3-Clause.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "strawberry-django-aggregates"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "Hasura-shape aggregations over Django querysets in Strawberry GraphQL — count, sum, avg, min, max, stddev, variance, array_agg + Odoo-grade group-by with timezone-correct date bucketing."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
license = { text = "BSD-3-Clause" }
|
|
8
|
+
authors = [{ name = "strawberry-django-aggregates maintainers" }]
|
|
9
|
+
keywords = [
|
|
10
|
+
"django", "strawberry", "graphql", "aggregate", "groupby",
|
|
11
|
+
"hasura", "postgraphile", "odoo", "analytics",
|
|
12
|
+
]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Framework :: Django",
|
|
16
|
+
"Framework :: Django :: 6.0",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: BSD License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Topic :: Database",
|
|
23
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
dependencies = [
|
|
28
|
+
"django>=6.0",
|
|
29
|
+
"strawberry-graphql>=0.315.0",
|
|
30
|
+
"strawberry-graphql-django>=0.82.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
# Postgres-only operators (array_agg, string_agg, stddev, variance).
|
|
35
|
+
# When this extra is NOT installed, those operators raise OperatorNotSupportedError
|
|
36
|
+
# at resolver entry on non-Postgres connections.
|
|
37
|
+
postgres = []
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/Apexive/strawberry-django-aggregates"
|
|
41
|
+
Documentation = "https://github.com/Apexive/strawberry-django-aggregates/blob/main/docs/SPEC.md"
|
|
42
|
+
Issues = "https://github.com/Apexive/strawberry-django-aggregates/issues"
|
|
43
|
+
|
|
44
|
+
[dependency-groups]
|
|
45
|
+
dev = [
|
|
46
|
+
"pytest>=8",
|
|
47
|
+
"pytest-django>=4",
|
|
48
|
+
"ruff>=0.6",
|
|
49
|
+
"mypy>=1.10",
|
|
50
|
+
"django-stubs>=5",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["setuptools>=68", "wheel"]
|
|
55
|
+
build-backend = "setuptools.build_meta"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
where = ["."]
|
|
59
|
+
include = ["strawberry_django_aggregates*"]
|
|
60
|
+
exclude = ["tests*", "docs*"]
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 79
|
|
64
|
+
target-version = "py314"
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint]
|
|
67
|
+
select = ["E", "F", "W", "I", "B", "UP"]
|
|
68
|
+
|
|
69
|
+
[tool.pytest.ini_options]
|
|
70
|
+
# Django is configured programmatically in tests/conftest.py.
|
|
71
|
+
python_files = ["test_*.py"]
|
|
72
|
+
testpaths = ["tests"]
|
|
73
|
+
addopts = "-ra --strict-markers"
|
|
74
|
+
pythonpath = ["."]
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""strawberry-django-aggregates — Hasura aggregations for Django.
|
|
2
|
+
|
|
3
|
+
Public surface:
|
|
4
|
+
|
|
5
|
+
- :class:`AggregateBuilder` — convenience builder; emits all aggregate
|
|
6
|
+
types and the corresponding strawberry fields for a given model.
|
|
7
|
+
- :func:`make_aggregate_type` — generate the ``<Model>Aggregate`` type.
|
|
8
|
+
- :func:`make_grouped_type` — generate the ``<Model>Grouped`` /
|
|
9
|
+
``<Model>GroupedResult`` types.
|
|
10
|
+
- :func:`make_having_input` — generate the ``<Model>Having`` input.
|
|
11
|
+
- :func:`make_group_by_spec` — generate the ``<Model>GroupBySpec`` input.
|
|
12
|
+
- :func:`compute_aggregation` — backend primitive; returns flat
|
|
13
|
+
composite-key result rows. Callable outside GraphQL.
|
|
14
|
+
- :class:`AggregateOp` — enum of aggregate operators.
|
|
15
|
+
- :class:`TimeGranularity`, :class:`NumberGranularity` — enums of date
|
|
16
|
+
bucketing tokens (``date_trunc`` and ``date_part::int`` respectively).
|
|
17
|
+
- :func:`parse_aggregate_order` — parser for ``"<field>:<op>"`` order
|
|
18
|
+
terms; raises on unknown.
|
|
19
|
+
- :data:`BigInt` — string-encoded 64-bit signed integer scalar. Output
|
|
20
|
+
type for ``SUM`` over integer Django fields (Postgres widens to
|
|
21
|
+
``bigint``; the 32-bit GraphQL ``Int`` would silently overflow).
|
|
22
|
+
- :class:`BucketRange` + :func:`bucket_range` — half-open ``[from, to)``
|
|
23
|
+
interval emitted as a sibling on ``<Model>GroupKey`` for each
|
|
24
|
+
TIME-granularity bucket (SPEC § 7).
|
|
25
|
+
- :func:`generate_bucket_spine` + :func:`fill_bucket_results` —
|
|
26
|
+
empty-bucket filling helpers powering ``compute_aggregation(fill=True)``
|
|
27
|
+
(SPEC § 7.2). Pure stdlib; callable from any Python context.
|
|
28
|
+
- :func:`encode_group_cursor` + :func:`decode_group_cursor` — opaque
|
|
29
|
+
cursor encoding for Relay-style grouped pagination (SPEC § 4
|
|
30
|
+
cursor-pagination). Pure stdlib; the same encoding is used by the
|
|
31
|
+
builder's connection field.
|
|
32
|
+
- :func:`register_relation_aggregate` — attach a
|
|
33
|
+
``<relation>Aggregate(filter: ...)`` field on an existing
|
|
34
|
+
strawberry-django parent type so callers can issue
|
|
35
|
+
``customer.ordersAggregate { count, sum { total } }``. Per-row
|
|
36
|
+
resolver in v1.0; dataloader-based batching is a v1.x improvement
|
|
37
|
+
(SPEC § 4.2 / Stream 9).
|
|
38
|
+
- :func:`make_grouped_connection_type` — emit Relay-style
|
|
39
|
+
``<Model>GroupedConnection`` / ``<Model>GroupedEdge`` types.
|
|
40
|
+
- Errors: :class:`AggregateError`, :class:`OperatorNotSupportedError`,
|
|
41
|
+
:class:`OrderFieldNotAllowed`, :class:`AggregationAcrossRelationError`.
|
|
42
|
+
|
|
43
|
+
See ``docs/SPEC.md`` for the full contract.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
from strawberry_django_aggregates.builder import AggregateBuilder
|
|
49
|
+
from strawberry_django_aggregates.compiler import (
|
|
50
|
+
bucket_range,
|
|
51
|
+
compute_aggregation,
|
|
52
|
+
)
|
|
53
|
+
from strawberry_django_aggregates.errors import (
|
|
54
|
+
AggregateError,
|
|
55
|
+
AggregationAcrossRelationError,
|
|
56
|
+
GranularityNotApplicable,
|
|
57
|
+
GroupByFieldNotAllowed,
|
|
58
|
+
HavingFieldNotAllowed,
|
|
59
|
+
JSONPathNotAllowed,
|
|
60
|
+
OperatorNotSupportedError,
|
|
61
|
+
OrderFieldNotAllowed,
|
|
62
|
+
)
|
|
63
|
+
from strawberry_django_aggregates.fill import (
|
|
64
|
+
fill_bucket_results,
|
|
65
|
+
generate_bucket_spine,
|
|
66
|
+
)
|
|
67
|
+
from strawberry_django_aggregates.granularity import (
|
|
68
|
+
NumberGranularity,
|
|
69
|
+
TimeGranularity,
|
|
70
|
+
validate_week_start,
|
|
71
|
+
)
|
|
72
|
+
from strawberry_django_aggregates.operators import (
|
|
73
|
+
AggregateOp,
|
|
74
|
+
default_operators_for,
|
|
75
|
+
)
|
|
76
|
+
from strawberry_django_aggregates.ordering import (
|
|
77
|
+
comodel_ordering_terms,
|
|
78
|
+
parse_aggregate_order,
|
|
79
|
+
)
|
|
80
|
+
from strawberry_django_aggregates.pagination import (
|
|
81
|
+
decode_group_cursor,
|
|
82
|
+
encode_group_cursor,
|
|
83
|
+
)
|
|
84
|
+
from strawberry_django_aggregates.relations import (
|
|
85
|
+
register_relation_aggregate,
|
|
86
|
+
)
|
|
87
|
+
from strawberry_django_aggregates.types import (
|
|
88
|
+
BigInt,
|
|
89
|
+
BucketRange,
|
|
90
|
+
make_aggregate_type,
|
|
91
|
+
make_group_by_spec,
|
|
92
|
+
make_grouped_connection_type,
|
|
93
|
+
make_grouped_type,
|
|
94
|
+
make_having_input,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
__version__ = "1.0.0"
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
# Builder (high-level)
|
|
101
|
+
"AggregateBuilder",
|
|
102
|
+
# Type generators
|
|
103
|
+
"make_aggregate_type",
|
|
104
|
+
"make_grouped_type",
|
|
105
|
+
"make_grouped_connection_type",
|
|
106
|
+
"make_having_input",
|
|
107
|
+
"make_group_by_spec",
|
|
108
|
+
# Backend primitive
|
|
109
|
+
"compute_aggregation",
|
|
110
|
+
# Vocabularies
|
|
111
|
+
"AggregateOp",
|
|
112
|
+
"TimeGranularity",
|
|
113
|
+
"NumberGranularity",
|
|
114
|
+
"validate_week_start",
|
|
115
|
+
"default_operators_for",
|
|
116
|
+
# Custom scalars
|
|
117
|
+
"BigInt",
|
|
118
|
+
# Bucket range — half-open [from, to) interval for TIME-granularity
|
|
119
|
+
# group-by buckets (SPEC § 7 / Stream 5).
|
|
120
|
+
"BucketRange",
|
|
121
|
+
"bucket_range",
|
|
122
|
+
# Empty-bucket filling — dense bucket spine over date-bucketed
|
|
123
|
+
# results (SPEC § 7.2 / Stream 7).
|
|
124
|
+
"fill_bucket_results",
|
|
125
|
+
"generate_bucket_spine",
|
|
126
|
+
# Cursor pagination — opaque base64 / JSON encoding over the
|
|
127
|
+
# canonical-order group-by alias values (SPEC § 4 / Stream 11).
|
|
128
|
+
"encode_group_cursor",
|
|
129
|
+
"decode_group_cursor",
|
|
130
|
+
# Cross-relation aggregate field — attach
|
|
131
|
+
# ``<relation>Aggregate(filter: ...)`` to a strawberry-django
|
|
132
|
+
# parent type (SPEC § 4.2 / Stream 9).
|
|
133
|
+
"register_relation_aggregate",
|
|
134
|
+
# Ordering
|
|
135
|
+
"parse_aggregate_order",
|
|
136
|
+
"comodel_ordering_terms",
|
|
137
|
+
# Errors
|
|
138
|
+
"AggregateError",
|
|
139
|
+
"OperatorNotSupportedError",
|
|
140
|
+
"OrderFieldNotAllowed",
|
|
141
|
+
"AggregationAcrossRelationError",
|
|
142
|
+
"HavingFieldNotAllowed",
|
|
143
|
+
"GroupByFieldNotAllowed",
|
|
144
|
+
"GranularityNotApplicable",
|
|
145
|
+
"JSONPathNotAllowed",
|
|
146
|
+
]
|