Skip to content
4 changes: 3 additions & 1 deletion src/main/java/org/hisp/dhis/BaseDhis2.java
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ public class BaseDhis2 {
String.format(
"""
%1$s,programType,trackedEntityType[%2$s],categoryCombo[%1$s,categories[%3$s]],\
programStages[%1$s,programStageDataElements[%4$s]],\
programStages[%1$s,\
programStageDataElements[%4$s],\
programStageSections[%1$s,formName,sortOrder,programStage[%1$s],dataElements[%1$s],programIndicators[%1$s]]],\
programTrackedEntityAttributes[id,code,name,trackedEntityAttribute[%5$s]]""",
NAME_FIELDS,
TRACKED_ENTITY_TYPE_FIELDS,
Expand Down
36 changes: 21 additions & 15 deletions src/main/java/org/hisp/dhis/model/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@
package org.hisp.dhis.model;

import static org.hisp.dhis.util.CollectionUtils.notEmpty;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hisp.dhis.model.trackedentity.ProgramTrackedEntityAttribute;
import org.hisp.dhis.model.trackedentity.TrackedEntityAttribute;
import org.hisp.dhis.model.trackedentity.TrackedEntityType;
import org.hisp.dhis.model.trackedentity.TrackedEntityTypeAttribute;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
Expand Down Expand Up @@ -79,7 +78,7 @@ public List<TrackedEntityAttribute> getTrackedEntityTypeAttributes() {

return trackedEntityType.getTrackedEntityTypeAttributes().stream()
.map(TrackedEntityTypeAttribute::getTrackedEntityAttribute)
.collect(Collectors.toUnmodifiableList());
.toList();
}

/**
Expand Down Expand Up @@ -108,7 +107,7 @@ public List<TrackedEntityAttribute> getNonConfidentialTrackedEntityTypeAttribute
public List<TrackedEntityAttribute> getTrackedEntityAttributes() {
return programTrackedEntityAttributes.stream()
.map(ProgramTrackedEntityAttribute::getTrackedEntityAttribute)
.collect(Collectors.toUnmodifiableList());
.toList();
}

/**
Expand All @@ -121,7 +120,7 @@ public List<TrackedEntityAttribute> getNonConfidentialTrackedEntityAttributes()
return programTrackedEntityAttributes.stream()
.map(ProgramTrackedEntityAttribute::getTrackedEntityAttribute)
.filter(tea -> !tea.isConfidentialNullSafe())
.collect(Collectors.toUnmodifiableList());
.toList();
}

/**
Expand All @@ -131,10 +130,7 @@ public List<TrackedEntityAttribute> getNonConfidentialTrackedEntityAttributes()
*/
@JsonIgnore
public List<DataElement> getDataElements() {
return programStages.stream()
.flatMap(ps -> ps.getDataElements().stream())
.distinct()
.collect(Collectors.toUnmodifiableList());
return programStages.stream().flatMap(ps -> ps.getDataElements().stream()).distinct().toList();
}

/**
Expand All @@ -147,7 +143,7 @@ public List<DataElement> getAnalyticsDataElements() {
return programStages.stream()
.flatMap(ps -> ps.getAnalyticsDataElements().stream())
.distinct()
.collect(Collectors.toUnmodifiableList());
.toList();
}

/**
Expand All @@ -162,7 +158,17 @@ public List<DataElement> getAnalyticsDataElementsWithLegendSet() {
.flatMap(ps -> ps.getAnalyticsDataElements().stream())
.filter(de -> notEmpty(de.getLegendSets()) && de.getValueType().isNumeric())
.distinct()
.collect(Collectors.toUnmodifiableList());
.toList();
}

/**
* Returns program stage sections which are part of the stages of this program.
*
* @return an immutable set of {@link ProgramStageSection}.
*/
@JsonIgnore
public List<ProgramStageSection> getProgramStageSections() {
return programStages.stream().flatMap(ps -> ps.getProgramStageSections().stream()).toList();
}

/**
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/hisp/dhis/model/ProgramStage.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
public class ProgramStage extends NameableObject {
@JsonProperty private List<ProgramStageDataElement> programStageDataElements = new ArrayList<>();

@JsonProperty private List<ProgramStageSection> programStageSections = new ArrayList<>();

public ProgramStage(String id, String name) {
this.id = id;
this.name = name;
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/org/hisp/dhis/model/ProgramStageSection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2004-2025, 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.model;

import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class ProgramStageSection extends NameableObject {
@JsonProperty private String formName;

@JsonProperty private Integer sortOrder;

@JsonProperty private ProgramStage programStage;

@JsonProperty private List<DataElement> dataElements = new ArrayList<>();

@JsonProperty private List<ProgramIndicator> programIndicators = new ArrayList<>();
}
53 changes: 50 additions & 3 deletions src/test/java/org/hisp/dhis/ProgramApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
import static org.hisp.dhis.support.Assertions.assertNotEmpty;
import static org.hisp.dhis.support.Assertions.assertSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.util.List;
import org.hisp.dhis.model.DataElement;
import org.hisp.dhis.model.Program;
import org.hisp.dhis.model.ProgramIndicator;
import org.hisp.dhis.model.ProgramStage;
import org.hisp.dhis.model.ProgramStageDataElement;
import org.hisp.dhis.model.ProgramStageSection;
import org.hisp.dhis.model.ProgramType;
import org.hisp.dhis.model.trackedentity.ProgramTrackedEntityAttribute;
import org.hisp.dhis.model.trackedentity.TrackedEntityAttribute;
Expand All @@ -53,15 +55,15 @@
@Tag(TestTags.INTEGRATION)
public class ProgramApiTest {
@Test
void testGetProgram() {
void testGetProgramChildProgram() {
Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG);

Program pr = dhis2.getProgram("IpHINAT79UW");

assertNotNull(pr);
assertEquals("IpHINAT79UW", pr.getId());
assertNotBlank(pr.getShortName());
assertNotBlank(pr.getName());
assertNotBlank(pr.getShortName());
assertNotNull(pr.getCreated());
assertNotNull(pr.getLastUpdated());
assertEquals(ProgramType.WITH_REGISTRATION, pr.getProgramType());
Expand Down Expand Up @@ -130,6 +132,51 @@ void testGetProgram() {
assertNotEmpty(pr.getAnalyticsDataElements());
}

@Test
void testGetProgramInpatientCase() {
Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG);

Program pr = dhis2.getProgram("eBAyeGv0exc");

assertNotNull(pr);
assertEquals("eBAyeGv0exc", pr.getId());
assertNotBlank(pr.getName());
assertNotBlank(pr.getShortName());
assertNotNull(pr.getProgramStages());
assertNotNull(pr.getProgramStageSections());
assertFalse(pr.getProgramStageSections().isEmpty());

ProgramStage ps = pr.getProgramStages().get(0);

assertNotNull(ps);
assertEquals("Zj7UnCAulEk", ps.getId());
assertNotNull(ps.getProgramStageSections());

ProgramStageSection pss = ps.getProgramStageSections().get(0);

assertNotNull(pss);
assertEquals("d7ZILSbPgYh", pss.getId());
assertNotBlank(pss.getName());
assertNotBlank(pss.getDescription());
assertNotNull(pss.getSortOrder());
assertNotNull(pss.getDataElements());
assertNotNull(pss.getProgramIndicators());

DataElement de = pss.getDataElements().get(0);

assertNotNull(de);
assertEquals("oZg33kd9taw", de.getId());
assertNotBlank(de.getName());
assertNotBlank(de.getShortName());

ProgramIndicator pi = pss.getProgramIndicators().get(0);

assertNotNull(pi);
assertEquals("x7PaHGvgWY2", pi.getId());
assertNotBlank(pi.getName());
assertNotBlank(pi.getShortName());
}

@Test
void testGetPrograms() {
Dhis2 dhis2 = new Dhis2(TestFixture.DEFAULT_CONFIG);
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/org/hisp/dhis/util/ConfigUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

public class ConfigUtilsTest {
@Test
void testGetAsListA() {
void testGetAsList() {
List<String> expected =
List.of("http://localhost", "http://localhost:3000", "https://localhost:3000");

Expand Down