Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Download is missing support for NAME, CODE, UID and ID #16049

Merged
merged 9 commits into from
Jan 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,8 @@
package org.hisp.dhis.analytics.outlier;

import static org.hisp.dhis.analytics.common.ColumnHeader.ABSOLUTE_DEVIATION;
import static org.hisp.dhis.analytics.common.ColumnHeader.ATTRIBUTE_OPTION_COMBO_NAME;
import static org.hisp.dhis.analytics.common.ColumnHeader.CATEGORY_OPTION_COMBO_NAME;
import static org.hisp.dhis.analytics.common.ColumnHeader.DIMENSION_NAME;
import static org.hisp.dhis.analytics.common.ColumnHeader.LOWER_BOUNDARY;
import static org.hisp.dhis.analytics.common.ColumnHeader.MEDIAN_ABS_DEVIATION;
import static org.hisp.dhis.analytics.common.ColumnHeader.ORG_UNIT_NAME;
import static org.hisp.dhis.analytics.common.ColumnHeader.STANDARD_DEVIATION;
import static org.hisp.dhis.analytics.common.ColumnHeader.UPPER_BOUNDARY;
import static org.hisp.dhis.analytics.common.ColumnHeader.ZSCORE;
Expand All @@ -51,10 +47,6 @@
public enum Order {
Z_SCORE(ZSCORE.getItem(), "z_score"),
MODIFIED_ZSCORE(ColumnHeader.MODIFIED_ZSCORE.getItem(), "z_score"),
DE_NAME(DIMENSION_NAME.getItem(), "de_name"),
OU_NAME(ORG_UNIT_NAME.getItem(), "ou_name"),
COC_NAME(CATEGORY_OPTION_COMBO_NAME.getItem(), "coc_name"),
AOC_NAME(ATTRIBUTE_OPTION_COMBO_NAME.getItem(), "aoc_name"),
VALUE("value", "value"),
MEDIAN(ColumnHeader.MEDIAN.getItem(), "middle_value"),
MEAN(ColumnHeader.MEAN.getItem(), "middle_value"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.hisp.dhis.analytics.QueryKey;
import org.hisp.dhis.analytics.SortOrder;
import org.hisp.dhis.analytics.outlier.Order;
import org.hisp.dhis.common.IdScheme;

/** Encapsulation of a web API request for outlier value detection. */
@Data
Expand Down Expand Up @@ -72,6 +73,8 @@ public class OutlierQueryParams {

private Integer maxResults;

private IdScheme outputIdScheme = IdScheme.UID;

public boolean hasHeaders() {
return headers != null && !headers.isEmpty();
}
Expand All @@ -88,6 +91,7 @@ public String queryKey() {
key.add(threshold);
key.add(orderBy);
key.add(sortOrder);
key.add(outputIdScheme);

if (ds != null) {
ds.forEach(e -> key.add("ds", "[" + e + "]"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public OutlierRequest getFromQuery(OutlierQueryParams queryParams, boolean analy
.analyzeOnly(analyzeOnly)
.dataStartDate(queryParams.getDataStartDate())
.dataEndDate(queryParams.getDataEndDate())
.outputIdScheme(queryParams.getOutputIdScheme())
.queryKey(queryParams.queryKey());

if (queryParams.getAlgorithm() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.hisp.dhis.analytics.OutlierDetectionAlgorithm;
import org.hisp.dhis.analytics.SortOrder;
import org.hisp.dhis.analytics.outlier.Order;
import org.hisp.dhis.common.IdScheme;
import org.hisp.dhis.common.OrganisationUnitDescendants;
import org.hisp.dhis.dataelement.DataElement;
import org.hisp.dhis.organisationunit.OrganisationUnit;
Expand Down Expand Up @@ -82,6 +83,8 @@ public class OutlierRequest {

@Default private double threshold = 3.0d;

@Default private IdScheme outputIdScheme = IdScheme.UID;

private boolean analyzeOnly;

private String explainOrderId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
import static org.hisp.dhis.analytics.common.ColumnHeader.UPPER_BOUNDARY;
import static org.hisp.dhis.analytics.common.ColumnHeader.VALUE;
import static org.hisp.dhis.analytics.common.ColumnHeader.ZSCORE;
import static org.hisp.dhis.common.IdentifiableProperty.CODE;
import static org.hisp.dhis.common.IdentifiableProperty.ID;
import static org.hisp.dhis.common.ValueType.NUMBER;
import static org.hisp.dhis.common.ValueType.TEXT;

Expand All @@ -62,10 +64,15 @@
import org.hisp.dhis.analytics.common.TableInfoReader;
import org.hisp.dhis.analytics.outlier.data.Outlier;
import org.hisp.dhis.analytics.outlier.data.OutlierRequest;
import org.hisp.dhis.category.CategoryOptionCombo;
import org.hisp.dhis.common.ExecutionPlan;
import org.hisp.dhis.common.Grid;
import org.hisp.dhis.common.GridHeader;
import org.hisp.dhis.common.IdScheme;
import org.hisp.dhis.common.IdentifiableObject;
import org.hisp.dhis.common.IdentifiableObjectManager;
import org.hisp.dhis.common.IllegalQueryException;
import org.hisp.dhis.dataelement.DataElement;
import org.hisp.dhis.feedback.ErrorCode;
import org.hisp.dhis.feedback.ErrorMessage;
import org.hisp.dhis.organisationunit.OrganisationUnit;
Expand All @@ -91,6 +98,8 @@ public class AnalyticsOutlierService {

private final TableInfoReader tableInfoReader;

private final IdentifiableObjectManager idObjectManager;

/**
* Transform the incoming request into api response (json).
*
Expand All @@ -103,7 +112,7 @@ public Grid getOutliers(OutlierRequest request) throws IllegalQueryException {

Grid grid = new ListGrid();
setHeaders(grid, request);
setMetaData(grid, request, outliers);
setMetaData(grid, outliers, request);
setRows(grid, outliers, request);

return grid;
Expand Down Expand Up @@ -247,14 +256,28 @@ private void setHeaders(Grid grid, OutlierRequest request) {
new GridHeader(UPPER_BOUNDARY.getItem(), UPPER_BOUNDARY.getName(), NUMBER, false, false));
}

private void setMetaData(Grid grid, OutlierRequest request, List<Outlier> outliers) {
/**
* The method add the metadata into the response grid.
*
* @param grid the {@link Grid}
* @param outliers the list of {@link Outlier}
* @param request the {@link OutlierRequest}
*/
private void setMetaData(Grid grid, List<Outlier> outliers, OutlierRequest request) {
grid.addMetaData("algorithm", request.getAlgorithm());
grid.addMetaData("threshold", request.getThreshold());
grid.addMetaData("orderBy", request.getOrderBy().getColumnName());
grid.addMetaData("maxResults", request.getMaxResults());
grid.addMetaData("count", outliers.size());
}

/**
* The method add the rows into the response grid.
*
* @param grid the {@link Grid}
* @param outliers the list of {@link Outlier}
* @param request the {@link OutlierRequest}
*/
private void setRows(Grid grid, List<Outlier> outliers, OutlierRequest request) {
outliers.forEach(
v -> {
Expand All @@ -264,16 +287,27 @@ private void setRows(Grid grid, List<Outlier> outliers, OutlierRequest request)
Collection<OrganisationUnit> roots = user != null ? user.getOrganisationUnits() : null;

grid.addRow();
grid.addValue(v.getDx());
grid.addValue(v.getDxName());

IdentifiableObject object = idObjectManager.get(DataElement.class, v.getDx());
grid.addValue(getIdProperty(object, v.getDx(), request.getOutputIdScheme()));
grid.addValue(getIdProperty(object, v.getDx(), IdScheme.NAME));

grid.addValue(v.getPe());
grid.addValue(v.getOu());
grid.addValue(v.getOuName());

object = idObjectManager.get(OrganisationUnit.class, v.getOu());
grid.addValue(getIdProperty(object, v.getOu(), request.getOutputIdScheme()));
grid.addValue(getIdProperty(object, v.getOu(), IdScheme.NAME));

grid.addValue(ou.getParentNameGraph(roots, true));
grid.addValue(v.getCoc());
grid.addValue(v.getCocName());
grid.addValue(v.getAoc());
grid.addValue(v.getAocName());

object = idObjectManager.get(CategoryOptionCombo.class, v.getCoc());
grid.addValue(getIdProperty(object, v.getCoc(), request.getOutputIdScheme()));
grid.addValue(getIdProperty(object, v.getCoc(), IdScheme.NAME));

object = idObjectManager.get(CategoryOptionCombo.class, v.getAoc());
grid.addValue(getIdProperty(object, v.getAoc(), request.getOutputIdScheme()));
grid.addValue(getIdProperty(object, v.getAoc(), IdScheme.NAME));

grid.addValue(v.getValue());
grid.addValue(isModifiedZScore ? v.getMedian() : v.getMean());
grid.addValue(v.getStdDev());
Expand All @@ -283,4 +317,28 @@ private void setRows(Grid grid, List<Outlier> outliers, OutlierRequest request)
grid.addValue(v.getUpperBound());
});
}

/**
* The method retrieves ID Property. Depend on the IdScheme parameter it could be ID, UID, UUID,
* Code or Name. The default property is the UID.
*
* @param object the {@link IdentifiableObject}
* @param uid the {@link String}, default UID of the identifiable object (data element,
* organisation unit, category option combo, etc...)
* @param idScheme the {@link IdScheme}
* @return ID Property of the identifiable object (ID, UID, UUID, Code or Name)
*/
private String getIdProperty(IdentifiableObject object, String uid, IdScheme idScheme) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javadoc is missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (object == null || idScheme == IdScheme.UID || idScheme == IdScheme.UUID) {
return uid;
}
if (idScheme.getIdentifiableProperty() == ID) {
return Long.toString(object.getId());
}
if (idScheme.getIdentifiableProperty() == CODE) {
return object.getCode();
}

return object.getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,10 @@ private Outlier getOutlier(Calendar calendar, ResultSet rs) throws SQLException

Outlier outlier = new Outlier();
outlier.setDx(rs.getString("de_uid"));
outlier.setDxName(rs.getString("de_name"));
outlier.setPe(isoPeriod);
outlier.setOu(rs.getString("ou_uid"));
outlier.setOuName(rs.getString("ou_name"));
outlier.setCoc(rs.getString("coc_uid"));
outlier.setCocName(rs.getString("coc_name"));
outlier.setAoc(rs.getString("aoc_uid"));
outlier.setAocName(rs.getString("aoc_name"));
outlier.setValue(rs.getDouble("value"));

return outlier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,6 @@ private String getSqlStatement(OutlierRequest request, boolean withParams) {
+ "ax.ou as ou_uid, "
+ "ax.co as coc_uid, "
+ "ax.ao as aoc_uid, "
+ "ax.de_name, "
+ "ax.ou_name, "
+ "ax.coc_name, "
+ "ax.aoc_name, "
+ "ax.value, "
+ "ax.pestartdate as pe_start_date, "
+ "ax.petype as pt_name, "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,12 +571,6 @@ private List<AnalyticsTableColumn> getOutlierStatsColumns() {
quote("attributeoptioncomboid"), INTEGER, NOT_NULL, "dv.attributeoptioncomboid"),
new AnalyticsTableColumn(quote("dataelementid"), INTEGER, NOT_NULL, "dv.dataelementid")
.withIndexColumns(List.of(quote("dataelementid"))),

// TODO: Remove all these name columns from here. Analytics tables should not have them.
new AnalyticsTableColumn(quote("de_name"), VARCHAR_255, "de.name"),
new AnalyticsTableColumn(quote("ou_name"), VARCHAR_255, "ou.name"),
new AnalyticsTableColumn(quote("coc_name"), VARCHAR_255, "co.name"),
new AnalyticsTableColumn(quote("aoc_name"), VARCHAR_255, "ao.name"),
new AnalyticsTableColumn(quote("petype"), VARCHAR_255, "pt.name"),
new AnalyticsTableColumn(quote("path"), VARCHAR_255, "ou.path"),
// mean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void testGetSqlStatement() {
.build();
String sql = subject.getSqlStatement(builder.build());
String expected =
"select * from (select ax.dx as de_uid, ax.ou as ou_uid, ax.co as coc_uid, ax.ao as aoc_uid, ax.de_name, ax.ou_name, ax.coc_name, ax.aoc_name, ax.value, ax.pestartdate as pe_start_date, ax.petype as pt_name, ax.avg_middle_value as middle_value, ax.std_dev, ax.mad, abs(ax.value::double precision - ax.avg_middle_value) as middle_value_abs_dev, (case when ax.std_dev = 0 then 0 else abs(ax.value::double precision - ax.avg_middle_value ) / ax.std_dev end) as z_score, ax.avg_middle_value - (ax.std_dev * :threshold) as lower_bound, ax.avg_middle_value + (ax.std_dev * :threshold) as upper_bound from analytics ax where dataelementid in (:data_element_ids) and (ax.\"path\" like '/ouabcdefghA%' or ax.\"path\" like '/ouabcdefghB%') and ax.pestartdate >= :start_date and ax.peenddate <= :end_date) t1 where t1.z_score > :threshold order by middle_value_abs_dev desc limit :max_results ";
"select * from (select ax.dx as de_uid, ax.ou as ou_uid, ax.co as coc_uid, ax.ao as aoc_uid, ax.value, ax.pestartdate as pe_start_date, ax.petype as pt_name, ax.avg_middle_value as middle_value, ax.std_dev, ax.mad, abs(ax.value::double precision - ax.avg_middle_value) as middle_value_abs_dev, (case when ax.std_dev = 0 then 0 else abs(ax.value::double precision - ax.avg_middle_value ) / ax.std_dev end) as z_score, ax.avg_middle_value - (ax.std_dev * :threshold) as lower_bound, ax.avg_middle_value + (ax.std_dev * :threshold) as upper_bound from analytics ax where dataelementid in (:data_element_ids) and (ax.\"path\" like '/ouabcdefghA%' or ax.\"path\" like '/ouabcdefghB%') and ax.pestartdate >= :start_date and ax.peenddate <= :end_date) t1 where t1.z_score > :threshold order by middle_value_abs_dev desc limit :max_results ";
assertEquals(expected, sql);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public enum ColumnHeader {
EVENT_STATUS("eventstatus", "Event status"),
DIMENSION("dx", "Data"),
DIMENSION_NAME("dxname", "Data name"),
PERIOD("pe", "Event status"),
PERIOD("pe", "Period"),
CATEGORY_OPTION_COMBO("coc", "Category option combo"),
CATEGORY_OPTION_COMBO_NAME("cocname", "Category option combo name"),
ATTRIBUTE_OPTION_COMBO("aoc", "Attribute option combo"),
Expand Down
Loading