Report¶
Tip
All projects which use the report
app should provide a URL named
project.report
. This should be the home page for reports.
If you don’t have a natural URL for this, then see Project home
in Snippets below.
Icon¶
<i class="fa fa-table"></i>
New Report¶
To write a new report:
Note
The report must be created in the app folder in reports.py
e.g. example_report/reports.py
.
Create a report based on the ReportMixin
class e.g:
from report.service import ReportMixin
class EnquiryMonthReport(ReportMixin):
REPORT_SLUG = "enquiry-month"
REPORT_TITLE = "Enquiries received"
def run_csv_report(self, csv_writer, parameters=None):
count = 0
csv_writer.writerow(("name", "email", "phone"))
enquiries = Enquiry.objects.all().order_by("created")
for x in enquiries:
count = count + 1
csv_writer.writerow([x.name, x.email, x.phone])
return count
def user_passes_test(self, user):
return user.is_staff
Tip
When naming a report (using the slug
), it might be a good idea to
start with the name of the app e.g. invoice-time-analysis-contact
.
Tip
The user_passes_test
method should return True
if the user has
permission to run the report.
Initialise the report in your app management command e.g. init_app_invoice
:
EnquiryMonthReport().init_report()
Schedule a Report¶
To schedule a simple report (with no parameters) using a view, override the
ReportSpecificationScheduleMixin
e.g:
from report.views import ReportSpecificationScheduleMixin
class ReportSpecificationScheduleView(
LoginRequiredMixin,
ReportSpecificationScheduleMixin,
UserPassesTestMixin,
BaseMixin,
UpdateView,
):
"""Create a new schedule for this report specification."""
pass
Tip
This view is created as a mixin because most projects will need
permission checks (using UserPassesTestMixin
) to make sure the
user can run the report.
Tip
ReportSpecificationScheduleMixin
needs to appear before
UserPassesTestMixin
in the list of classes.
The report will need a slug
URL e.g:
url(
regex=r"^example/(?P<slug>[-\w\d]+)/report/$",
view=ReportSpecificationScheduleView.as_view(),
name="project.report.specification.schedule",
),
To schedule a simple report (with no parameters) from the Django template, use
the REPORT_SLUG
as the parameter e.g:
<a href="{% url 'project.report.specification.schedule' report.slug %}" class="pure-menu-link">
<i class="fa fa-table"></i>
{{ report.title }}
</a>
Parameters¶
To handle report parameters, you could create a _check_parameters
method in
your report class e.g.
https://gitlab.com/kb/invoice/blob/master/invoice/service.py#L432
e.g:
class TimeAnalysisByContact(ReportMixin):
def _check_parameters(self, parameters):
if not parameters:
raise ReportError("Cannot run report without any parameters")
contact_pk = parameters.get("contact_pk")
return contact_pk
def run_csv_report(self, csv_writer, parameters=None):
contact_pk = self._check_parameters(parameters)
Snippets¶
List of available reports:
from report.models import ReportSpecification
[(x.app, x.module, x.report_class) for x in ReportSpecification.objects.all()]
To get a list of outstanding reports:
from report.models import ReportSchedule
ReportSchedule.objects.outstanding()
Project home (project.report
):
from django.urls import reverse_lazy
from django.views.generic import RedirectView
url(
regex=r"^report/$",
view=RedirectView.as_view(url=reverse_lazy("report.schedule.list")),
name="project.report",
),
Testing¶
Content¶
To check the CSV content:
Initialise the app / project so the ReportSpecification
is created e.g:
from django.core.management import call_command
call_command("init_app_invoice")
Get the report, prepare the parameters and schedule the report:
from report.models import ReportSchedule, ReportSpecification
report_specification = ReportSpecification.objects.get(
slug="invoice-time-analysis-contact"
)
parameters = {"contact_pk": contact.pk}
report_specification.schedule(user, parameters=parameters)
Run the report and find the schedule:
schedule_pks = ReportSchedule.objects.run_reports()
assert 1 == len(schedule_pks)
schedule_pk = schedule_pks[0]
schedule = ReportSchedule.objects.get(pk=schedule_pk)
Check the report output:
import csv
reader = csv.reader(open(schedule.output_file.path), "excel")
first_row = None
result = []
for row in reader:
if not first_row:
first_row = row
else:
result.append(row)
assert ["Ticket", "Contact", "Charge", "Fixed", "Non-Charge"] == first_row
assert [
["Apple", "pat", "30.0", "0", "0"],
] == result
Scheduled¶
To check a report has been scheduled:
# make sure the report is scheduled
qs = ReportSchedule.objects.current()
assert 1 == qs.count()
# optional checks
schedule = qs.first()
assert report_specification.slug == schedule.report.slug
assert timezone.now().date() == schedule.created.date()