Skip to content

Commit 63e2634

Browse files
committed
youthPRPHeadcountPerSchool report
1 parent 039446b commit 63e2634

File tree

7 files changed

+1149
-1
lines changed

7 files changed

+1149
-1
lines changed

api/src/main/java/ca/bc/gov/educ/studentdatacollection/api/constants/v1/DistrictReportTypeCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public enum DistrictReportTypeCode {
3333
ALL_STUDENT_ELL_DIS_CSV("ALL_STUDENT_ELL_DIS_CSV"),
3434
ALL_STUDENT_REFUGEE_DIS_CSV("ALL_STUDENT_REFUGEE_DIS_CSV"),
3535
DIS_ZERO_FTE_SUMMARY("DIS_ZERO_FTE_SUMMARY"),
36+
DIS_PRP_OR_YOUTH_SUMMARY("DIS_PRP_OR_YOUTH_SUMMARY"),
3637
DIS_BAND_RESIDENCE_HEADCOUNT("DIS_BAND_RESIDENCE_HEADCOUNT"),
3738
DIS_BAND_RESIDENCE_HEADCOUNT_PER_SCHOOL("DIS_BAND_RESIDENCE_HEADCOUNT_PER_SCHOOL"),
3839
;

api/src/main/java/ca/bc/gov/educ/studentdatacollection/api/controller/v1/ReportGenerationController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class ReportGenerationController implements ReportGenerationEndpoint {
5555
private final SdcSchoolCollectionStudentSearchService sdcSchoolCollectionStudentSearchService;
5656
private final SdcSchoolCollectionHistoryService sdcSchoolCollectionHistoryService;
5757
private final ZeroFTEHeadCountReportService zeroFTEHeadCountReportService;
58+
private final PRPorYouthHeadcountReportService prpOrYouthHeadcountReportService;
5859
private static final SdcSchoolCollectionStudentMapper sdcSchoolCollectionStudentMapper = SdcSchoolCollectionStudentMapper.mapper;
5960

6061
@Override
@@ -119,6 +120,7 @@ public DownloadableReportResponse generateSDCDistrictReport(UUID sdcDistrictColl
119120
case ALL_STUDENT_ELL_DIS_CSV -> allStudentLightCollectionGenerateCsvService.generateEllFromSdcDistrictCollectionID(sdcDistrictCollectionID);
120121
case ALL_STUDENT_REFUGEE_DIS_CSV -> allStudentLightCollectionGenerateCsvService.generateRefugeeFromSdcDistrictCollectionID(sdcDistrictCollectionID);
121122
case DIS_ZERO_FTE_SUMMARY -> zeroFTEHeadCountReportService.generateZeroFTEHeadcountReport(sdcDistrictCollectionID);
123+
case DIS_PRP_OR_YOUTH_SUMMARY -> prpOrYouthHeadcountReportService.generatePerSchoolReport(sdcDistrictCollectionID);
122124
case DIS_BAND_RESIDENCE_HEADCOUNT -> bandOfResidenceHeadcountReportService.generateDistrictBandOfResidenceReport(sdcDistrictCollectionID);
123125
case DIS_BAND_RESIDENCE_HEADCOUNT_PER_SCHOOL -> bandOfResidenceHeadcountPerSchoolReportService.generateBandOfResidenceHeadcountPerSchoolReport(sdcDistrictCollectionID);
124126
default -> new DownloadableReportResponse();

api/src/main/java/ca/bc/gov/educ/studentdatacollection/api/reports/BaseReportGenerationService.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ca.bc.gov.educ.studentdatacollection.api.reports;
22

3+
import ca.bc.gov.educ.studentdatacollection.api.constants.v1.FacilityTypeCodes;
34
import ca.bc.gov.educ.studentdatacollection.api.constants.v1.SchoolCategoryCodes;
45
import ca.bc.gov.educ.studentdatacollection.api.exception.EntityNotFoundException;
56
import ca.bc.gov.educ.studentdatacollection.api.exception.StudentDataCollectionAPIRuntimeException;
@@ -164,7 +165,18 @@ public List<SchoolTombstone> getAllSchoolTombstones(UUID sdcDistrictCollectionID
164165

165166
return allSchoolCollections.stream()
166167
.map(schoolCollection -> restUtils.getSchoolBySchoolID(schoolCollection.getSchoolID().toString())
167-
.orElseThrow(() -> new EntityNotFoundException(SdcSchoolCollection.class, "SchoolID", schoolCollection.getSchoolID().toString())))
168+
.orElseThrow(() -> new EntityNotFoundException(SdcSchoolCollection.class, "SchoolID", schoolCollection.getSchoolID().toString())))
169+
.toList();
170+
}
171+
172+
public List<SchoolTombstone> getAllSchoolTombstonesYouthPRP(UUID sdcDistrictCollectionID) {
173+
List<SdcSchoolCollectionEntity> allSchoolCollections = sdcSchoolCollectionRepository.findAllBySdcDistrictCollectionID(sdcDistrictCollectionID);
174+
var prpAndYouthSchools = Arrays.asList(FacilityTypeCodes.SHORT_PRP.getCode(), FacilityTypeCodes.LONG_PRP.getCode(), FacilityTypeCodes.YOUTH.getCode());
175+
176+
return allSchoolCollections.stream()
177+
.map(schoolCollection -> restUtils.getSchoolBySchoolID(schoolCollection.getSchoolID().toString())
178+
.orElseThrow(() -> new EntityNotFoundException(SdcSchoolCollection.class, "SchoolID", schoolCollection.getSchoolID().toString())))
179+
.filter(school -> prpAndYouthSchools.contains(school.getFacilityTypeCode()))
168180
.toList();
169181
}
170182
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package ca.bc.gov.educ.studentdatacollection.api.reports;
2+
3+
import ca.bc.gov.educ.studentdatacollection.api.constants.v1.DistrictReportTypeCode;
4+
import ca.bc.gov.educ.studentdatacollection.api.constants.v1.SchoolGradeCodes;
5+
import ca.bc.gov.educ.studentdatacollection.api.exception.EntityNotFoundException;
6+
import ca.bc.gov.educ.studentdatacollection.api.exception.StudentDataCollectionAPIRuntimeException;
7+
import ca.bc.gov.educ.studentdatacollection.api.model.v1.SdcDistrictCollectionEntity;
8+
import ca.bc.gov.educ.studentdatacollection.api.properties.ApplicationProperties;
9+
import ca.bc.gov.educ.studentdatacollection.api.repository.v1.SdcDistrictCollectionRepository;
10+
import ca.bc.gov.educ.studentdatacollection.api.repository.v1.SdcSchoolCollectionRepository;
11+
import ca.bc.gov.educ.studentdatacollection.api.repository.v1.SdcSchoolCollectionStudentRepository;
12+
import ca.bc.gov.educ.studentdatacollection.api.rest.RestUtils;
13+
import ca.bc.gov.educ.studentdatacollection.api.struct.external.institute.v1.SchoolTombstone;
14+
import ca.bc.gov.educ.studentdatacollection.api.struct.v1.headcounts.PRPorYouthHeadcountResult;
15+
import ca.bc.gov.educ.studentdatacollection.api.struct.v1.reports.*;
16+
import com.fasterxml.jackson.core.JsonProcessingException;
17+
import jakarta.annotation.PostConstruct;
18+
import lombok.extern.slf4j.Slf4j;
19+
import net.sf.jasperreports.engine.JRException;
20+
import net.sf.jasperreports.engine.JasperCompileManager;
21+
import net.sf.jasperreports.engine.JasperReport;
22+
import org.springframework.stereotype.Service;
23+
24+
import java.io.InputStream;
25+
import java.util.*;
26+
27+
@Service
28+
@Slf4j
29+
public class PRPorYouthHeadcountReportService extends BaseReportGenerationService<PRPorYouthHeadcountResult>{
30+
31+
protected static final String ALLPRPORYOUTH = "allPRPorYouth";
32+
private final SdcDistrictCollectionRepository sdcDistrictCollectionRepository;
33+
private final SdcSchoolCollectionStudentRepository sdcSchoolCollectionStudentRepository;
34+
private final RestUtils restUtils;
35+
private List<SchoolTombstone> allSchoolsTombstones;
36+
private List<PRPorYouthHeadcountResult> prpOrYouthHeadcountList;
37+
private JasperReport prpOrYouthHeadcountReport;
38+
39+
public PRPorYouthHeadcountReportService(SdcDistrictCollectionRepository sdcDistrictCollectionRepository, SdcSchoolCollectionStudentRepository sdcSchoolCollectionStudentRepository, SdcSchoolCollectionRepository sdcSchoolCollectionRepository , RestUtils restUtils) {
40+
super(restUtils, sdcSchoolCollectionRepository);
41+
this.sdcDistrictCollectionRepository = sdcDistrictCollectionRepository;
42+
this.sdcSchoolCollectionStudentRepository = sdcSchoolCollectionStudentRepository;
43+
this.restUtils = restUtils;
44+
}
45+
46+
@PostConstruct
47+
public void init() {
48+
ApplicationProperties.bgTask.execute(this::initialize);
49+
}
50+
51+
private void initialize() {
52+
this.compileJasperReports();
53+
}
54+
55+
private void compileJasperReports(){
56+
try {
57+
InputStream inputYouthPRPHeadcount = getClass().getResourceAsStream("/reports/youthPrpHeadcountsPerSchool.jrxml");
58+
prpOrYouthHeadcountReport = JasperCompileManager.compileReport(inputYouthPRPHeadcount);
59+
} catch (JRException e) {
60+
throw new StudentDataCollectionAPIRuntimeException("Compiling Jasper reports has failed :: " + e.getMessage());
61+
}
62+
}
63+
64+
public DownloadableReportResponse generatePerSchoolReport(UUID sdcDistrictCollectionID){
65+
try {
66+
Optional<SdcDistrictCollectionEntity> sdcDistrictCollectionEntityOptional = sdcDistrictCollectionRepository.findById(sdcDistrictCollectionID);
67+
SdcDistrictCollectionEntity sdcDistrictCollectionEntity = sdcDistrictCollectionEntityOptional.orElseThrow(() ->
68+
new EntityNotFoundException(SdcDistrictCollectionEntity.class, "sdcDistrictCollectionID", sdcDistrictCollectionID.toString()));
69+
70+
this.allSchoolsTombstones = getAllSchoolTombstonesYouthPRP(sdcDistrictCollectionID);
71+
List<UUID> youthPRPSchoolIDs = this.allSchoolsTombstones.stream().map(SchoolTombstone::getSchoolId).map(UUID::fromString).toList();
72+
var studentList = sdcSchoolCollectionStudentRepository.getYouthPRPHeadcountsBySdcDistrictCollectionIdGroupBySchoolId(sdcDistrictCollectionEntity.getSdcDistrictCollectionID(), youthPRPSchoolIDs);
73+
this.prpOrYouthHeadcountList = studentList;
74+
return generateJasperReport(convertToYouthPRPJSONStringDistrict(studentList, sdcDistrictCollectionEntity), prpOrYouthHeadcountReport, DistrictReportTypeCode.DIS_PRP_OR_YOUTH_SUMMARY.getCode());
75+
} catch (JsonProcessingException e) {
76+
log.error("Exception occurred while writing PDF report for dis prp youth :: " + e.getMessage());
77+
throw new StudentDataCollectionAPIRuntimeException("Exception occurred while writing PDF report for dis prp youth :: " + e.getMessage());
78+
}
79+
}
80+
81+
private String convertToYouthPRPJSONStringDistrict(List<PRPorYouthHeadcountResult> mappedResults, SdcDistrictCollectionEntity sdcDistrictCollection) throws JsonProcessingException {
82+
HeadcountNode mainNode = new HeadcountNode();
83+
HeadcountReportNode reportNode = new HeadcountReportNode();
84+
setReportTombstoneValuesDis(sdcDistrictCollection, reportNode);
85+
86+
var nodeMap = generateNodeMap(Boolean.TRUE);
87+
88+
mappedResults.forEach(PRPorYouthHeadcountResult -> setRowValues(nodeMap, PRPorYouthHeadcountResult));
89+
90+
reportNode.setPrograms(nodeMap.values().stream().sorted(Comparator.comparing(o -> Integer.parseInt(o.getSequence()))).toList());
91+
mainNode.setReport(reportNode);
92+
return objectWriter.writeValueAsString(mainNode);
93+
}
94+
95+
protected HashMap<String, HeadcountChildNode> generateNodeMap(boolean includeKH) {
96+
HashMap<String, HeadcountChildNode> nodeMap = new HashMap<>();
97+
Set<String> includedSchoolIDs = new HashSet<>();
98+
addValuesForSectionToMap(nodeMap, ALLPRPORYOUTH, "All Youth Or PRP Students", "00", includeKH);
99+
int sequencePrefix = 10;
100+
101+
if (prpOrYouthHeadcountList != null) {
102+
for (PRPorYouthHeadcountResult result : prpOrYouthHeadcountList) {
103+
String schoolID = result.getSchoolID();
104+
Optional<SchoolTombstone> schoolOptional = restUtils.getSchoolBySchoolID(schoolID);
105+
int finalSequencePrefix = sequencePrefix;
106+
schoolOptional.ifPresent(school -> {
107+
includedSchoolIDs.add(school.getSchoolId());
108+
String schoolTitle = school.getMincode() + " - " + school.getDisplayName();
109+
addValuesForSectionToMap(nodeMap, schoolID, schoolTitle, String.valueOf(finalSequencePrefix), includeKH);
110+
});
111+
sequencePrefix += 10;
112+
}
113+
}
114+
115+
for (SchoolTombstone school : allSchoolsTombstones) {
116+
if (!includedSchoolIDs.contains(school.getSchoolId())) {
117+
String schoolTitle = school.getMincode() + " - " + school.getDisplayName();
118+
addValuesForSectionToMap(nodeMap, school.getSchoolId(), schoolTitle, String.valueOf(sequencePrefix), includeKH);
119+
sequencePrefix += 10;
120+
}
121+
}
122+
123+
return nodeMap;
124+
}
125+
126+
private void addValuesForSectionToMap(HashMap<String, HeadcountChildNode> nodeMap, String sectionPrefix, String sectionTitle, String sequencePrefix, boolean includeKH){
127+
if (Objects.equals(sectionPrefix, ALLPRPORYOUTH)) {
128+
nodeMap.put(sectionPrefix, new GradeHeadcountChildNode(sectionTitle, "true", sequencePrefix + "0", false, true, true, includeKH));
129+
} else {
130+
nodeMap.put(sectionPrefix, new GradeHeadcountChildNode(sectionTitle, "false", sequencePrefix + "0", false, true, true, includeKH));
131+
}
132+
}
133+
134+
protected void setRowValues(HashMap<String, HeadcountChildNode> nodeMap, PRPorYouthHeadcountResult gradeResult) {
135+
Optional<SchoolGradeCodes> optionalCode = SchoolGradeCodes.findByValue(gradeResult.getEnrolledGradeCode());
136+
var code = optionalCode.orElseThrow(() ->
137+
new EntityNotFoundException(SchoolGradeCodes.class, "Grade Value", gradeResult.getEnrolledGradeCode()));
138+
139+
GradeHeadcountChildNode allYouthPRPNode = (GradeHeadcountChildNode)nodeMap.get(ALLPRPORYOUTH);
140+
if (allYouthPRPNode.getValueForGrade(code) == null) {
141+
allYouthPRPNode.setValueForGrade(code, "0");
142+
}
143+
144+
String schoolID = gradeResult.getSchoolID();
145+
if (nodeMap.containsKey(schoolID)) {
146+
((GradeHeadcountChildNode)nodeMap.get(schoolID)).setValueForGrade(code, gradeResult.getYouthPRPTotals());
147+
} else {
148+
log.warn("School ID {} not found in node map", schoolID);
149+
}
150+
151+
int currentTotal = Integer.parseInt(gradeResult.getYouthPRPTotals());
152+
int accumulatedTotal = Integer.parseInt(allYouthPRPNode.getValueForGrade(code));
153+
allYouthPRPNode.setValueForGrade(code, String.valueOf(accumulatedTotal + currentTotal));
154+
}
155+
}

api/src/main/java/ca/bc/gov/educ/studentdatacollection/api/repository/v1/SdcSchoolCollectionStudentRepository.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,18 @@ long countAllByAssignedStudentIdInAndSdcSchoolCollection_SdcSchoolCollectionIDIn
384384
"ORDER BY s.sdcSchoolCollection.schoolID, s.enrolledGradeCode")
385385
List<FrenchCombinedHeadcountResult> getFrenchHeadcountsBySdcDistrictCollectionIdGroupBySchoolId(@Param("sdcDistrictCollectionID") UUID sdcDistrictCollectionID);
386386

387+
@Query("SELECT " +
388+
"s.enrolledGradeCode AS enrolledGradeCode, " +
389+
"s.sdcSchoolCollection.schoolID AS schoolID, " +
390+
"COUNT(DISTINCT s.sdcSchoolCollectionStudentID) AS youthPRPTotals " +
391+
"FROM SdcSchoolCollectionStudentEntity s " +
392+
"WHERE s.sdcSchoolCollection.sdcDistrictCollectionID = :sdcDistrictCollectionID " +
393+
"AND s.sdcSchoolCollection.schoolID IN :youthPRPSchoolIDs " +
394+
"AND s.sdcSchoolCollectionStudentStatusCode NOT IN ('ERROR', 'DELETED') " +
395+
"GROUP BY s.sdcSchoolCollection.schoolID, s.enrolledGradeCode " +
396+
"ORDER BY s.sdcSchoolCollection.schoolID, s.enrolledGradeCode")
397+
List<PRPorYouthHeadcountResult> getYouthPRPHeadcountsBySdcDistrictCollectionIdGroupBySchoolId(@Param("sdcDistrictCollectionID") UUID sdcDistrictCollectionID, @Param("youthPRPSchoolIDs") List<UUID> youthPRPSchoolIDs);
398+
387399
@Query("SELECT " +
388400
"s.enrolledGradeCode AS enrolledGradeCode, " +
389401
"COUNT(DISTINCT CASE WHEN s.isSchoolAged = true AND s.frenchProgramNonEligReasonCode IS NULL AND ep.enrolledProgramCode = '05' THEN s.sdcSchoolCollectionStudentID END) AS schoolAgedFrancophone, " +
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package ca.bc.gov.educ.studentdatacollection.api.struct.v1.headcounts;
2+
3+
/**
4+
* This interface is used as data mapper for PRP or Youth Summary report.
5+
*/
6+
public interface PRPorYouthHeadcountResult extends HeadcountResult{
7+
String getYouthPRPTotals();
8+
}

0 commit comments

Comments
 (0)