Admin - Django admin extensions

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