.. _time_series: Time Series Reports =================== A Time series report is a report that is generated for a periods of time. The period can be daily, weekly, monthly, yearly or custom, calculations will be performed for each period in the time series. General use case ---------------- Here is a quick look at the general use case .. code-block:: python from django.utils.translation import gettext_lazy as _ from django.db.models import Sum from slick_reporting.views import ReportView class TimeSeriesReport(ReportView): report_model = SalesTransaction group_by = "client" time_series_pattern = "monthly" # options are: "daily", "weekly", "bi-weekly", "monthly", "quarterly", "semiannually", "annually" and "custom" date_field = "date" # These columns will be calculated for each period in the time series. time_series_columns = [ ComputationField.create(Sum, "value", verbose_name=_("Sales For Month")), ] columns = [ "name", "__time_series__", # This is the same as the time_series_columns, but this one will be on the whole set ComputationField.create(Sum, "value", verbose_name=_("Total Sales")), ] chart_settings = [ Chart( "Client Sales", Chart.BAR, data_source=["sum__value"], title_source=["name"], ), Chart( "Total Sales Monthly", Chart.PIE, data_source=["sum__value"], title_source=["name"], plot_total=True, ), Chart( "Total Sales [Area chart]", Chart.AREA, data_source=["sum__value"], title_source=["name"], ), ] Allowing the User to Choose the time series pattern --------------------------------------------------- You can allow the User to Set the Pattern for the report , Let's create another version of the above report where the user can choose the pattern .. code-block:: python class TimeSeriesReportWithSelector(TimeSeriesReport): report_title = _("Time Series Report With Pattern Selector") time_series_selector = True time_series_selector_choices = ( ("daily", _("Daily")), ("weekly", _("Weekly")), ("bi-weekly", _("Bi-Weekly")), ("monthly", _("Monthly")), ) time_series_selector_default = "bi-weekly" time_series_selector_label = _("Period Pattern") # The label for the time series selector time_series_selector_allow_empty = True # Allow the user to select an empty time series, in which case no time series will be applied to the report. Set Custom Dates for the Time Series ------------------------------------ You might want to set irregular pattern for the time series, Like first 10 days of each month , or the 3 summer month of every year. Let's see how you can do that, inheriting from teh same Time series we did first. .. code-block:: python def get_current_year(): return datetime.datetime.now().year class TimeSeriesReportWithCustomDates(TimeSeriesReport): report_title = _("Time Series Report With Custom Dates") time_series_pattern = "custom" time_series_custom_dates = ( ( datetime.datetime(get_current_year(), 1, 1), datetime.datetime(get_current_year(), 1, 10), ), ( datetime.datetime(get_current_year(), 2, 1), datetime.datetime(get_current_year(), 2, 10), ), ( datetime.datetime(get_current_year(), 3, 1), datetime.datetime(get_current_year(), 3, 10), ), ) Customize the Computation Field label ------------------------------------- Maybe you want to customize how the title of the time series computation field. For this you want to Subclass ``ComputationField``, where you can customize how the title is created and use it in the time_series_column instead of the one created on the fly. Example: .. code-block:: python class SumOfFieldValue(ComputationField): # A custom computation Field identical to the one created like this # Similar to `ComputationField.create(Sum, "value", verbose_name=_("Total Sales"))` calculation_method = Sum calculation_field = "value" name = "sum_of_value" @classmethod def get_time_series_field_verbose_name(cls, date_period, index, dates, pattern): # date_period: is a tuple (start_date, end_date) # index is the index of the current pattern in the patterns on the report # dates: the whole dates we have on the reports # pattern it's the pattern name, ex: monthly, daily, custom return f"First 10 days sales {date_period[0].month}-{date_period[0].year}" class TimeSeriesReportWithCustomDatesAndCustomTitle(TimeSeriesReportWithCustomDates): report_title = _("Time Series Report With Custom Dates and custom Title") time_series_columns = [ SumOfFieldValue, # Use our newly created ComputationField with the custom time series verbose name ] chart_settings = [ Chart( "Client Sales", Chart.BAR, data_source=[ "sum_of_value" ], # Note: This is the name of our `TotalSalesField` `field title_source=["name"], ), Chart( "Total Sales [Pie]", Chart.PIE, data_source=["sum_of_value"], title_source=["name"], plot_total=True, ), ] Time Series without a group by ------------------------------ Maybe you want to get the time series calculated on the whole set, without grouping by anything. You can do that by omitting the `group_by` attribute, and having only time series (or other computation fields) columns. Example: .. code-block:: python class TimeSeriesWithoutGroupBy(ReportView): report_title = _("Time Series without a group by") report_model = SalesTransaction time_series_pattern = "monthly" date_field = "date" time_series_columns = [ ComputationField.create(Sum, "value", verbose_name=_("Sales For ")), ] columns = [ "__time_series__", ComputationField.create(Sum, "value", verbose_name=_("Total Sales")), ] chart_settings = [ Chart( "Total Sales [Bar]", Chart.BAR, data_source=["sum__value"], title_source=["name"], ), Chart( "Total Sales [Pie]", Chart.PIE, data_source=["sum__value"], title_source=["name"], ), ] .. _time_series_options: Time Series Options ------------------- .. attribute:: ReportView.time_series_pattern the time series pattern to be used in the report, it can be one of the following: Possible options are: daily, weekly, semimonthly, monthly, quarterly, semiannually, annually and custom. if `custom` is set, you'd need to override `time_series_custom_dates` .. attribute:: ReportView.time_series_custom_dates A list of tuples of (start_date, end_date) pairs indicating the start and end of each period. .. attribute:: ReportView.time_series_columns a list of Calculation Field names which will be included in the series calculation. .. code-block:: python class MyReport(ReportView): time_series_columns = [ ComputationField.create( Sum, "value", verbose_name=_("Value"), is_summable=True, name="sum__value" ), ComputationField.create( Avg, "Price", verbose_name=_("Avg Price"), is_summable=False ), ] Links to demo '''''''''''''' Time series Selector pattern `Demo `_ and the `Code on github `_ for it.