Filters are defined on the report on the fields
attribute. Every filter is injected into the report context and then in a template they are looped over and rendered:
{% for field in report_filters %}
<fieldset id="fieldset_{{ field.slug }}">
<div class="form-group" id="group_{{ field.slug }}">
{{ field.field|safe }}
</div>
</fieldset>
{% endfor %}
Each filter is responsible for rendering itself via the render
command. The filters are not first rendered on page load. It happens like this:
- First the reports template is loaded (TODO: find link to template)
- Javascript in
reports.async.js
makes a query to that looks like
GET "/a/aspace/reports/async/submissions_by_form/?hq_filters=true&&startdate=2017-01-30&enddate=2017-02-05&filterSet=false&&"`
- HQ processes the request and returns a dict:
{
filters: "<HTML for the filters>",
report: "<HTML for initial report page>",
slug: "<slug for report e.g submissions_by_form>"
title: "<title for report e.g. Submissions By Form>"
url_root: "<root url e.g. /a/aspace/reports/async/>"
}
- Success function calls
loadFilters
to splat in the filter HTML. Similar function is called for report body
Note: some of the report templates have links to other scripts that load when the html gets loaded.
Filters are applied when the Apply
button is hit on the report. The request makes the same call when initially rendering the filters.
First a call is made to the backend to get the filter HTML:
GET /a/aspace/reports/filters/submissions_by_form/?emw=t__0&emw=t__4&form_unknown=&form_unknown_xmlns=&form_app_id=&form_module=&form_xmlns=&sub_time=&startdate=2017-01-01&enddate=2017-01-31&
This returns a dict with the filter
HTML put no report data:
{
filter: '<filter HTML>',
slug: '<slug>',
title: '<title>',
}
Then another query is fired off to get the report HTML
GET "/a/aspace/reports/async/submissions_by_form/?hq_filters=true&&startdate=2017-01-30&enddate=2017-02-05&filterSet=true&&"
This returns a dict with the report html:
{
report: '<report HTML>'
filter: null,
slug: '<slug>',
title: '<title>',
}
A key difference here is that filterSet
is set to true
.
Most of the routing goes through corehq/apps/reports/dispatcher.py
. Report classes are determined via the map_name
attribute on the ReportDispatcher
class. Various other available reports are added in via settings. The main method that does this is ReportDispatcher.get_reports
.
Once we've gotten the report class by the slug, we instantiate it with the passed in params. Depending on the params passed in, we either return the report html or the filter html (as discussed above).