Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
package org.hisp.dhis.common;

import static org.hisp.dhis.common.DimensionalObjectUtils.COMPOSITE_DIM_OBJECT_PLAIN_SEP;
import static org.hisp.dhis.common.TotalAggregationType.AVERAGE;
import static org.hisp.dhis.common.TotalAggregationType.SUM;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
Expand Down Expand Up @@ -101,12 +103,38 @@ public DimensionItemType getDimensionItemType() {

@Override
public List<LegendSet> getLegendSets() {
return dataSet.getLegendSets();
if (dataSet != null) {
return dataSet.getLegendSets();
}

return List.of();
}

@Override
public LegendSet getLegendSet() {
if (dataSet != null) {
return dataSet.getLegendSet();
}

return null;
}

@Override
public boolean hasLegendSet() {
if (dataSet != null) {
return dataSet.hasLegendSet();
}

return false;
}

@Override
public TotalAggregationType getTotalAggregationType() {
return TotalAggregationType.AVERAGE;
if (metric.isSum()) {
return SUM;
}

return AVERAGE;
}

// -------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public enum ReportingRateMetric {

ReportingRateMetric(String key, String displayName) {
this.displayName = displayName;
this.key = key;
}

public String key() {
Expand All @@ -54,4 +55,8 @@ public String key() {
public String displayName() {
return displayName;
}

public boolean isSum() {
return this == ACTUAL_REPORTS || this == EXPECTED_REPORTS || this == ACTUAL_REPORTS_ON_TIME;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
import static org.hisp.dhis.common.ReportingRateMetric.ACTUAL_REPORTS;
import static org.hisp.dhis.common.ReportingRateMetric.ACTUAL_REPORTS_ON_TIME;
import static org.hisp.dhis.common.ReportingRateMetric.EXPECTED_REPORTS;
import static org.hisp.dhis.common.ReportingRateMetric.REPORTING_RATE;
import static org.hisp.dhis.common.ReportingRateMetric.REPORTING_RATE_ON_TIME;
import static org.hisp.dhis.commons.util.DebugUtils.getStackTrace;
import static org.hisp.dhis.commons.util.SystemUtils.getCpuCores;
Expand Down Expand Up @@ -729,11 +730,30 @@ private void addReportRateToGrid(
.addValue(params.isSkipRounding() ? value : getRoundedValueObject(params, value));

if (params.isIncludeNumDen()) {
grid.addValue(actual).addValue(target).addValue(PERCENT).addNullValues(2);
int multiplier = isRateMetric(metric) ? PERCENT : 1;
int divisor = 1;
double factor = (double) multiplier / divisor;

grid.addValue(actual)
.addValue(target)
.addValue(factor)
.addValue(multiplier)
.addValue(divisor);
}
}
}

/**
* Indicates whether the given metric is a percentage rate, as opposed to a count of reports.
*
* @param metric the {@link ReportingRateMetric}.
* @return true if the metric is {@link ReportingRateMetric#REPORTING_RATE} or {@link
* ReportingRateMetric#REPORTING_RATE_ON_TIME}.
*/
private boolean isRateMetric(ReportingRateMetric metric) {
return metric == REPORTING_RATE || metric == REPORTING_RATE_ON_TIME;
}

private boolean satisfiesMeasureCriteria(DataQueryParams params, Double value) {
if (params.hasMeasureCriteria() && value != null) {
Number finalValue =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import static org.hisp.dhis.test.TestBase.createDataSet;
import static org.hisp.dhis.test.TestBase.injectSecurityContextNoSettings;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
Expand Down Expand Up @@ -73,7 +74,7 @@
*/
class AnalyticsServiceReportingRateTest extends AnalyticsServiceBaseTest {
@BeforeEach
public void setUp() {
void setUp() {
injectSecurityContextNoSettings(new SystemUser());
}

Expand Down Expand Up @@ -326,8 +327,7 @@ void verifyReportingRatesForMonthsWithMoreThen30DaysAreComputedCorrectly() {
targets.put(dataSetA.getUid() + "-" + "201901", 1D);

// Response for COMPLETENESS - set the completeness value to the same
// number of
// days of the selected month
// number of days of the selected month
Map<String, Object> actuals = new HashMap<>();
actuals.put(dataSetA.getUid() + "-" + "201901", 31D);

Expand All @@ -343,6 +343,186 @@ void verifyReportingRatesForMonthsWithMoreThen30DaysAreComputedCorrectly() {
assertReportingRatesGrid(grid, dataSetA, "201901");
}

@Test
void verifyNumDenForRateMetricsHasPercentMultiplierAndDivisor() {
DataSet dataSetA = createDataSet('A');
OrganisationUnit ou = new OrganisationUnit("aaaa");

Grid grid =
getNumDenGrid(
dataSetA,
ou,
10,
true,
500D,
100D,
ReportingRateMetric.REPORTING_RATE,
ReportingRateMetric.REPORTING_RATE_ON_TIME);

for (ReportingRateMetric metric :
List.of(ReportingRateMetric.REPORTING_RATE, ReportingRateMetric.REPORTING_RATE_ON_TIME)) {
List<Object> row = getRowByDx(grid, makeKey(dataSetA, metric));

assertEquals(50L, getValue(grid, row, "value"), metric.name());
assertEquals(500D, getValue(grid, row, "numerator"), metric.name());
assertEquals(1000D, getValue(grid, row, "denominator"), metric.name());
assertEquals(100D, getValue(grid, row, "factor"), metric.name());
assertEquals(100, getValue(grid, row, "multiplier"), metric.name());
assertEquals(1, getValue(grid, row, "divisor"), metric.name());
}
}

@Test
void verifyNumDenForCountMetricsHasUnitFactorMultiplierAndDivisor() {
DataSet dataSetA = createDataSet('A');
OrganisationUnit ou = new OrganisationUnit("aaaa");

Grid grid =
getNumDenGrid(
dataSetA,
ou,
10,
true,
500D,
100D,
ReportingRateMetric.ACTUAL_REPORTS,
ReportingRateMetric.ACTUAL_REPORTS_ON_TIME,
ReportingRateMetric.EXPECTED_REPORTS);

for (ReportingRateMetric metric :
List.of(
ReportingRateMetric.ACTUAL_REPORTS,
ReportingRateMetric.ACTUAL_REPORTS_ON_TIME,
ReportingRateMetric.EXPECTED_REPORTS)) {
List<Object> row = getRowByDx(grid, makeKey(dataSetA, metric));

assertEquals(1D, getValue(grid, row, "factor"), metric.name());
assertEquals(1, getValue(grid, row, "multiplier"), metric.name());
assertEquals(1, getValue(grid, row, "divisor"), metric.name());
}

// Numerator and denominator are always the actual and the expected reports
List<Object> expected =
getRowByDx(grid, makeKey(dataSetA, ReportingRateMetric.EXPECTED_REPORTS));
assertEquals(1000L, getValue(grid, expected, "value"));
assertEquals(0D, getValue(grid, expected, "numerator"));
assertEquals(1000D, getValue(grid, expected, "denominator"));

List<Object> actual = getRowByDx(grid, makeKey(dataSetA, ReportingRateMetric.ACTUAL_REPORTS));
assertEquals(500L, getValue(grid, actual, "value"));
assertEquals(500D, getValue(grid, actual, "numerator"));
assertEquals(1000D, getValue(grid, actual, "denominator"));
}

@Test
void verifyNumDenWhenReportingRateIsCappedAt100() {
DataSet dataSetA = createDataSet('A');
OrganisationUnit ou = new OrganisationUnit("aaaa");

// More actual reports than expected: the rate is capped, numerator and
// denominator are not
Grid grid =
getNumDenGrid(dataSetA, ou, 1, true, 150D, 100D, ReportingRateMetric.REPORTING_RATE);

List<Object> row = getRowByDx(grid, makeKey(dataSetA, ReportingRateMetric.REPORTING_RATE));

assertEquals(100L, getValue(grid, row, "value"));
assertEquals(150D, getValue(grid, row, "numerator"));
assertEquals(100D, getValue(grid, row, "denominator"));
assertEquals(100D, getValue(grid, row, "factor"));
assertEquals(100, getValue(grid, row, "multiplier"));
assertEquals(1, getValue(grid, row, "divisor"));
}

@Test
void verifyNoNumDenColumnsWhenIncludeNumDenIsFalse() {
DataSet dataSetA = createDataSet('A');
OrganisationUnit ou = new OrganisationUnit("aaaa");

Grid grid =
getNumDenGrid(dataSetA, ou, 1, false, 50D, 100D, ReportingRateMetric.REPORTING_RATE);

assertThat(grid.getHeaders(), hasSize(3));
assertThat(grid.getRows(), hasSize(1));
assertThat(grid.getRow(0), hasSize(3));
assertEquals(-1, getDimensionIndex(grid.getHeaders(), "multiplier"));
assertEquals(-1, getDimensionIndex(grid.getHeaders(), "divisor"));
}

/**
* Runs an aggregated query for the given reporting rate metrics of a single data set and org
* unit, using a period filter with the given number of monthly periods.
*/
private Grid getNumDenGrid(
DataSet dataSet,
OrganisationUnit ou,
int timeUnits,
boolean includeNumDen,
double actualReports,
double expectedReportsPerPeriod,
ReportingRateMetric... metrics) {
List<DimensionalItemObject> reportingRates = new ArrayList<>();

for (ReportingRateMetric metric : metrics) {
ReportingRate reportingRate = new ReportingRate(dataSet);
reportingRate.setMetric(metric);
reportingRates.add(reportingRate);
}

List<DimensionalItemObject> periods = new ArrayList<>();

Stream.iterate(1, i -> i + 1)
.limit(timeUnits)
.forEach(
x ->
periods.add(
PeriodDimension.of(
new MonthlyPeriodType()
.createPeriod(new DateTime(2014, x, 1, 0, 0).toDate()))));

DataQueryParams params =
DataQueryParams.newBuilder()
.withOrganisationUnit(ou)
.withDataElements(reportingRates)
.withIgnoreLimit(true)
.withIncludeNumDen(includeNumDen)
.withFilters(List.of(new BaseDimensionalObject("pe", DimensionType.PERIOD, periods)))
.build();

initMock(params);

Map<String, Object> actuals = new HashMap<>();
actuals.put(dataSet.getUid() + "-" + ou.getUid(), actualReports);

when(analyticsManager.getAggregatedDataValues(
any(DataQueryParams.class), eq(AnalyticsTableType.COMPLETENESS), eq(0)))
.thenReturn(CompletableFuture.completedFuture(actuals));

Map<String, Object> targets = new HashMap<>();
targets.put(dataSet.getUid() + "-" + ou.getUid(), expectedReportsPerPeriod);

when(analyticsManager.getAggregatedDataValues(
any(DataQueryParams.class), eq(AnalyticsTableType.COMPLETENESS_TARGET), eq(0)))
.thenReturn(CompletableFuture.completedFuture(targets));

return target.getAggregatedDataValueGrid(params);
}

private List<Object> getRowByDx(Grid grid, String dx) {
int dxIndex = getDimensionIndex(grid.getHeaders(), "dx");

return grid.getRows().stream()
.filter(row -> dx.equals(row.get(dxIndex)))
.findFirst()
.orElseThrow(() -> new AssertionError("No row found for dx: " + dx));
}

private Object getValue(Grid grid, List<Object> row, String header) {
int index = getDimensionIndex(grid.getHeaders(), header);
assertNotEquals(-1, index, "Missing header: " + header);
return row.get(index);
}

private void assertReportingRatesGrid(Grid grid, DataSet dataset, String period) {
assertThat(grid.getRows(), hasSize(1));
assertThat(grid.getRow(0), hasSize(3));
Expand Down
Loading
Loading