Django Insights

Time series

Use a time series for how something changes: per day, week or month. It is a line chart, with the data in a table behind it.

from django.db.models import Sum
from django.db.models.functions import TruncMonth

from django_insights.metrics import metrics
from django_insights.metrics_types import TimeSeriesResult
from shop.models import Order


@metrics.timeseries(
    question="What is the revenue per month?",
    xlabel="Month",
    xformat="%Y-%m",
    ylabel="Revenue in USD",
)
def revenue_per_month() -> TimeSeriesResult:
    return (
        Order.objects.annotate(month=TruncMonth("created_at"))
        .values("month")
        .annotate(total=Sum("total"))
        .values_list("month", "total")
        .order_by("month")
    )

Rows #

Each row is (timestamp, value), with a datetime. Order by the timestamp: a line chart draws the rows in the order you return them.

  • A time series has at most one value per timestamp. Group in the query, with TruncDay, TruncWeek or TruncMonth, so you do not return two rows for the same moment. Otherwise collecting fails and names the insight.
  • Periods without data are not in the query result, so they are missing from the line. That is fine for most questions.
  • xformat only changes how the timestamp reads in the data table.

Other periods #

from datetime import timedelta

from django.db.models import Count
from django.db.models.functions import TruncDay
from django.utils import timezone


@metrics.timeseries(
    question="How many orders are placed per day?",
    desc="The last 90 days",
    xlabel="Day",
    xformat="%Y-%m-%d",
    ylabel="Orders",
)
def orders_per_day() -> TimeSeriesResult:
    since = timezone.now() - timedelta(days=90)
    return (
        Order.objects.filter(created_at__gte=since)
        .annotate(day=TruncDay("created_at"))
        .values("day")
        .annotate(total=Count("pk"))
        .values_list("day", "total")
        .order_by("day")
    )