Skip to content

Commit

Permalink
Merge pull request #4971 from dimagi/facility-reporting-rate-report
Browse files Browse the repository at this point in the history
Add new facility mode for reporting rate report
  • Loading branch information
czue committed Jan 7, 2015
2 parents 48b972c + d27bccb commit 5156667
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 29 deletions.
37 changes: 29 additions & 8 deletions corehq/apps/reports/commtrack/data_sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,20 @@ class ReportingStatusDataSource(ReportDataSource, CommtrackDataSourceMixin, Mult
location_id: ID of location to get data for. Omit for all locations.
"""

@property
def converted_start_datetime(self):
start_date = self.start_date
if isinstance(start_date, unicode):
start_date = parser.parse(start_date)
return start_date

@property
def converted_end_datetime(self):
end_date = self.end_date
if isinstance(end_date, unicode):
end_date = parser.parse(end_date)
return end_date

def get_data(self):
# todo: this will probably have to paginate eventually
if self.all_relevant_forms:
Expand All @@ -418,39 +432,46 @@ def get_data(self):
for spoint_id, loc_id in spoint_loc_map.items():
loc = locations[loc_id]

form_ids = StockReport.objects.filter(
results = StockReport.objects.filter(
stocktransaction__case_id=spoint_id
).exclude(
date__lte=self.start_date
).exclude(
date__gte=self.end_date
).values_list(
'form_id', flat=True
'form_id',
'date'
).order_by('-date').distinct() # not truly distinct due to ordering

matched = False
for form_id in form_ids:
for form_id, date in results:
if self.converted_start_datetime > date:
break

try:
if XFormInstance.get(form_id).xmlns in form_xmlnses:
if self.converted_end_datetime >= date and \
XFormInstance.get(form_id).xmlns in form_xmlnses:
yield {
'loc': loc,
'loc_id': loc._id,
'loc_path': loc.path,
'name': loc.name,
'type': loc.location_type,
'reporting_status': 'reporting',
'geo': loc._geopoint,
'last_reporting_date': date,
}
matched = True
break
except ResourceNotFound:
logging.error('Stock report for location {} in {} references non-existent form {}'.format(
loc._id, loc.domain, form_id
))

if not matched:
yield {
'loc': loc,
'loc_id': loc._id,
'loc_path': loc.path,
'name': loc.name,
'type': loc.location_type,
'reporting_status': 'nonreporting',
'geo': loc._geopoint,
'last_reporting_date': results[0][1] if results else ''
}
84 changes: 65 additions & 19 deletions corehq/apps/reports/commtrack/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from corehq.apps.products.models import Product, SQLProduct
from corehq.apps.reports.graph_models import PieChart, MultiBarChart, Axis
from corehq.apps.reports.standard import ProjectReport, ProjectReportParametersMixin, DatespanMixin
from corehq.apps.reports.filters.commtrack import SelectReportingType
from dimagi.utils.couch.loosechange import map_reduce
from datetime import datetime
from corehq.apps.locations.models import Location
Expand Down Expand Up @@ -366,6 +367,7 @@ class ReportingRatesReport(GenericTabularReport, CommtrackReportMixin):
'corehq.apps.reports.filters.fixtures.AsyncLocationFilter',
'corehq.apps.reports.filters.forms.FormsByApplicationFilter',
'corehq.apps.reports.filters.dates.DatespanFilter',
'corehq.apps.reports.filters.commtrack.SelectReportingType',
]
exportable = True
emailable = True
Expand All @@ -375,20 +377,56 @@ class ReportingRatesReport(GenericTabularReport, CommtrackReportMixin):
def show_in_navigation(cls, domain=None, project=None, user=None):
return super(ReportingRatesReport, cls).show_in_navigation(domain, project, user)

def is_aggregate_report(self):
return self.request.GET.get(SelectReportingType.slug, '') != 'facilities'

@property
def headers(self):
return DataTablesHeader(*(DataTablesColumn(text) for text in [
_('Location'),
_('# Sites'),
_('# Reporting'),
_('Reporting Rate'),
_('# Non-reporting'),
_('Non-reporting Rate'),
]))
if self.is_aggregate_report():
return DataTablesHeader(*(DataTablesColumn(text) for text in [
_('Location'),
_('# Sites'),
_('# Reporting'),
_('Reporting Rate'),
_('# Non-reporting'),
_('Non-reporting Rate'),
]))
else:
return DataTablesHeader(*(DataTablesColumn(text) for text in [
_('Location'),
_('Parent location'),
_('Date of last report for selected period'),
_('Reporting'),
]))

@property
@memoized
def _facility_data(self):
config = {
'domain': self.domain,
'location_id': self.request.GET.get('location_id'),
'startdate': self.datespan.startdate_utc,
'enddate': self.datespan.enddate_utc,
'request': self.request,
}
statuses = list(ReportingStatusDataSource(config).get_data())

results = []
for status in statuses:
results.append([
status['name'],
status['loc'].parent.name if status['loc'].parent else '',
status['last_reporting_date'].date() if status['last_reporting_date'] else _('Never'),
_('Yes') if status['reporting_status'] == 'reporting' else _('No'),
])

master_tally = self.status_tally([site['reporting_status'] for site in statuses])

return master_tally, results

@property
@memoized
def _data(self):
def _aggregate_data(self):
config = {
'domain': self.domain,
'location_id': self.request.GET.get('location_id'),
Expand All @@ -414,16 +452,10 @@ def case_iter():
sites_by_agg_site = map_reduce(lambda (path, status): [(child_loc(path), path[-1])],
data=case_iter())

def status_tally(statuses):
total = len(statuses)

return map_reduce(lambda s: [(s,)],
lambda v: {'count': len(v), 'pct': len(v) / float(total)},
data=statuses)
status_counts = dict((loc_id, status_tally(statuses))
status_counts = dict((loc_id, self.status_tally(statuses))
for loc_id, statuses in status_by_agg_site.iteritems())

master_tally = status_tally([site['reporting_status'] for site in statuses])
master_tally = self.status_tally([site['reporting_status'] for site in statuses])

locs = sorted(Location.view('_all_docs', keys=status_counts.keys(), include_docs=True),
key=lambda loc: loc.name)
Expand All @@ -448,12 +480,26 @@ def _rows():

return master_tally, _rows()

def status_tally(self, statuses):
total = len(statuses)

return map_reduce(lambda s: [(s,)],
lambda v: {'count': len(v), 'pct': len(v) / float(total)},
data=statuses)

@property
def rows(self):
return self._data[1]
if self.is_aggregate_report():
return self._aggregate_data[1]
else:
return self._facility_data[1]

def master_pie_chart_data(self):
tally = self._data[0]
if self.is_aggregate_report():
tally = self._aggregate_data[0]
else:
tally = self._facility_data[0]

labels = {
'reporting': _('Reporting'),
'nonreporting': _('Non-reporting'),
Expand Down
16 changes: 14 additions & 2 deletions corehq/apps/reports/filters/commtrack.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
from corehq.apps.reports.filters.base import CheckboxFilter
from django.utils.translation import ugettext_lazy
from corehq.apps.reports.filters.base import BaseSingleOptionFilter, CheckboxFilter
from django.utils.translation import ugettext_lazy, ugettext_noop


class SelectReportingType(BaseSingleOptionFilter):
slug = "report_type"
label = ugettext_noop("Reporting data type")
default_text = ugettext_noop("Show aggregate data")

@property
def options(self):
return [
("facilities", "Show facility level data"),
]


class AdvancedColumns(CheckboxFilter):
Expand Down

0 comments on commit 5156667

Please sign in to comment.