Admin
stat(title=None, legend=(), key=None, y=None, x=None, template_name=None, config={})
Statistics resolver method wrapper. Registers a new statistics block to display and with what inside.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
title |
str |
Block title. Defaults to None. |
None |
legend |
List[str] |
List of field descriptors. Defaults to (). |
() |
key |
str |
Stat block unique key. Defaults to None. |
None |
y |
str |
Y axis key. Defaults to None. |
None |
x |
str |
X axis key. Defaults to None. |
None |
template_name |
str |
Django template name to use for block rendering. Defaults to None. |
None |
config |
dict |
Additional data to configure block. Defaults to {}. |
{} |
Examples:
@register_stats(ExistingModelAdmin, model=ExistingModel)
class ExistingModelStatsAdmin:
@stat(_('Participants per domain'), (
stat_legend('domain', _('Domain')),
stat_legend('participants_count', _('Participants count')),
), config={'type': 'pie', 'labelType': 'percent'})
def get_participants_per_domain(self, request, qs):
return (
qs
.values('domain')
.annotate(participants_count=models.Count(
'participants__participant_id', distinct=True
))
.order_by('domain')
)
By default every stat
must return list of dicts with some valuable
stats data. That will be then transformed using legend and config
into a required chart.
Source code in pxd_admin_extensions/contrib/stats/utils.py
def stat(
title=None, legend=(), key=None, y=None, x=None, template_name=None,
config={}
):
"""Statistics resolver method wrapper.
Registers a new statistics block to display and with what inside.
Args:
title (str, optional): Block title. Defaults to None.
legend (List[str], optional): List of field descriptors.
Defaults to ().
key (str, optional): Stat block unique key. Defaults to None.
y (str, optional): Y axis key. Defaults to None.
x (str, optional): X axis key. Defaults to None.
template_name (str, optional): Django template name to use for block
rendering.
Defaults to None.
config (dict, optional): Additional data to configure block.
Defaults to {}.
Example:
```python
@register_stats(ExistingModelAdmin, model=ExistingModel)
class ExistingModelStatsAdmin:
@stat(_('Participants per domain'), (
stat_legend('domain', _('Domain')),
stat_legend('participants_count', _('Participants count')),
), config={'type': 'pie', 'labelType': 'percent'})
def get_participants_per_domain(self, request, qs):
return (
qs
.values('domain')
.annotate(participants_count=models.Count(
'participants__participant_id', distinct=True
))
.order_by('domain')
)
```
By default every `stat` must return list of dicts with some valuable
stats data. That will be then transformed using legend and config
into a required chart.
"""
assert len(legend) > 1, 'There must be at least 2 parameters to manage.'
assert len(legend) < 4, 'We could match not more than 3 values at a time.'
x = x or legend[0]['key']
y = y or legend[1]['key']
def wrapper(func):
func.title = title
func.template_name = template_name
func.config = {'legend': legend, 'y': y, 'x': x, **config}
func.key = key
func.is_stat = True
return func
return wrapper
stat_legend(key, title=None, **kwargs)
Simplifier for statistics legend definition.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
key |
str |
Legend key. |
required |
title |
str |
Legend's title. Defaults to None. |
None |
Source code in pxd_admin_extensions/contrib/stats/utils.py
def stat_legend(key, title=None, **kwargs):
"""Simplifier for statistics legend definition.
Args:
key (str): Legend key.
title (str, optional): Legend's title. Defaults to None.
"""
return {'key': key, 'title': title or key, **kwargs}
StatsAdmin
Admin basement for stats interface.
changelist_view(self, request, extra_context=None)
The 'change list' admin view for this model.
Source code in pxd_admin_extensions/contrib/stats/admin.py
def changelist_view(self, request, extra_context=None):
response = super().changelist_view(
request, extra_context=extra_context,
)
try:
qs = response.context_data['cl'].queryset
except (AttributeError, KeyError):
return response
response.context_data['stats'] = self.collect_stats(request, qs)
response.context_data['stats_columns'] = list(range(min(
self.stat_columns_amount, len(response.context_data['stats'])
)))
return response
collect_stats(self, request, qs)
Collects all the defined stat callers and executes them.
Source code in pxd_admin_extensions/contrib/stats/admin.py
def collect_stats(self, request, qs):
"""Collects all the defined stat callers and executes them.
"""
attributes = ((key, getattr(self, key)) for key in dir(self))
stat_callables = (
(key, attr) for key, attr in attributes
if callable(attr) and hasattr(attr, 'is_stat') and attr.is_stat
)
key_normalized = (
(
attr.key if hasattr(attr, 'key') and attr.key else
key[key.startswith('get_') and 4 or 0:],
attr
)
for key, attr in stat_callables
)
return [
{
'key': k,
'result': list(attr(request, qs)),
'config': getattr(attr, 'config', None) or {},
'template_name': (
getattr(attr, 'template_name', None) or
self.stat_display_default_template_name
),
'title': getattr(attr, 'title', None) or k,
}
for k, attr in key_normalized
]
get_stats_date_period(self, request, date_hierarchy=None)
Simple method to resolve date truncation basedon the date hierarchy filter.
Source code in pxd_admin_extensions/contrib/stats/admin.py
def get_stats_date_period(self, request, date_hierarchy=None):
"""Simple method to resolve date truncation basedon the date
hierarchy filter.
"""
date_hierarchy = date_hierarchy or self.date_hierarchy
if date_hierarchy + '__day' in request.GET:
return 'minute'
if date_hierarchy + '__month' in request.GET:
return 'day'
if date_hierarchy + '__year' in request.GET:
return 'month'
return 'year'
create_simple_proxy_model(Model, postfix='Proxy', verbose_name=None, verbose_name_plural=None)
Factory to create "empty" proxy model.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
Model |
type |
Base model to inherit from. |
required |
postfix |
str |
Postfix for model to have unique name. Defaults to 'Proxy'. |
'Proxy' |
verbose_name |
str |
Model's verbose name. Defaults to None. |
None |
verbose_name_plural |
str |
Model's plural verbose name. Defaults to None. |
None |
Source code in pxd_admin_extensions/contrib/stats/factories.py
def create_simple_proxy_model(
Model,
postfix: str='Proxy',
verbose_name: str=None,
verbose_name_plural: str=None,
):
"""Factory to create "empty" proxy model.
Args:
Model (type): Base model to inherit from.
postfix (str, optional): Postfix for model to have unique name.
Defaults to 'Proxy'.
verbose_name (str, optional): Model's verbose name. Defaults to None.
verbose_name_plural (str, optional): Model's plural verbose name.
Defaults to None.
"""
v = verbose_name or Model._meta.verbose_name_raw
vp = verbose_name_plural or Model._meta.verbose_name_plural
class Meta:
proxy = True
verbose_name = v
verbose_name_plural = vp
return type(Model.__name__ + (postfix or 'Proxy'), (Model, ), {
'__module__': Model.__module__,
'Meta': Meta
})
register_stats(underlying_admin, registerer=<function register at 0x7f65361ed430>, stats_admin=<class 'pxd_admin_extensions.contrib.stats.admin.StatsAdmin'>, model=None, **kwargs)
Factory to generate admin panel for statistics display.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
underlying_admin |
ModelAdmin |
Current admin class. |
required |
registerer |
Callable |
Admin registerer. Defaults to admin.register. |
<function register at 0x7f65361ed430> |
stats_admin |
type |
Base class with admin statistics imlementation. Defaults to StatsAdmin. |
<class 'pxd_admin_extensions.contrib.stats.admin.StatsAdmin'> |
model |
Model |
Model to add stats for. Defaults to None. |
None |
Source code in pxd_admin_extensions/contrib/stats/factories.py
def register_stats(
underlying_admin, registerer=admin.register, stats_admin=StatsAdmin,
model=None, **kwargs
):
"""Factory to generate admin panel for statistics display.
Args:
underlying_admin (ModelAdmin): Current admin class.
registerer (Callable, optional): Admin registerer.
Defaults to admin.register.
stats_admin (type, optional): Base class with admin statistics
imlementation.
Defaults to StatsAdmin.
model (Model, optional): Model to add stats for. Defaults to None.
"""
def wrapper(Class):
postfix = STATS_POSTFIX
Model = model or underlying_admin.model
ProxyModel = create_simple_proxy_model(Model, postfix=postfix)
Admin = type(
underlying_admin.__name__,
(stats_admin, Class, underlying_admin,),
{
'underlying_admin': underlying_admin,
'underlying_admin': Model,
}
)
underlying_admin.change_list_template = ADMIN_STAT_ALTERNATIVES.get(
underlying_admin.change_list_template,
underlying_admin.change_list_template
)
return registerer(ProxyModel, **kwargs)(Admin)
return wrapper