diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java index 31d587e51392..3068a1222d4f 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java @@ -125,6 +125,18 @@ public class JdbcAnalyticsTableManager extends AbstractJdbcTableManager { .selectExpression("acs.categoryoptioncombouid as ao") .indexColumns(List.of("dx", "ao")) .build(), + AnalyticsTableColumn.builder() + .name("optionsetuid") + .dataType(CHARACTER_11) + .nullable(NULL) + .selectExpression("deo.optionsetuid as optionsetuid") + .build(), + AnalyticsTableColumn.builder() + .name("optionvalueuid") + .dataType(CHARACTER_11) + .nullable(NULL) + .selectExpression("deo.optionvalueuid as optionvalueuid") + .build(), AnalyticsTableColumn.builder() .name("pestartdate") .dataType(DATE) @@ -366,7 +378,8 @@ private void populateTable( inner join analytics_rs_categorystructure dcs on dv.categoryoptioncomboid=dcs.categoryoptioncomboid \ inner join analytics_rs_categorystructure acs on dv.attributeoptioncomboid=acs.categoryoptioncomboid \ inner join analytics_rs_categoryoptioncomboname aon on dv.attributeoptioncomboid=aon.categoryoptioncomboid \ - inner join analytics_rs_categoryoptioncomboname con on dv.categoryoptioncomboid=con.categoryoptioncomboid\s""", + inner join analytics_rs_categoryoptioncomboname con on dv.categoryoptioncomboid=con.categoryoptioncomboid \ + left outer join analytics_rs_dataelementoption deo on dv.dataelementid = deo.dataelementid and dv.value = deo.optionvaluecode \s""", Map.of( "approvalSelectExpression", approvalSelectExpression, "valueExpression", valueExpression, diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/util/AnalyticsUtils.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/util/AnalyticsUtils.java index acbbae870898..d61542330763 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/util/AnalyticsUtils.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/util/AnalyticsUtils.java @@ -105,11 +105,13 @@ import org.hisp.dhis.feedback.ErrorMessage; import org.hisp.dhis.hibernate.HibernateProxyUtils; import org.hisp.dhis.indicator.Indicator; +import org.hisp.dhis.option.OptionSet; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.period.FinancialPeriodType; import org.hisp.dhis.period.Period; import org.hisp.dhis.period.PeriodType; import org.hisp.dhis.program.Program; +import org.hisp.dhis.program.ProgramDataElementDimensionItem; import org.hisp.dhis.program.ProgramIndicator; import org.hisp.dhis.program.ProgramStage; import org.hisp.dhis.system.grid.ListGrid; @@ -825,6 +827,29 @@ public static Map getDimensionMetadataItemMap( coc.getDisplayProperty(params.getDisplayProperty()), includeMetadataDetails ? coc : null)); } + + OptionSet optionSet = dataElement.getOptionSet(); + if (optionSet != null) { + map.put( + dataElement.getUid() + "." + optionSet.getUid(), + includeMetadataDetails + ? new MetadataItem( + optionSet.getName(), optionSet, new HashSet<>(optionSet.getOptions())) + : new MetadataItem(optionSet.getName())); + } + } + if (DimensionItemType.PROGRAM_DATA_ELEMENT == item.getDimensionItemType() + && item instanceof ProgramDataElementDimensionItem programDataElement) { + + OptionSet optionSet = programDataElement.getOptionSet(); + if (optionSet != null) { + map.put( + programDataElement.getDataElement().getUid() + "." + optionSet.getUid(), + includeMetadataDetails + ? new MetadataItem( + optionSet.getName(), optionSet, new HashSet<>(optionSet.getOptions())) + : new MetadataItem(optionSet.getName())); + } } } diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java index 04ba319c9e30..d8b7bbc82ea3 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/DefaultResourceTableService.java @@ -61,6 +61,7 @@ import org.hisp.dhis.resourcetable.table.DataApprovalMinLevelResourceTable; import org.hisp.dhis.resourcetable.table.DataApprovalRemapLevelResourceTable; import org.hisp.dhis.resourcetable.table.DataElementGroupSetResourceTable; +import org.hisp.dhis.resourcetable.table.DataElementOptionResourceTable; import org.hisp.dhis.resourcetable.table.DataElementResourceTable; import org.hisp.dhis.resourcetable.table.DataSetOrganisationUnitCategoryResourceTable; import org.hisp.dhis.resourcetable.table.DataSetResourceTable; @@ -162,7 +163,8 @@ private final List getResourceTables() { new DataElementResourceTable(logged, idObjectManager.getAllNoAcl(DataElement.class)), new DatePeriodResourceTable(logged, getAndValidateAvailableDataYears()), new PeriodResourceTable(logged, periodService.getAllPeriods()), - new CategoryOptionComboResourceTable(logged)); + new CategoryOptionComboResourceTable(logged), + new DataElementOptionResourceTable(logged)); } /** diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/table/DataElementOptionResourceTable.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/table/DataElementOptionResourceTable.java new file mode 100644 index 000000000000..66d7ccdb5fd4 --- /dev/null +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/resourcetable/table/DataElementOptionResourceTable.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2004-2024, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.hisp.dhis.resourcetable.table; + +import static org.hisp.dhis.commons.util.TextUtils.replace; +import static org.hisp.dhis.db.model.Table.toStaging; +import static org.hisp.dhis.system.util.SqlUtils.appendRandom; + +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.hisp.dhis.db.model.Column; +import org.hisp.dhis.db.model.DataType; +import org.hisp.dhis.db.model.Index; +import org.hisp.dhis.db.model.Logged; +import org.hisp.dhis.db.model.Table; +import org.hisp.dhis.db.model.constraint.Nullable; +import org.hisp.dhis.resourcetable.ResourceTable; +import org.hisp.dhis.resourcetable.ResourceTableType; + +@RequiredArgsConstructor +public class DataElementOptionResourceTable implements ResourceTable { + public static final String TABLE_NAME = "analytics_rs_dataelementoption"; + + private final Logged logged; + + @Override + public Table getTable() { + return new Table(toStaging(TABLE_NAME), getColumns(), getPrimaryKey(), logged); + } + + @Override + public Table getMainTable() { + return new Table(TABLE_NAME, getColumns(), getPrimaryKey(), logged); + } + + private List getColumns() { + return List.of( + new Column("dataelementid", DataType.BIGINT, Nullable.NOT_NULL), + new Column("optionsetid", DataType.BIGINT, Nullable.NOT_NULL), + new Column("optionvalueid", DataType.BIGINT, Nullable.NOT_NULL), + new Column("dataelementuid", DataType.CHARACTER_11, Nullable.NOT_NULL), + new Column("optionsetuid", DataType.CHARACTER_11, Nullable.NOT_NULL), + new Column("optionvalueuid", DataType.CHARACTER_11, Nullable.NOT_NULL), + new Column("optionvaluecode", DataType.VARCHAR_255, Nullable.NOT_NULL)); + } + + private List getPrimaryKey() { + return List.of("dataelementid", "optionsetid", "optionvalueid"); + } + + @Override + public List getIndexes() { + return List.of( + Index.builder() + .name(appendRandom("in_optionsetoptionvalue")) + .tableName(toStaging(TABLE_NAME)) + .columns(List.of("dataelementuid", "optionsetuid", "optionvalueuid")) + .build(), + Index.builder() + .name(appendRandom("in_dataelementoptioncode")) + .tableName(toStaging(TABLE_NAME)) + .columns(List.of("dataelementuid", "optionvaluecode")) + .build()); + } + + @Override + public ResourceTableType getTableType() { + return ResourceTableType.DATA_ELEMENT_CATEGORY_OPTION_COMBO; + } + + @Override + public Optional getPopulateTempTableStatement() { + String sql = + replace( + """ + insert into ${tableName} \ + (dataelementid, optionsetid, optionvalueid, dataelementuid, optionsetuid, optionvalueuid, optionvaluecode) \ + select de.dataelementid, os.optionsetid as optionsetid, ov.optionvalueid as optionvalueid, \ + de.uid as dataelementuid, os.uid as optionsetuid, ov.uid as optionvalueuid, ov.code as optionvaluecode from optionvalue ov \ + inner join optionset os on ov.optionsetid = os.optionsetid \ + inner join dataelement de on os.optionsetid = de.optionsetid;""", + "tableName", + toStaging(TABLE_NAME)); + + return Optional.of(sql); + } + + @Override + public Optional> getPopulateTempTableContent() { + return Optional.empty(); + } +} diff --git a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dimension/DefaultDimensionService.java b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dimension/DefaultDimensionService.java index 8d37336eda60..a3093eeef652 100644 --- a/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dimension/DefaultDimensionService.java +++ b/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dimension/DefaultDimensionService.java @@ -352,6 +352,13 @@ public DimensionalItemObject getDataDimensionalItemObject( String id1 = splitSafe(dimensionItem, COMPOSITE_DIM_OBJECT_ESCAPED_SEP, 1); String id2 = splitSafe(dimensionItem, COMPOSITE_DIM_OBJECT_ESCAPED_SEP, 2); + String optionSetSelectionMode = splitSafe(dimensionItem, "-", 1); + if (optionSetSelectionMode != null && id2 != null) { + id2 = splitSafe(id2, "-", 0); + } else if (optionSetSelectionMode != null && id1 != null) { + id1 = splitSafe(id1, "-", 0); + } + DataElementOperand operand; ReportingRate reportingRate; ProgramDataElementDimensionItem programDataElement; @@ -373,6 +380,11 @@ public DimensionalItemObject getDataDimensionalItemObject( != null) { return programAttribute; } + + if (!idScheme.is(IdentifiableProperty.UID) || CodeGenerator.isValidUid(id0)) { + return idObjectManager.get(DataDimensionItem.DATA_DIM_CLASSES, idScheme, id0); + } + } else if (!idScheme.is(IdentifiableProperty.UID) || CodeGenerator.isValidUid(dimensionItem)) { return idObjectManager.get(DataDimensionItem.DATA_DIM_CLASSES, idScheme, dimensionItem); }