Skip to content

Commit

Permalink
Merge pull request #315 from bcgov/feature/GRAD2-3037
Browse files Browse the repository at this point in the history
GRAD2-3037 - updates dtos to school id
  • Loading branch information
mightycox authored Feb 3, 2025
2 parents 9e913a4 + 431b457 commit 09627cb
Show file tree
Hide file tree
Showing 7 changed files with 404 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package ca.bc.gov.educ.api.grad.report.controller.v2;

import ca.bc.gov.educ.api.grad.report.model.dto.v2.ReportGradStudentData;
import ca.bc.gov.educ.api.grad.report.model.dto.StudentCredentialDistribution;
import ca.bc.gov.educ.api.grad.report.model.dto.v2.StudentSearchRequest;

import ca.bc.gov.educ.api.grad.report.model.dto.v2.YearEndReportRequest;
import ca.bc.gov.educ.api.grad.report.service.v2.CommonService;
import ca.bc.gov.educ.api.grad.report.util.*;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
Expand Down Expand Up @@ -78,4 +80,31 @@ public ResponseEntity<Integer> getReportsCount(@RequestParam String reportType,
return response.GET(commonService.countBySchoolOfRecordsAndReportType(reportContainerIds, reportType));
}
}

@PostMapping(EducGradReportApiConstants.STUDENT_FOR_SCHOOL_YEAREND_REPORT)
@PreAuthorize(PermissionsConstants.READ_GRADUATION_STUDENT_CERTIFICATES)
@Operation(summary = "Get List of students for school year end reports", description = "Get List of students for school year end reports", tags = { "School Year End Reports" })
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
public ResponseEntity<List<ReportGradStudentData>> getSchoolYearEndReportGradStudentData(@RequestBody YearEndReportRequest yearEndReportRequest) {
logger.debug("getAllStudentSchoolYearEndDistribution :");
return response.GET(commonService.getSchoolYearEndReportGradStudentData(yearEndReportRequest));
}

@GetMapping(EducGradReportApiConstants.STUDENT_FOR_SCHOOL_YEAREND_REPORT)
@PreAuthorize(PermissionsConstants.READ_GRADUATION_STUDENT_CERTIFICATES)
@Operation(summary = "Get List of students for school year end reports", description = "Get List of students for school year end reports", tags = { "School Year End Reports" })
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
public ResponseEntity<List<ReportGradStudentData>> getSchoolYearEndReportGradStudentData() {
logger.debug("getAllStudentSchoolYearEndDistribution : ");
return response.GET(commonService.getSchoolYearEndReportGradStudentData());
}

