A django package for creating simple stats from a query
Project description
django-simple-stats
A django package for creating stats from a query.
installation
Install it from pip:
pip install django-simple-stats
or the latest version from git:
pip install git+https://github.com/spapas/django-simple-stats
No other installation is needed.
Usage
The only supported method is the get_stats. It expects a django query and a configuration list. Each element of the configuration list is a dictionary with the following attributes:
label (required): The textual description of this statistic
kind (required): What kind of aggregate we need. Choices are: query_aggregate_single, query_aggregate, choice_aggregate, choice_aggregate_with_null, query_aggregate_date, query_aggregate_buckets.
method (required): The aggregate method. Can be one of count, sum, max, min, avg.
field (required): The field that the aggreate will run on; use __ for joins i.e fiedld1__field2
what (optional): Only required for query_aggregate_date, it is eithed year, month, day
choices (optional): Only required for choice_aggregate and choice_aggregate_with_null, it must be a django choices list
buckets (optional): only required for query_aggregate_buckets. Must be a list from the biggest to the lowest value.
aggr_field (optional): this field is optional and can be used for query_aggregate, query_aggregate_date, choice_aggregate and choice_aggregate_with_null. It denotes a field that would run the aggregate function on.
See below for a complete example.
The response will be a list of dictionaries with the following attributes:
label: Same as the label in the configuration
value: Will have a value if you use the query_aggregate_single, else will be None
values: Will be empty for query_aggregate_single else will be a list of tuples. Each tuple will have two elements, (label, value)
Stat kinds
The query_aggregate_single will run the aggregate function on a field and return a single value. For example you can get the total number of rows of your query or the sum of all fields.
The query_aggregate will run the aggregate function on a field and return the list of values. You can run the aggregate function on a different field by passing aggr_field (so you can group by a field and return the sum of another field for each group). This is mainly useful for foreign keys and if you’ve got distinct values in your queries. For example count the number of rows per user. Also it is useful for booleans for example to get the number of rows that have a flag turned on and off.
The choice_aggregate is similar to the query_aggregate but will use a choices attribute to return better looking values. This will not return Null values
The choice_aggregate_with_null is the same as choice_aggregate but will return Null values (so you can add a (None, "Empty") choice to your choices)
The query_aggregate_date is similar to the query_aggregate but will return the aggregates on a specific date field; use what to pass year, month, day.
Finally, the query_aggregate_buckets is used to create buckets of values. You’ll pass the list of buckets and the query will return the results that belong in each bucket. The stats module will run individual queries with field__gte for each value. So for example if you pass [100, 50, 10] and you have a field price it will run price__gte=100, price__gte=50, price__gte=10 and return the results.
Example
from simple_stats import get_stats
STATS_CFG = cfg = [
{
'label': 'Total',
'kind': 'query_aggregate_single',
'method': 'count',
'field': 'id',
},
{
'label': 'Total price',
'kind': 'query_aggregate_single',
'method': 'sum',
'field': 'price',
},
{
'label': 'Per authority',
'kind': 'query_aggregate',
'method': 'count',
'field': 'pilot_authority__name',
},
{
'label': 'Per authority by price',
'kind': 'query_aggregate',
'method': 'count',
'field': 'pilot_authority__name',
'aggr_field': 'price',
},
{
'label': 'Per status',
'kind': 'choice_aggregate',
'method': 'count',
'field': 'status',
'choices': models.STATUS_CHOICES,
},
{
'label': 'Per status by price',
'kind': 'choice_aggregate',
'method': 'count',
'field': 'status',
'aggr_field': 'price',
'choices': models.STATUS_CHOICES,
},
{
'label': 'Per year',
'kind': 'query_aggregate_date',
'method': 'count',
'field': 'created_on',
'what': 'year',
},
{
'label': 'Per year by price',
'kind': 'query_aggregate_date',
'method': 'count',
'field': 'created_on',
'what': 'year',
'aggr_field': 'price',
},
{
'label': 'Per price',
'kind': 'query_aggregate_buckets',
'method': 'count',
'field': 'price',
'buckets': [100_00, 50_00, 1_000, 500, 0]
}
]
def my_view(request):
qs = TestModel.objects.all()
stats = get_stats(qs, STATS_CFG)
return render(request, 'my_template.html', {'stats': stats})
The stats will be an array of dictionaries like the following:
[
{'label': 'Total', 'values': [], 'value': 1216},
{'label': 'Total price', 'values': [], 'value': 323.16},
{'label': 'Per authority', 'values': [('Authority 1', 200), ('Authority 2', 9), ], 'value': None},
{'label': 'Per authority by price', 'values': [('Authority 1', 123.23), ('Authority 2', 42.12), ], 'value': None},
{'label': 'Per status', 'values': [('New', 200), ('Cancel', 0), 'value': None},
{'label': 'Per status by price', 'values': [('New', 32.01), ('Cancel', 44.23), 'value': None},
{'label': 'Per year', 'values': [(2021, 582), (2022, 634)], 'value': None}
{'label': 'Per year by price', 'values': [(2021, 5.82), (2022, 6.34)], 'value': None}
{'label': 'Per price', 'values': [('> 5000', 1), ('> 1000', 29), ('> 500', 86), ('> 0', 305)], 'value': None}
]
You can display this in your template using something like:
<div class='row'>
{% for s in stats %}
<div class='col-md-4 mb-5' style='max-height: 500px; overflow: auto;'>
<h4>{{ s.label }}</h4>
{% if s.values %}
<table class='table table-condensed table-striped small table-sm'>
{% for v in s.values %}
<tr>
<td>{{ v.0 }}</td>
<td>{{ v.1 }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<b>{{ s.value }}</b>
{% endif %}
</div>
{% endfor %}
</div>
Exporting the stats
You can easily export these stats in xls using the xlwt (https://pypi-hypernode.com/project/xlwt/) library and this function:
import xlwt
def create_xls_resp(stats, response):
context = self.get_context_data()
import xlwt
wb = xlwt.Workbook(encoding="utf-8")
for stat in stats:
ws = wb.add_sheet(stat["label"][:31])
ws.write(0,0,stat["label"], xlwt.easyxf('font: name Calibri, bold on', ))
if stat["value"]:
ws.write(0,1,stat["value"], xlwt.easyxf('font: name Calibri, bold on', ))
for i, val in enumerate(stat["values"], start=2):
for j,v in enumerate(val, start=0):
ws.write(i,j,v)
wb.save(response)
Now you can call it like this from your view:
from django.http import HttpResponse
def my_export_view(request):
qs = TestModel.objects.all()
stats = get_stats(qs, STATS_CFG)
response = HttpResponse(content_type="application/ms-excel")
response["Content-Disposition"] = "attachment; filename=export.xls"
create_xls_resp(response)
return response
Changelog
v.0.4.0: Allow the aggregate function to run on a different field using aggr_field
v.0.3.1: Fix small bug with choice_aggregate_with_null
v.0.3.0: Add choice_aggregate_with_null and throw if stat kind is not found
v.0.2.1: Fix small bug with column aliases
v.0.2.0: Changed API; use query_aggregate_datetime for a datetime field and query_aggregate_date for a date field
v.0.1.0: Initial version
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file django-simple-stats-0.4.0.tar.gz
.
File metadata
- Download URL: django-simple-stats-0.4.0.tar.gz
- Upload date:
- Size: 6.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.26.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.43.0 CPython/3.8.1
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | fc3a11de3e3ac61fab4455d265c7ecb031a6625d34c054d0afac853b376ce87e |
|
MD5 | 93188c04ad6d51a25af26c35e13d37d4 |
|
BLAKE2b-256 | bc92f4436e2b42af75b749060f4dadc9b53969db3ccd192e53074072de7b9b65 |