Skip to content

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