Defining Metrics

Learn how to define metrics in Djing.

Djing metrics allow you to quickly gain insight on key business indicators for your application. For example, you may define a metric to display the total number of users added to your application per day, or the amount of weekly sales for a given product.

Djing offers several types of built-in metrics: value, table, partition, and progress. We’ll examine each type of metric and demonstrate their usage below.

Value Metrics

Value metrics display a single value and, if desired, its change compared to a previous time interval. For example, a value metric might display the total number of users created in the last thirty days compared with the previous thirty days:

Value metrics may be generated using the djing:value Artisan command. By default, all new metrics will be placed in the djing_admin/app/Djing/Metrics directory:

commander djing:value NewUsers
djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Post)

    def ranges(self):
        return {
            30: "30 Days",
            60: "60 Days",
            365: "365 Days",
            "TODAY": "Today",
            "MTD": "Month To Date",
            "QTD": "Quarter To Date",
            "YTD": "Year To Date",
        }

Value Query Types

Value metrics don’t only ship with a count helper. You may also use a variety of other aggregate functions when building your metric. Let’s explore each of them now.

— Average

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.average(request, Post)
    
    # ...

— Sum

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.sum(request, Post)
    
    # ...

— Max

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.max(request, Post)
    
    # ...

— Min

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.min(request, Post)
    
    # ...

Value Ranges

Every value metric class contains a ranges method. This method determines the ranges that will be available in the value metric’s range selection menu. The dict’s keys determine the number of days that should be included in the query, while the values determine the “human readable” text that will be placed in the range selection menu. Of course, you are not required to define any ranges at all:

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Post)

    def ranges(self):
        return {
            30: "30 Days",
            60: "60 Days",
            365: "365 Days",
            "TODAY": "Today",
            "YESTERDAY": "Yesterday",
            "THIS_WEEK": "This Week",
            "MTD": "Month To Date",
            "QTD": "Quarter To Date",
            "YTD": "Year To Date",
        }

Zero Result Values

By default, Djing will handle results of 0 as a result containing no data. This may not always be correct, which is why you can use the allow_zero_result method to indicate that 0 is a valid value result:

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Post).allow_zero_results()
    
    # ...

Formatting the Value

You can add a prefix and / or suffix to the Value metric’s result by invoking the prefix and suffix methods when returning the ValueResult instance:

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Post)
                .prefix("$")
                .suffix("per unit")
    
    # ...

You may also use the currency method to specify that a given value result represents a currency value:

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Post).currency()
    
    # ...

By default, the currency symbol will be $, but you may also specify your own currency symbol by passing the symbol as an argument to the currency method:

djing_admin/app/Djing/Metrics/ValueMetric.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Value import Value
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class ValueMetric(Value):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Post).currency("£")
    
    # ...

Progress Metric

Progress metrics display current progress against a target value within a bar chart. For example, a progress metric might display the number of users registered for the given month compared to a target goal:

Progress metrics may be generated using the djing:progress Artisan command. By default, all new metrics will be placed in the djing_admin/app/Djing/Metrics directory:

commander djing:progress NewPosts

Once your progress metric class has been generated, you’re ready to customize it. Each progress metric class contains a calculate method. This method should return a Djing.Metrics.ProgressResult object. Don’t worry, Djing ships with a variety of helpers for quickly generating results.

In this example, we are using the count helper to determine if we have reached our new post creation goal for the month. The count helper will automatically perform a count query against the specified Django model:

djing_admin/app/Djing/Metrics/NewPosts.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Progress import Progress
from djing.core.Http.Requests.DjingRequest import DjingRequest
from posts.models import Post

class NewPosts(Progress):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        def progress_callback(query: QuerySet) -> QuerySet:
            return query

        return self.count(request, Post, progress_callback, target=100)

    def ranges(self):
        return {
            30: "30 Days",
            60: "60 Days",
            365: "365 Days",
            "TODAY": "Today",
            "MTD": "Month To Date",
            "QTD": "Quarter To Date",
            "YTD": "Year To Date",
        }

Sum

Progress metrics don’t only ship with a count helper. You may also use the sum aggregate method when building your metric. For example, the following call to the sum method will display a progress metric with the sum of the completed transaction amounts against a target sales goal:

djing_admin/app/Djing/Metrics/NewPosts.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Progress import Progress
from djing.core.Http.Requests.DjingRequest import DjingRequest
from transactions.models import Transaction

class NewPosts(Progress):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        def progress_callback(query: QuerySet) -> QuerySet:
            return query.where(completed=True)

        return self.sum(
            request, Transaction, progress_callback, column='amount', target=2000
        )

    # ...

Formatting the Progress value

djing_admin/app/Djing/Metrics/NewPosts.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Progress import Progress
from djing.core.Http.Requests.DjingRequest import DjingRequest
from transactions.models import Transaction

class NewPosts(Progress):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        def progress_callback(query: QuerySet) -> QuerySet:
            return query.where(completed=True)

        return self
                .sum(
                    request,
                    Transaction,
                    progress_callback,
                    column='amount',
                    target=2000,
                )
                .prefix("$")

    # ...

Partition Metrics

Partition metrics displays a pie chart of values. For example, a partition metric might display the total number of users for each billing plan offered by your application:

Partition metrics may be generated using the djing:partition Artisan command. By default, all new metrics will be placed in the djing_admin/app/Djing/Metrics directory:

commander djing:partition UsersPerPlan

Once your partition metric class has been generated, you’re ready to customize it. Each partition metric class contains a calculate method. This method should return a Djing.Metrics.PartitionResult object. Don’t worry, Djing ships with a variety of helpers for quickly generating results.

