{"id":37067054,"url":"https://github.com/kit-oz/drf-aggregation","last_synced_at":"2026-01-14T07:52:23.378Z","repository":{"id":56190509,"uuid":"312798400","full_name":"kit-oz/drf-aggregation","owner":"kit-oz","description":"Get DB aggregations using Django ORM","archived":false,"fork":false,"pushed_at":"2025-06-05T05:11:24.000Z","size":109,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-24T23:36:17.392Z","etag":null,"topics":["aggregation","django","django-orm","django-rest-framework"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kit-oz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-14T10:58:27.000Z","updated_at":"2025-06-05T05:06:15.000Z","dependencies_parsed_at":"2024-10-18T21:27:17.557Z","dependency_job_id":null,"html_url":"https://github.com/kit-oz/drf-aggregation","commit_stats":{"total_commits":44,"total_committers":3,"mean_commits":"14.666666666666666","dds":0.25,"last_synced_commit":"5399bb5d1924ee52c20f99bb4d50cc882adc55ff"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/kit-oz/drf-aggregation","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kit-oz%2Fdrf-aggregation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kit-oz%2Fdrf-aggregation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kit-oz%2Fdrf-aggregation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kit-oz%2Fdrf-aggregation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kit-oz","download_url":"https://codeload.github.com/kit-oz/drf-aggregation/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kit-oz%2Fdrf-aggregation/sbom","scorecard":{"id":561727,"data":{"date":"2025-08-11","repo":{"name":"github.com/kit-oz/drf-aggregation","commit":"0bddbd6ead909334f316ff6cb509f3b2a9539098"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4,"checks":[{"name":"Maintained","score":0,"reason":"1 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":10,"reason":"GitHub workflow tokens follow principle of least privilege","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/python-publish.yml:16","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/kit-oz/drf-aggregation/python-publish.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/python-publish.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/kit-oz/drf-aggregation/python-publish.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:31","Warn: pipCommand not pinned by hash: .github/workflows/python-publish.yml:32","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   1 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   2 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"CII-Best-Practices","score":2,"reason":"badge detected: InProgress","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/python-publish.yml:19"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Vulnerabilities","score":0,"reason":"18 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2020-32 / GHSA-2m34-jcjv-45xf","Warn: Project is vulnerable to: PYSEC-2020-36 / GHSA-3gh2-xw74-jmcw","Warn: Project is vulnerable to: PYSEC-2021-98 / GHSA-68w8-qjq3-2gfm","Warn: Project is vulnerable to: GHSA-7xr5-9hcq-chf9","Warn: Project is vulnerable to: GHSA-8x94-hmjh-97hq","Warn: Project is vulnerable to: PYSEC-2020-34 / GHSA-fr28-569j-53c4","Warn: Project is vulnerable to: PYSEC-2021-9 / GHSA-fvgf-6h6h-3322","Warn: Project is vulnerable to: PYSEC-2020-35 / GHSA-hmr4-m2h5-33qx","Warn: Project is vulnerable to: PYSEC-2020-33 / GHSA-m6gj-h9gm-gw44","Warn: Project is vulnerable to: PYSEC-2021-99 / GHSA-p99v-5w3c-jqq9","Warn: Project is vulnerable to: GHSA-rrqc-c2jx-6jgv","Warn: Project is vulnerable to: GHSA-rxjp-mfm9-w4wr","Warn: Project is vulnerable to: GHSA-v6rh-hp5x-86rv","Warn: Project is vulnerable to: GHSA-vfq6-hq5r-27r6","Warn: Project is vulnerable to: PYSEC-2020-31 / GHSA-wpjr-j57x-wxfw","Warn: Project is vulnerable to: PYSEC-2021-6 / GHSA-xgxc-v2qg-chmh","Warn: Project is vulnerable to: GHSA-xpfp-f569-q3p2","Warn: Project is vulnerable to: GHSA-gw84-84pc-xp82"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T13:54:36.538Z","repository_id":56190509,"created_at":"2025-08-20T13:54:36.539Z","updated_at":"2025-08-20T13:54:36.539Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28413512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T05:26:33.345Z","status":"ssl_error","status_checked_at":"2026-01-14T05:21:57.251Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aggregation","django","django-orm","django-rest-framework"],"created_at":"2026-01-14T07:52:22.773Z","updated_at":"2026-01-14T07:52:23.370Z","avatar_url":"https://github.com/kit-oz.png","language":"Python","readme":"# Django Aggregation\n\n[![Published on Django Packages](https://img.shields.io/badge/Published%20on-Django%20Packages-0c3c26)](https://djangopackages.org/packages/p/drf-aggregation/)\n[![PyPI version](https://badge.fury.io/py/drf-aggregation.svg)](https://badge.fury.io/py/drf-aggregation)\n[![codecov badge](https://codecov.io/gh/kit-oz/drf-aggregation/branch/main/graph/badge.svg?token=X1RWDJI9NG)](https://codecov.io/gh/kit-oz/drf-aggregation)\n[![Python Versions](https://img.shields.io/pypi/pyversions/drf-aggregation.svg)](https://pypi.org/project/drf-aggregation/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nGet DB aggregations using Django ORM for efficient data analysis and reporting.\nThis repository provides tools to perform complex aggregations with ease, leveraging the power of Django's ORM.\n\n## Key Features\n\n- Perform multiple aggregations simultaneously\n- Calculate percentiles (PostgreSQL) and percentages with minimal additional setup\n- Group data by multiple fields\n- Generate time series data (PostgreSQL, MySQL)\n- Flexible result limiting and pagination\n- Custom aggregation types support\n\n## Installation\n\n```bash\npip install drf-aggregation\n```\n\n## Usage\n\n### With DRF\n\nFirst, add the `AggregationMixin` to your viewset:\n\n```python\nfrom drf_aggregation import AggregationMixin\nfrom rest_framework.viewsets import GenericViewSet\n\nclass TicketViewSet(AggregationMixin, GenericViewSet):\n    queryset = Ticket.objects.all()\n    serializer_class = TicketSerializer\n```\n\nThen, register the viewset with your router:\n\n```python\nurlpatterns = [\n    path(\"aggregation/ticket\", TicketViewSet.as_view({\"post\": \"aggregation\"})),\n]\n```\n\nOnce set up, you can make requests like the following:\n\n```http\nPOST /aggregation/ticket\nContent-Type: application/json\n{\n    \"group_by\": \"service\",\n    \"limit\": 5,\n    \"order_by\": \"-total_tasks\",\n    \"aggregations\": {\n        \"total_tasks\": {\n            \"type\": \"count\"\n        },\n        \"average_execution_time\": {\n            \"type\": \"average\",\n            \"field\": \"execution_time\"\n        }\n    }\n}\n```\n\n### Direct Usage in Code\n\nYou can also use the aggregation function directly:\n\n```python\nfrom drf_aggregation import get_aggregations\n\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    }\n)\n```\n\n## Parameters\n\n- **aggregations**: A dictionary specifying the aggregations to perform.\n  - **key**: The name under which the aggregation result will be returned.\n  - **value**: A dictionary with aggregation settings.\n    - **type**: The type of aggregation (e.g., count, sum).\n    - **index_by_group**: Index for sorting by a specific field.\n    - **field**: Required for sum, average, minimum, maximum, percentile.\n    - **percentile**: A value from 0 to 1, required for percentile calculations.\n    - **additional_filter**: Uses filter parser from [drf-complex-filter](https://github.com/kit-oz/drf-complex-filter), required for percent.\n\n- **group_by**: List of fields to group the results by.\n- **order_by**: List of fields to sort the results.\n- **limit**: Number of groups to return or a dictionary with settings:\n  - **limit**: Number of groups to return.\n  - **offset**: Offset for the start of returned groups.\n  - **by_group**: Field to limit the result by, defaults to the first grouping field.\n  - **by_aggregation**: Aggregation to limit the result by, defaults to the first declared aggregation.\n  - **show_other**: Return remaining records as an additional group.\n  - **other_label**: Label for the additional group.\n\n## Supported Field Types\n\n- `IntegerField`\n- `FloatField`\n- `DateField` (min/max only)\n- `DateTimeField` (min/max only)\n- `DurationField`\n\n## Extending Aggregation Types\n\nBy default, the following aggregations are enabled: `count`, `distinct`, `sum`, `average`, `minimum`, `maximum`.\n\nTo enable additional aggregations like percent and percentile, modify your `settings.py`:\n\n```python\n# in settings.py\nDRF_AGGREGATION_SETTINGS = {\n    \"AGGREGATION_CLASSES\": [\n        \"drf_aggregation.aggregations.common.CommonAggregations\",\n        # Requires additional package \"drf-complex-filter\"\n        \"drf_aggregation.aggregations.percent.PercentAggregation\",\n        # Works only on PostgreSQL\n        \"drf_aggregation.aggregations.percentile.PercentileAggregation\",\n    ],\n}\n```\n\n### Custom Aggregations\n\nCreate a class with static methods for custom aggregation types:\n\n```python\nclass MyAggregations:\n    @staticmethod\n    def my_aggregation(aggregation, queryset):\n        name = aggregation.get(\"name\")\n        return {f\"{name}\": models.Count(\"id\")}\n\n# in settings.py\nDRF_AGGREGATION_SETTINGS = {\n    \"AGGREGATION_CLASSES\": [\n        \"drf_aggregation.aggregations.common.CommonAggregations\",\n        \"path.to.MyAggregations\",\n    ],\n}\n\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"value\": {\n            \"type\": \"my_aggregation\"\n        }\n    }\n)\n```\n\n## Usage Examples\n\n### Grouping Results\n\nGroup results by a list of fields:\n\n```python\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    },\n    group_by=[\"field1\", \"field2\"]\n)\n```\n\n### Sorting Results\n\nSort results by specified fields:\n\n```python\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    },\n    group_by=\"field1\",\n    order_by=\"field1\"\n)\n```\n\nUse aggregations as sorting keys:\n\n```python\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    },\n    group_by=\"field1\",\n    order_by=\"-total_tasks\"\n)\n```\n\n### Limiting Displayed Groups\n\nLimit the number of displayed groups:\n\n```python\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    },\n    group_by=\"field1\",\n    order_by=\"-total_tasks\",\n    limit=2\n)\n```\n\nDisplay remaining groups as an additional category:\n\n```python\nresult = get_aggregations(\n    queryset=Ticket.objects.all(),\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    },\n    group_by=\"field1\",\n    order_by=\"-total_tasks\",\n    limit={\n        \"limit\": 2,\n        \"show_other\": true\n    }\n)\n```\n\n## Time Series\n\n**Note**: Time series aggregations are not supported on SQLite.\n\nTo perform time series aggregations, annotate your queryset with a truncated date field:\n\n```python\ntruncate_rules = { \"created_at\": \"day\" }\nqueryset = truncate_date(Ticket.objects.all(), truncate_rules)\n\nresult = get_aggregations(\n    queryset=queryset,\n    aggregations={\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    },\n    group_by=\"created_at__trunc__day\"\n)\n```\n\nUsing `AggregationMixin`, pass `truncate_rules` in the request body:\n\n```http\nPOST /aggregation/ticket\nContent-Type: application/json\n{\n    \"truncate_rules\": { \"created_at\": \"day\" },\n    \"group_by\": \"created_at__trunc__day\",\n    \"aggregations\": {\n        \"total_tasks\": {\n            \"type\": \"count\"\n        }\n    }\n}\n```\n\nAvailable truncation periods: `year`, `quarter`, `month`, `week`, `day`, `hour`, `minute`, `second`\n\nFor more details on date truncation, see the [Django documentation](https://docs.djangoproject.com/en/3.1/ref/models/database-functions/#trunc).\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkit-oz%2Fdrf-aggregation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkit-oz%2Fdrf-aggregation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkit-oz%2Fdrf-aggregation/lists"}