@GetMapping(EducGradReportApiConstants.STUDENT_FOR_SCHOOL_REPORT)
@PreAuthorize(PermissionsConstants.READ_GRADUATION_STUDENT_CERTIFICATES)
@Operation(summary = "Get List of students for school year end reports", description = "Get List of students for school year end reports", tags = { "School Year End Reports" })
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")})
public ResponseEntity<List<ReportGradStudentData>> getSchoolReportGradStudentData() {
logger.debug("getAllStudentSchoolYearEndDistribution : ");
return response.GET(commonService.getSchoolReportGradStudentData());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ca.bc.gov.educ.api.grad.report.model.dto.v2;

import ca.bc.gov.educ.api.grad.report.model.dto.GradCertificateTypes;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

@Data
public class ReportGradStudentData implements Serializable {

private static final long serialVersionUID = 1L;

private UUID graduationStudentRecordId;
private UUID schoolOfRecordId;
private UUID schoolAtGradId;
private UUID districtId;
private String pen;
private String firstName;
private String middleName;
private String lastName;
private String studentGrade;
private String studentStatus;
private String districtName;
private String schoolName;
private String schoolAddress1;
private String schoolAddress2;
private String schoolCity;
private String schoolProvince;
private String schoolCountry;
private String schoolPostal;
private String programCode;
private String programName;
private String programCompletionDate;
private String graduated;
private String transcriptTypeCode;
private String certificateTypeCode;
private String paperType;
private LocalDateTime updateDate;
private List<GradCertificateTypes> certificateTypes;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ca.bc.gov.educ.api.grad.report.model.dto.v2;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;
import java.util.UUID;

@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
public class YearEndReportRequest {
List<UUID> schoolIds;
List<UUID> districtIds;
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,53 @@
package ca.bc.gov.educ.api.grad.report.service.v2;

import ca.bc.gov.educ.api.grad.report.model.dto.GraduationStudentRecordSearchResult;
import ca.bc.gov.educ.api.grad.report.model.dto.v2.ReportGradStudentData;
import ca.bc.gov.educ.api.grad.report.model.dto.StudentCredentialDistribution;
import ca.bc.gov.educ.api.grad.report.model.dto.v2.StudentSearchRequest;
import ca.bc.gov.educ.api.grad.report.model.dto.v2.YearEndReportRequest;
import ca.bc.gov.educ.api.grad.report.model.entity.SchoolReportEntity;
import ca.bc.gov.educ.api.grad.report.repository.*;
import ca.bc.gov.educ.api.grad.report.repository.v2.SchoolReportRepository;
import ca.bc.gov.educ.api.grad.report.service.BaseService;
import ca.bc.gov.educ.api.grad.report.util.EducGradReportApiConstants;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.*;

@Service("commonServiceV2")
public class CommonService extends BaseService {
public class CommonService {

final GradStudentCertificatesRepository gradStudentCertificatesRepository;
final GradStudentTranscriptsRepository gradStudentTranscriptsRepository;
final GradStudentReportsRepository gradStudentReportsRepository;
final SchoolReportRepository schoolReportRepository;
final SchoolReportYearEndRepository schoolReportYearEndRepository;
final SchoolReportMonthlyRepository schoolReportMonthlyRepository;
final RESTService restService;
final EducGradReportApiConstants constants;

private static final Logger logger = LoggerFactory.getLogger(CommonService.class);

public static final int PAGE_SIZE = 1000;

@Autowired
public CommonService(GradStudentCertificatesRepository gradStudentCertificatesRepository, GradStudentTranscriptsRepository gradStudentTranscriptsRepository, GradStudentReportsRepository gradStudentReportsRepository, SchoolReportRepository schoolReportRepository, RESTService restService) {
public CommonService(GradStudentCertificatesRepository gradStudentCertificatesRepository, GradStudentTranscriptsRepository gradStudentTranscriptsRepository, GradStudentReportsRepository gradStudentReportsRepository, SchoolReportRepository schoolReportRepository, RESTService restService, SchoolReportYearEndRepository schoolReportYearEndRepository, EducGradReportApiConstants constants, SchoolReportMonthlyRepository schoolReportMonthlyRepository) {
this.gradStudentCertificatesRepository = gradStudentCertificatesRepository;
this.gradStudentTranscriptsRepository = gradStudentTranscriptsRepository;
this.gradStudentReportsRepository = gradStudentReportsRepository;
this.schoolReportRepository = schoolReportRepository;
this.schoolReportYearEndRepository = schoolReportYearEndRepository;
this.schoolReportMonthlyRepository = schoolReportMonthlyRepository;
this.restService = restService;
this.constants = constants;
}


Expand Down Expand Up @@ -118,4 +138,136 @@ public Integer countBySchoolOfRecordsAndReportType(List<UUID> schoolOfRecords, S
}
return reportsCount;
}

public List<ReportGradStudentData> getSchoolReportGradStudentData() {
PageRequest nextPage = PageRequest.of(0, PAGE_SIZE);
Page<SchoolReportEntity> students = schoolReportMonthlyRepository.findStudentForSchoolReport(nextPage);
return processReportGradStudentDataList(students, YearEndReportRequest.builder().build());
}

public List<ReportGradStudentData> getSchoolYearEndReportGradStudentData(YearEndReportRequest yearEndReportRequest) {
logger.debug("getSchoolYearEndReportGradStudentData>");
PageRequest nextPage = PageRequest.of(0, PAGE_SIZE);
Page<SchoolReportEntity> schoolStudents = schoolReportYearEndRepository.findStudentForSchoolYearEndReport(nextPage);
return processReportGradStudentDataList(schoolStudents, yearEndReportRequest);
}

public List<ReportGradStudentData> getSchoolYearEndReportGradStudentData() {
logger.debug("getSchoolYearEndReportGradStudentData>");
PageRequest nextPage = PageRequest.of(0, PAGE_SIZE);
Page<SchoolReportEntity> students = schoolReportYearEndRepository.findStudentForSchoolYearEndReport(nextPage);
return processReportGradStudentDataList(students, YearEndReportRequest.builder().build());
}

private List<ReportGradStudentData> processReportGradStudentDataList(Page<SchoolReportEntity> students, YearEndReportRequest yearEndReportRequest) {
List<ReportGradStudentData> result = new ArrayList<>();
long startTime = System.currentTimeMillis();
if(students.hasContent()) {
PageRequest nextPage;
result.addAll(getNextPageStudentsFromGradStudentApi(students, yearEndReportRequest));
final int totalNumberOfPages = students.getTotalPages();
logger.debug("Total number of pages: {}, total rows count {}", totalNumberOfPages, students.getTotalElements());

List<Callable<Object>> tasks = new ArrayList<>();

for (int i = 1; i < totalNumberOfPages; i++) {
nextPage = PageRequest.of(i, PAGE_SIZE);
UUIDPageTask pageTask = new UUIDPageTask(nextPage, yearEndReportRequest);
tasks.add(pageTask);
}

processReportGradStudentDataTasksAsync(tasks, result);
}
logger.debug("Completed in {} sec, total objects acquired {}", (System.currentTimeMillis() - startTime) / 1000, result.size());
return result;
}

private synchronized List<ReportGradStudentData> getNextPageStudentsFromGradStudentApi(Page<SchoolReportEntity> students, YearEndReportRequest yearEndReportRequest) {
List<ReportGradStudentData> result = new ArrayList<>();
List<UUID> studentGuidsInBatch = students.getContent().stream().map(SchoolReportEntity::getGraduationStudentRecordId).distinct().toList();
List<ReportGradStudentData> studentsInBatch = getReportGradStudentData(studentGuidsInBatch);

if(studentsInBatch != null && yearEndReportRequest.getDistrictIds() != null && !yearEndReportRequest.getDistrictIds().isEmpty()) {
studentsInBatch.removeIf(st -> (!yearEndReportRequest.getDistrictIds().contains(st.getDistrictId())));
}
if(studentsInBatch != null && yearEndReportRequest.getSchoolIds() != null && !yearEndReportRequest.getSchoolIds().isEmpty()) {
studentsInBatch.removeIf(st -> (!yearEndReportRequest.getSchoolIds().contains(st.getSchoolOfRecordId())));
}

for(SchoolReportEntity e: students.getContent()) {
String paperType = e.getPaperType();
String certificateTypeCode = e.getCertificateTypeCode(); //either transcript or certificate codes
ReportGradStudentData s = getReportGradStudentDataByGraduationStudentRecordIdFromList(e.getGraduationStudentRecordId(), studentsInBatch);
if(s != null) {
ReportGradStudentData dataResult = SerializationUtils.clone(s);
dataResult.setPaperType(paperType);
if ("YED4".equalsIgnoreCase(paperType)) {
dataResult.setTranscriptTypeCode(certificateTypeCode);
} else {
dataResult.setCertificateTypeCode(certificateTypeCode);
}
if("YED4".equalsIgnoreCase(paperType) && "CUR".equalsIgnoreCase(s.getStudentStatus())) {
result.add(dataResult);
}
if (!"YED4".equalsIgnoreCase(paperType)) {
result.add(dataResult);
}
}
}
return result;
}

private synchronized ReportGradStudentData getReportGradStudentDataByGraduationStudentRecordIdFromList(UUID id, List<ReportGradStudentData> studentsInBatch) {
for(ReportGradStudentData s: studentsInBatch) {
if(s.getGraduationStudentRecordId().equals(id)) {
return s;
}
}
return null;
}

private synchronized List<ReportGradStudentData> getReportGradStudentData(List<UUID> studentGuids) {
return this.restService.postForList(constants.getStudentsForSchoolDistribution(), studentGuids, ReportGradStudentData.class);
}

private void processReportGradStudentDataTasksAsync(List<Callable<Object>> tasks, List<ReportGradStudentData> result) {
if(tasks.isEmpty()) return;
List<Future<Object>> executionResult;
ExecutorService executorService = Executors.newWorkStealingPool();
try {
executionResult = executorService.invokeAll(tasks);
for (Future<?> f : executionResult) {
Object o = f.get();
if(o instanceof Pair<?, ?>) {
Pair<PageRequest, List<ReportGradStudentData>> taskResult = (Pair<PageRequest, List<ReportGradStudentData>>) o;
result.addAll(taskResult.getRight());
logger.debug("Page {} processed successfully", taskResult.getLeft().getPageNumber());
} else {
logger.error("Error during the task execution: {}", f.get());
}
}
} catch (InterruptedException | ExecutionException ex) {
logger.error("Multithreading error during the task execution: {}", ex.getLocalizedMessage());
Thread.currentThread().interrupt();
} finally {
executorService.shutdown();
}
}

class UUIDPageTask implements Callable<Object> {

private final PageRequest pageRequest;
private final YearEndReportRequest yearEndReportRequest;

public UUIDPageTask(PageRequest pageRequest, YearEndReportRequest yearEndReportRequest) {
this.pageRequest = pageRequest;
this.yearEndReportRequest = yearEndReportRequest;
}

@Override
public Object call() throws Exception {
Page<SchoolReportEntity> students = schoolReportYearEndRepository.findStudentForSchoolYearEndReport(pageRequest);
return Pair.of(pageRequest, getNextPageStudentsFromGradStudentApi(students, yearEndReportRequest));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import reactor.util.retry.Retry;

import java.time.Duration;
import java.util.List;

@Service
public class RESTService {
Expand Down Expand Up @@ -59,6 +60,28 @@ public <T> T get(String url, Class<T> clazz) {
return obj;
}

public <T> List<T> postForList(String url, Object body, Class<T> clazz) {
try {
return graduationServiceWebClient.post()
.uri(url)
.headers(h -> h.set(EducGradReportApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.body(BodyInserters.fromValue(body))
.retrieve()
.onStatus(HttpStatusCode::is5xxServerError,
clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value())))
.bodyToFlux(clazz)
.collectList()
.retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
.filter(ServiceException.class::isInstance)
.onRetryExhaustedThrow((retryBackoffSpec, retrySignal) -> {
throw new ServiceException(getErrorMessage(url, SERVICE_FAILED_ERROR), HttpStatus.SERVICE_UNAVAILABLE.value());
}))
.block();
} catch (Exception e) {
throw new ServiceException(getErrorMessage(url, e.getLocalizedMessage()), HttpStatus.SERVICE_UNAVAILABLE.value(), e);
}
}

public <T> T post(String url, Object body, Class<T> clazz) {
T obj;
try {
Expand Down
Loading

0 comments on commit 09627cb

Please sign in to comment.