In this example, we are using the count helper, which will automatically perform a count query against the specified Eloquent model and retrieve the number of models belonging to each distinct value of your specified “group by” column:

djing_admin/app/Djing/Metrics/UsersPerPlan.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Partition import Partition
from djing.core.Http.Requests.DjingRequest import DjingRequest
from products.models import Product


class UsersPerPlan(Partition):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.count(request, Product, group_by_column="price")

    def uri_key(self):
        return "users-per-plan"

Partition Query Types

Partition metrics don’t only ship with a count helper. You may also use a variety of other aggregate functions when building your metric.

Average

The average method may be used to calculate the average of a given column within distinct groups. For example, the following call to the average method will display a pie chart with the average order price for each department of the company:

djing_admin/app/Djing/Metrics/UsersPerPlan.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Partition import Partition
from djing.core.Http.Requests.DjingRequest import DjingRequest
from products.models import Product


class UsersPerPlan(Partition):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.average(request, Product, group_by_column="price")
    
    # ...

Sum

The sum method may be used to calculate the sum of a given column within distinct groups. For example, the following call to the sum method will display a pie chart with the sum of all order prices for each department of the company:

djing_admin/app/Djing/Metrics/UsersPerPlan.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Partition import Partition
from djing.core.Http.Requests.DjingRequest import DjingRequest
from products.models import Product


class UsersPerPlan(Partition):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.sum(request, Product, group_by_column="price")
    
    # ...

Max

The max method may be used to calculate the max of a given column within distinct groups. For example, the following call to the max method will display a pie chart with the maximum order price for each department of the company:

djing_admin/app/Djing/Metrics/UsersPerPlan.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Partition import Partition
from djing.core.Http.Requests.DjingRequest import DjingRequest
from products.models import Product


class UsersPerPlan(Partition):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.average(request, Product, group_by_column="price")
    
    # ...

Min

The min method may be used to calculate the min of a given column within distinct groups. For example, the following call to the min method will display a pie chart with the minimum order price for each department of the company:

djing_admin/app/Djing/Metrics/UsersPerPlan.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.Partition import Partition
from djing.core.Http.Requests.DjingRequest import DjingRequest
from products.models import Product


class UsersPerPlan(Partition):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return self.average(request, Product, group_by_column="price")
    
    # ...

Table Metrics

Table metrics allow you to display custom lists of links along with a list of actions, as well as an optional icon.

Table metrics may be generated using the djing:table Artisan command. By default, all new metrics will be placed in the djing_admin/app/Djing/Metrics directory:

commander djing:table NewReleases

Once your table metric class has been generated, you’re ready to customize it. Each table metric class contains a calculate method. This method should return an array of Djing.Metrics.MetricTableRow objects. Each metric row allows you to specify a title and subtitle, which will be displayed stacked on the row:

djing_admin/app/Djing/Metrics/NewReleases.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.MetricTableRow import MetricTableRow
from djing.core.Metrics.Table import Table
from djing.core.Http.Requests.DjingRequest import DjingRequest


class NewReleases(Table):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return [
            MetricTableRow.make()
                .title("v1.0")
                .subtitle("Initial release of Djing Admin"),
                
            MetricTableRow.make()
                .title("v2.0")
                .subtitle("The second major series of Djing Admin"),
        ]

Adding Actions to Table Rows

While table metrics are great for showing progress, documentation links, or recent entries to your models, they become even more powerful by attaching actions to them.

You can use the actions method to return an array of Djing.Menu.MenuItem instances, which will be displayed in a dropdown menu:

djing_admin/app/Djing/Metrics/NewReleases.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.MetricTableRow import MetricTableRow
from djing.core.Metrics.Table import Table
from djing.core.Http.Requests.DjingRequest import DjingRequest


class NewReleases(Table):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return [
            MetricTableRow.make()
                .title("v1.0")
                .subtitle("Initial release of Djing Admin")
                .action(lambda: [
                     MenuItem::externalLink('View release notes', '/releases/1.0'),
                ]),
                
            MetricTableRow.make()
                .title("v2.0")
                .subtitle("The second major series of Djing Admin")
                .action(lambda: [
                     MenuItem::externalLink('View release notes', '/releases/2.0'),
                ]),
        ]

Displaying Icons on Table Rows

Table metrics also support displaying an icon to the left of the title and subtitle for each row. You can use this information to visually delineate different table rows by type, or by using them to show progress on an internal process.

To show an icon on your table metric row, use the icon method and pass in the key for the icon you wish to use:

djing_admin/app/Djing/Metrics/NewReleases.py
from typing import Optional
from django.db.models import Model
from djing.core.Metrics.MetricTableRow import MetricTableRow
from djing.core.Metrics.Table import Table
from djing.core.Http.Requests.DjingRequest import DjingRequest


class NewReleases(Table):
    def calculate(self, request: DjingRequest, model: Optional[Model] = None):
        return [
            MetricTableRow.make()
                .icon("check-circle")
                .icon_class("text-green-500")
                .title("v1.0")
                .subtitle("Initial release of Djing Admin")
                .action(lambda: [
                     MenuItem::externalLink('View release notes', '/releases/1.0'),
                ]),
                
            MetricTableRow.make()
                .icon("check-circle")
                .icon_class("text-green-500")
                .title("v2.0")
                .subtitle("The second major series of Djing Admin")
                .action(lambda: [
                     MenuItem::externalLink('View release notes', '/releases/2.0'),
                ]),
        ]

Last updated