diff --git a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/RealWorldTestingReportController.java b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/RealWorldTestingReportController.java index 3698cf54f5..6189862154 100644 --- a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/RealWorldTestingReportController.java +++ b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/RealWorldTestingReportController.java @@ -9,7 +9,8 @@ import org.springframework.web.bind.annotation.RestController; import gov.healthit.chpl.report.ReportDataManager; -import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryReport; +import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByAcbReport; +import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByDeveloperReport; import gov.healthit.chpl.util.LogMethodUsage; import gov.healthit.chpl.util.SwaggerSecurityRequirement; import io.swagger.v3.oas.annotations.Operation; @@ -30,25 +31,36 @@ public RealWorldTestingReportController(ReportDataManager reportDataManager) { this.reportDataManager = reportDataManager; } - @Operation(summary = "Retrieves the data used to generate the Real World Testing Plans report.", - description = "Retrieves the data used to generate the Real World Testing Plans report.", + @Operation(summary = "Retrieves the data used to generate the Real World Testing Plans summary by ONC-ACB report.", + description = "Retrieves the data used to generate the Real World Testing Plans summary by ONC-ACB report.", security = { @SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY) }) @LogMethodUsage @RequestMapping(value = "/plans", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - public @ResponseBody List getRealWorldTestingPlanReports() { - return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingPlanSummaryReports(); + public @ResponseBody List getRealWorldTestingPlanSummaryByAcbReports() { + return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingPlanSummaryByAcbReports(); } - @Operation(summary = "Retrieves the data used to generate the Real World Testing Results report.", - description = "Retrieves the data used to generate the Real World Testing Results report.", + @Operation(summary = "Retrieves the data used to generate the Real World Testing Results summary by ONC-ACB report.", + description = "Retrieves the data used to generate the Real World Testing Results summary by ONC-ACB report.", security = { @SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY) }) @LogMethodUsage @RequestMapping(value = "/results", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - public @ResponseBody List getRealWorldTestingResultsReports() { - return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingResultsSummaryReports(); + public @ResponseBody List getRealWorldTestingResultsSummaryByAcbReports() { + return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingResultsSummaryByAcbReports(); + } + + @Operation(summary = "Retrieves the data used to generate the Real World Testing Results summary by Developer report.", + description = "Retrieves the data used to generate the Real World Testing Results summary by Developer report.", + security = { + @SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY) + }) + @LogMethodUsage + @RequestMapping(value = "/results-summary-by-developer", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + public @ResponseBody List getRealWorldTestingResultsSummaryByDeveloperReports() { + return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingResultsSummaryByDeveloperReports(); } } diff --git a/chpl/chpl-resources/src/main/resources/environment.properties b/chpl/chpl-resources/src/main/resources/environment.properties index 3fb8355a97..55b976cd47 100644 --- a/chpl/chpl-resources/src/main/resources/environment.properties +++ b/chpl/chpl-resources/src/main/resources/environment.properties @@ -230,6 +230,8 @@ rwtPlanDueDate=12/15 rwtResultsStartDayOfYear=01/01 # Date when the Results is considered late - Format is MM/DD rwtResultsDueDate=03/15 +#Date when we stop gathering summary data about RWT results submissions, used in charts +rwtResultsDataGatheringEndDate=07/15 #Criteria that make a listing eligible for rwt realWorldTestingCriteriaKeys={2021: 'criterion.170_315_b_1_old,\ diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/realworldtesting/manager/RealWorldTestingReportService.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/realworldtesting/manager/RealWorldTestingReportService.java index 2f16485669..f56c77c553 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/realworldtesting/manager/RealWorldTestingReportService.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/realworldtesting/manager/RealWorldTestingReportService.java @@ -294,6 +294,13 @@ public LocalDate getResultsLateDate(Integer rwtEligYear) { return LocalDate.parse(mmddyyyy, formatter); } + public LocalDate getResultsDataGatheringStopDate(Integer rwtEligYear) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy"); + String mmdd = env.getProperty("rwtResultsDataGatheringEndDate"); + String mmddyyyy = mmdd + "/" + String.valueOf(rwtEligYear + 1); + return LocalDate.parse(mmddyyyy, formatter); + } + private boolean isWithdrawn(RealWorldTestingReport record) { String statusName = record.getCurrentStatus(); return withdrawnStatuses.stream() diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportEntity.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryByAcbReportEntity.java similarity index 87% rename from chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportEntity.java rename to chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryByAcbReportEntity.java index 09313c9a26..c4dff67e49 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportEntity.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryByAcbReportEntity.java @@ -27,8 +27,8 @@ @AllArgsConstructor @NoArgsConstructor @Entity -@Table(name = "real_world_testing_plan_summary_report") -public class RealWorldTestingPlanSummaryReportEntity extends EntityAudit { +@Table(name = "real_world_testing_plan_summary_by_acb_report") +public class RealWorldTestingPlanSummaryByAcbReportEntity extends EntityAudit { private static final long serialVersionUID = -1208758504334058893L; @Id @@ -52,8 +52,8 @@ public class RealWorldTestingPlanSummaryReportEntity extends EntityAudit { @Column(name = "requires_check_count") private Long requiresCheckCount; - public RealWorldTestingSummaryReport toDomain() { - return RealWorldTestingSummaryReport.builder() + public RealWorldTestingSummaryByAcbReport toDomain() { + return RealWorldTestingSummaryByAcbReport.builder() .id(id) .realWorldTestingYear(realWorldTestingYear) .certificationBody(certificationBody.toDomain()) diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportDao.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportDao.java index ad4c1ab6ea..3e5e0e7a49 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportDao.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingPlanSummaryReportDao.java @@ -14,11 +14,11 @@ @Component public class RealWorldTestingPlanSummaryReportDao extends BaseDAOImpl { - public void save(RealWorldTestingSummaryReport realWorldTestingSummaryReport) throws EntityRetrievalException { - RealWorldTestingPlanSummaryReportEntity entity = getEntityByCheckedDateAndAcb(realWorldTestingSummaryReport.getCheckedDate(), + public void save(RealWorldTestingSummaryByAcbReport realWorldTestingSummaryReport) throws EntityRetrievalException { + RealWorldTestingPlanSummaryByAcbReportEntity entity = getEntityByCheckedDateAndAcb(realWorldTestingSummaryReport.getCheckedDate(), realWorldTestingSummaryReport.getCertificationBody().getId()); if (entity == null) { - entity = RealWorldTestingPlanSummaryReportEntity.builder() + entity = RealWorldTestingPlanSummaryByAcbReportEntity.builder() .realWorldTestingYear(realWorldTestingSummaryReport.getRealWorldTestingYear()) .certificationBody(CertificationBodyEntity.builder() .id(realWorldTestingSummaryReport.getCertificationBody().getId()) @@ -36,33 +36,33 @@ public void save(RealWorldTestingSummaryReport realWorldTestingSummaryReport) th } } - public Optional getMaxRealWorldTestingYear() { + public Optional getMaxRealWorldTestingYearForAcbSummary() { return Optional.ofNullable(entityManager.createQuery( "select MAX(rwtpsr.realWorldTestingYear) " - + "from RealWorldTestingPlanSummaryReportEntity rwtpsr " + + "from RealWorldTestingPlanSummaryByAcbReportEntity rwtpsr " + "where (NOT deleted = true)", Long.class) .getSingleResult()); } - public List getRealWorldTestingReportsByTestingYear(Long realWorldTestingYear) { - return getEntitiesByRealWorldTestingYear(realWorldTestingYear).stream() + public List getRealWorldTestingSummaryByAcbReportsByTestingYear(Long realWorldTestingYear) { + return getAcbSummaryEntitiesByRealWorldTestingYear(realWorldTestingYear).stream() .map(entity -> entity.toDomain()) .toList(); } - private RealWorldTestingPlanSummaryReportEntity getEntityByCheckedDateAndAcb(LocalDate checkedDate, Long certificationBodyId) throws EntityRetrievalException { + private RealWorldTestingPlanSummaryByAcbReportEntity getEntityByCheckedDateAndAcb(LocalDate checkedDate, Long certificationBodyId) throws EntityRetrievalException { Query query = entityManager.createQuery( - "from RealWorldTestingPlanSummaryReportEntity rwtps " + "from RealWorldTestingPlanSummaryByAcbReportEntity rwtps " + "where (NOT deleted = true) " + "and checkedDate = :checkedDate " - + "and rwtps.certificationBody.id = :certificationBodyId", RealWorldTestingPlanSummaryReportEntity.class); + + "and rwtps.certificationBody.id = :certificationBodyId", RealWorldTestingPlanSummaryByAcbReportEntity.class); query.setParameter("checkedDate", checkedDate); query.setParameter("certificationBodyId", certificationBodyId); - List result = query.getResultList(); + List result = query.getResultList(); if (result.size() > 1) { - throw new EntityRetrievalException("Data error. Duplicate checked_date in real_world_testing_plan_summary_report table."); + throw new EntityRetrievalException("Data error. Duplicate checked_date in real_world_testing_plan_summary_by_acb_report table."); } if (result.size() > 0) { @@ -71,13 +71,21 @@ private RealWorldTestingPlanSummaryReportEntity getEntityByCheckedDateAndAcb(Loc return null; } - private List getEntitiesByRealWorldTestingYear(Long testingYear) { + private List getAcbSummaryEntitiesByRealWorldTestingYear(Long testingYear) { return entityManager.createQuery( - "from RealWorldTestingPlanSummaryReportEntity rwtps " + "from RealWorldTestingPlanSummaryByAcbReportEntity rwtps " + "where (NOT deleted = true) " - + "and rwtps.realWorldTestingYear = :realWorldTestingYear", RealWorldTestingPlanSummaryReportEntity.class) + + "and rwtps.realWorldTestingYear = :realWorldTestingYear", RealWorldTestingPlanSummaryByAcbReportEntity.class) .setParameter("realWorldTestingYear", testingYear) .getResultList(); } + public Optional getMaxRealWorldTestingYearForDeveloperSummary() { + return Optional.ofNullable(entityManager.createQuery( + "select MAX(rwtpsr.realWorldTestingYear) " + + "from RealWorldTestingPlanSummaryByDeveloperReportEntity rwtpsr " + + "where (NOT deleted = true)", Long.class) + .getSingleResult()); + + } } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingReportDataService.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingReportDataService.java index e88c73269e..f99fdbe25f 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingReportDataService.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingReportDataService.java @@ -22,20 +22,30 @@ public RealWorldTestingReportDataService(RealWorldTestingPlanSummaryReportDao re } @Transactional - public List getRealWorldTestingPlanSummaryReports() { - Optional rwtYear = realWorldTestingPlanSummaryReportDao.getMaxRealWorldTestingYear(); + public List getRealWorldTestingPlanSummaryByAcbReports() { + Optional rwtYear = realWorldTestingPlanSummaryReportDao.getMaxRealWorldTestingYearForAcbSummary(); if (rwtYear.isPresent()) { - return realWorldTestingPlanSummaryReportDao.getRealWorldTestingReportsByTestingYear(rwtYear.get()); + return realWorldTestingPlanSummaryReportDao.getRealWorldTestingSummaryByAcbReportsByTestingYear(rwtYear.get()); } else { return List.of(); } } @Transactional - public List getRealWorldTestingResultsSummaryReports() { - Optional rwtYear = realWorldTestingResultsSummaryReportDao.getMaxRealWorldTestingYear(); + public List getRealWorldTestingResultsSummaryByAcbReports() { + Optional rwtYear = realWorldTestingResultsSummaryReportDao.getMaxRealWorldTestingYearForAcbSummary(); if (rwtYear.isPresent()) { - return realWorldTestingResultsSummaryReportDao.getRealWorldTestingReportsByTestingYear(rwtYear.get()); + return realWorldTestingResultsSummaryReportDao.getRealWorldTestingSummaryByAcbReportsByTestingYear(rwtYear.get()); + } else { + return List.of(); + } + } + + @Transactional + public List getRealWorldTestingResultsSummaryByDeveloperReports() { + Optional rwtYear = realWorldTestingResultsSummaryReportDao.getMaxRealWorldTestingYearForDeveloperSummary(); + if (rwtYear.isPresent()) { + return realWorldTestingResultsSummaryReportDao.getRealWorldTestingSummaryByDeveloperReportsByTestingYear(rwtYear.get()); } else { return List.of(); } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportEntity.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryByAcbReportEntity.java similarity index 86% rename from chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportEntity.java rename to chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryByAcbReportEntity.java index 370960fe65..9e52147b3f 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportEntity.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryByAcbReportEntity.java @@ -27,8 +27,8 @@ @AllArgsConstructor @NoArgsConstructor @Entity -@Table(name = "real_world_testing_results_summary_report") -public class RealWorldTestingResultsSummaryReportEntity extends EntityAudit { +@Table(name = "real_world_testing_results_summary_by_acb_report") +public class RealWorldTestingResultsSummaryByAcbReportEntity extends EntityAudit { private static final long serialVersionUID = 4976557989831765742L; @Id @@ -52,8 +52,8 @@ public class RealWorldTestingResultsSummaryReportEntity extends EntityAudit { @Column(name = "requires_check_count") private Long requiresCheckCount; - public RealWorldTestingSummaryReport toDomain() { - return RealWorldTestingSummaryReport.builder() + public RealWorldTestingSummaryByAcbReport toDomain() { + return RealWorldTestingSummaryByAcbReport.builder() .id(id) .realWorldTestingYear(realWorldTestingYear) .certificationBody(certificationBody.toDomain()) diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryByDeveloperReportEntity.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryByDeveloperReportEntity.java new file mode 100644 index 0000000000..6011113c15 --- /dev/null +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryByDeveloperReportEntity.java @@ -0,0 +1,66 @@ +package gov.healthit.chpl.report.realworldtesting; + +import java.time.LocalDate; + +import gov.healthit.chpl.entity.EntityAudit; +import gov.healthit.chpl.entity.developer.DeveloperEntitySimple; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Getter +@Setter +@ToString +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "real_world_testing_results_summary_by_developer_report") +public class RealWorldTestingResultsSummaryByDeveloperReportEntity extends EntityAudit { + private static final long serialVersionUID = -1208758504334058121L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "real_world_testing_year") + private Long realWorldTestingYear; + + @OneToOne(optional = true, fetch = FetchType.LAZY) + @JoinColumn(name = "developer_id") + private DeveloperEntitySimple developer; + + @Column(name = "checked_date") + private LocalDate checkedDate; + + @Column(name = "checked_count") + private Long checkedCount; + + @Column(name = "requires_check_count") + private Long requiresCheckCount; + + public RealWorldTestingSummaryByDeveloperReport toDomain() { + return RealWorldTestingSummaryByDeveloperReport.builder() + .id(id) + .realWorldTestingYear(realWorldTestingYear) + .developerId(developer != null ? developer.getId() : null) + .developerName(developer != null ? developer.getName() : null) + .checkedDate(checkedDate) + .checkedCount(checkedCount) + .requiresCheckCount(requiresCheckCount) + .build(); + } +} diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportDao.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportDao.java index c3ede71113..2c7a01024a 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportDao.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingResultsSummaryReportDao.java @@ -8,17 +8,18 @@ import gov.healthit.chpl.dao.impl.BaseDAOImpl; import gov.healthit.chpl.entity.CertificationBodyEntity; +import gov.healthit.chpl.entity.developer.DeveloperEntitySimple; import gov.healthit.chpl.exception.EntityRetrievalException; import jakarta.persistence.Query; @Component public class RealWorldTestingResultsSummaryReportDao extends BaseDAOImpl { - public void save(RealWorldTestingSummaryReport realWorldTestingSummaryReport) throws EntityRetrievalException { - RealWorldTestingResultsSummaryReportEntity entity = getEntityByCheckedDateAndAcb(realWorldTestingSummaryReport.getCheckedDate(), + public void save(RealWorldTestingSummaryByAcbReport realWorldTestingSummaryReport) throws EntityRetrievalException { + RealWorldTestingResultsSummaryByAcbReportEntity entity = getEntityByCheckedDateAndAcb(realWorldTestingSummaryReport.getCheckedDate(), realWorldTestingSummaryReport.getCertificationBody().getId()); if (entity == null) { - entity = RealWorldTestingResultsSummaryReportEntity.builder() + entity = RealWorldTestingResultsSummaryByAcbReportEntity.builder() .realWorldTestingYear(realWorldTestingSummaryReport.getRealWorldTestingYear()) .certificationBody(CertificationBodyEntity.builder() .id(realWorldTestingSummaryReport.getCertificationBody().getId()) @@ -36,29 +37,33 @@ public void save(RealWorldTestingSummaryReport realWorldTestingSummaryReport) th } } - public Optional getMaxRealWorldTestingYear() { + public Optional getMaxRealWorldTestingYearForAcbSummary() { return Optional.ofNullable(entityManager.createQuery( "select MAX(rwtrsr.realWorldTestingYear) " - + "from RealWorldTestingResultsSummaryReportEntity rwtrsr " + + "from RealWorldTestingResultsSummaryByAcbReportEntity rwtrsr " + "where (NOT deleted = true)", Long.class) .getSingleResult()); } - public List getRealWorldTestingReportsByTestingYear(Long realWorldTestingYear) { - return getEntitiesByRealWorldTestingYear(realWorldTestingYear).stream() + public List getRealWorldTestingSummaryByAcbReportsByTestingYear(Long realWorldTestingYear) { + return getAcbSummaryEntitiesByRealWorldTestingYear(realWorldTestingYear).stream() .map(entity -> entity.toDomain()) .toList(); } - private RealWorldTestingResultsSummaryReportEntity getEntity(Long id) throws EntityRetrievalException { + private RealWorldTestingResultsSummaryByAcbReportEntity getEntityByCheckedDateAndAcb(LocalDate checkedDate, Long certificationBodyId) throws EntityRetrievalException { Query query = entityManager.createQuery( - "from RealWorldTestingResultsSummaryReportEntity where (NOT deleted = true) and id = :id", RealWorldTestingResultsSummaryReportEntity.class); - query.setParameter("id", id); - List result = query.getResultList(); + "from RealWorldTestingResultsSummaryByAcbReportEntity rwtrs " + + "where (NOT deleted = true) " + + "and checkedDate = :checkedDate " + + "and rwtrs.certificationBody.id = :certificationBodyId", RealWorldTestingResultsSummaryByAcbReportEntity.class); + query.setParameter("checkedDate", checkedDate); + query.setParameter("certificationBodyId", certificationBodyId); + List result = query.getResultList(); if (result.size() > 1) { - throw new EntityRetrievalException("Data error. Duplicate id in real_world_testing_results_summary_report table."); + throw new EntityRetrievalException("Data error. Duplicate checked_date in real_world_testing_results_summary_by_acb_report table."); } if (result.size() > 0) { @@ -67,18 +72,65 @@ private RealWorldTestingResultsSummaryReportEntity getEntity(Long id) throws Ent return null; } - private RealWorldTestingResultsSummaryReportEntity getEntityByCheckedDateAndAcb(LocalDate checkedDate, Long certificationBodyId) throws EntityRetrievalException { - Query query = entityManager.createQuery( - "from RealWorldTestingResultsSummaryReportEntity rwtrs " + private List getAcbSummaryEntitiesByRealWorldTestingYear(Long testingYear) { + return entityManager.createQuery( + "from RealWorldTestingResultsSummaryByAcbReportEntity rwtrs " + "where (NOT deleted = true) " + + "and rwtrs.realWorldTestingYear = :realWorldTestingYear", RealWorldTestingResultsSummaryByAcbReportEntity.class) + .setParameter("realWorldTestingYear", testingYear) + .getResultList(); + } + + public void save(RealWorldTestingSummaryByDeveloperReport realWorldTestingSummaryReport) throws EntityRetrievalException { + RealWorldTestingResultsSummaryByDeveloperReportEntity entity = getEntityByCheckedDateAndDeveloper(realWorldTestingSummaryReport.getCheckedDate(), + realWorldTestingSummaryReport.getDeveloperId()); + if (entity == null) { + entity = RealWorldTestingResultsSummaryByDeveloperReportEntity.builder() + .realWorldTestingYear(realWorldTestingSummaryReport.getRealWorldTestingYear()) + .developer(DeveloperEntitySimple.builder() + .id(realWorldTestingSummaryReport.getDeveloperId()) + .build()) + .checkedDate(realWorldTestingSummaryReport.getCheckedDate()) + .checkedCount(realWorldTestingSummaryReport.getCheckedCount()) + .requiresCheckCount(realWorldTestingSummaryReport.getRequiresCheckCount()) + .build(); + + create(entity); + } else { + entity.setCheckedCount(realWorldTestingSummaryReport.getCheckedCount()); + entity.setRequiresCheckCount(realWorldTestingSummaryReport.getRequiresCheckCount()); + update(entity); + } + } + + public Optional getMaxRealWorldTestingYearForDeveloperSummary() { + return Optional.ofNullable(entityManager.createQuery( + "select MAX(rwtrsr.realWorldTestingYear) " + + "from RealWorldTestingResultsSummaryByDeveloperReportEntity rwtrsr " + + "where (NOT deleted = true)", Long.class) + .getSingleResult()); + + } + + public List getRealWorldTestingSummaryByDeveloperReportsByTestingYear(Long realWorldTestingYear) { + return getDeveloperSummaryEntitiesByRealWorldTestingYear(realWorldTestingYear).stream() + .map(entity -> entity.toDomain()) + .toList(); + } + + private RealWorldTestingResultsSummaryByDeveloperReportEntity getEntityByCheckedDateAndDeveloper(LocalDate checkedDate, Long developerId) throws EntityRetrievalException { + Query query = entityManager.createQuery( + "from RealWorldTestingResultsSummaryByDeveloperReportEntity rwtrs " + + "JOIN FETCH rwtrs.developer dev " + + "where (NOT rwtrs.deleted = true) " + "and checkedDate = :checkedDate " - + "and rwtrs.certificationBody.id = :certificationBodyId", RealWorldTestingResultsSummaryReportEntity.class); + + "and dev.id = :developerId", RealWorldTestingResultsSummaryByDeveloperReportEntity.class); query.setParameter("checkedDate", checkedDate); - query.setParameter("certificationBodyId", certificationBodyId); - List result = query.getResultList(); + query.setParameter("developerId", developerId); + List result = query.getResultList(); if (result.size() > 1) { - throw new EntityRetrievalException("Data error. Duplicate checked_date in real_world_testing_results_summary_report table."); + throw new EntityRetrievalException("Data error. Duplicate checked_date in real_world_testing_results_by_devleoper_summary_report table."); } if (result.size() > 0) { @@ -87,11 +139,12 @@ private RealWorldTestingResultsSummaryReportEntity getEntityByCheckedDateAndAcb( return null; } - private List getEntitiesByRealWorldTestingYear(Long testingYear) { + private List getDeveloperSummaryEntitiesByRealWorldTestingYear(Long testingYear) { return entityManager.createQuery( - "from RealWorldTestingResultsSummaryReportEntity rwtrs " - + "where (NOT deleted = true) " - + "and rwtrs.realWorldTestingYear = :realWorldTestingYear", RealWorldTestingResultsSummaryReportEntity.class) + "from RealWorldTestingResultsSummaryByDeveloperReportEntity rwtrs " + + "JOIN FETCH rwtrs.developer " + + "where (NOT rwtrs.deleted = true) " + + "and rwtrs.realWorldTestingYear = :realWorldTestingYear", RealWorldTestingResultsSummaryByDeveloperReportEntity.class) .setParameter("realWorldTestingYear", testingYear) .getResultList(); } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryReport.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryByAcbReport.java similarity index 93% rename from chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryReport.java rename to chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryByAcbReport.java index b9e19b7eac..a91f942239 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryReport.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryByAcbReport.java @@ -13,7 +13,7 @@ @Data @Builder -public class RealWorldTestingSummaryReport { +public class RealWorldTestingSummaryByAcbReport { private Long id; private Long realWorldTestingYear; diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryByDeveloperReport.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryByDeveloperReport.java new file mode 100644 index 0000000000..e56a97bc44 --- /dev/null +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/realworldtesting/RealWorldTestingSummaryByDeveloperReport.java @@ -0,0 +1,27 @@ +package gov.healthit.chpl.report.realworldtesting; + +import java.time.LocalDate; + +import gov.healthit.chpl.util.LocalDateDeserializer; +import gov.healthit.chpl.util.LocalDateSerializer; +import lombok.Builder; +import lombok.Data; +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.annotation.JsonSerialize; + +@Data +@Builder +public class RealWorldTestingSummaryByDeveloperReport { + private Long id; + + private Long realWorldTestingYear; + private Long developerId; + private String developerName; + + @JsonDeserialize(using = LocalDateDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) + private LocalDate checkedDate; + + private Long checkedCount; + private Long requiresCheckCount; +} diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/RealWorldTestingSummaryReportCreatorJob.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/RealWorldTestingSummaryReportCreatorJob.java index 0f2e4a832d..60be011a8f 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/RealWorldTestingSummaryReportCreatorJob.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/RealWorldTestingSummaryReportCreatorJob.java @@ -5,6 +5,7 @@ import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.quartz.JobExecutionContext; @@ -17,13 +18,18 @@ import org.springframework.transaction.support.TransactionTemplate; import org.springframework.web.context.support.SpringBeanAutowiringSupport; +import gov.healthit.chpl.developer.search.ActiveListingSearchOptions; +import gov.healthit.chpl.developer.search.DeveloperSearchRequest; +import gov.healthit.chpl.developer.search.DeveloperSearchResult; +import gov.healthit.chpl.developer.search.DeveloperSearchService; import gov.healthit.chpl.domain.CertificationBody; import gov.healthit.chpl.manager.CertificationBodyManager; import gov.healthit.chpl.realworldtesting.domain.RealWorldTestingReport; import gov.healthit.chpl.realworldtesting.manager.RealWorldTestingReportService; import gov.healthit.chpl.report.realworldtesting.RealWorldTestingPlanSummaryReportDao; import gov.healthit.chpl.report.realworldtesting.RealWorldTestingResultsSummaryReportDao; -import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryReport; +import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByAcbReport; +import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByDeveloperReport; import gov.healthit.chpl.util.DateUtil; import lombok.extern.log4j.Log4j2; @@ -42,13 +48,16 @@ public class RealWorldTestingSummaryReportCreatorJob extends QuartzJob { @Autowired private CertificationBodyManager certificationBodyManager; + @Autowired + private DeveloperSearchService developerSearchService; + @Autowired private PlatformTransactionManager transactionManager; @Override public void execute(JobExecutionContext context) throws JobExecutionException { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); - LOGGER.info("********* Starting the Real World Report Creator job for " + context.getMergedJobDataMap().getString("email") + " *********"); + LOGGER.info("********* Starting the Real World Testing Summary Report Creator job *********"); try { List activeAcbIds = certificationBodyManager.getAllActive().stream() .map(acb -> acb.getId()) @@ -65,26 +74,27 @@ public void execute(JobExecutionContext context) throws JobExecutionException { TransactionOperations transactionOperations = new TransactionTemplate(transactionManager, new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW)); transactionOperations.executeWithoutResult(status -> { - processRwtPlanCounts(rwtPlansReports); - processRwtResultsCounts(rwtResultReports); + processRwtPlanCountsByAcb(rwtPlansReports); + processRwtResultsCountsByAcb(rwtResultReports); + processRwtResultsCountsByDeveloper(rwtResultReports); }); } catch (Exception e) { LOGGER.catching(e); } finally { - LOGGER.info("********* Completed the Real World Report Creator job. *********"); + LOGGER.info("********* Completed the Real World Testing Summary Report Creator job. *********"); } } - private void processRwtResultsCounts(List reportRows) { + private void processRwtResultsCountsByAcb(List reportRows) { Integer rwtEligibilityYear = LocalDate.now().getYear() - 1; - if (!isDateInResultsSubmissionWindow(LocalDate.now(), rwtEligibilityYear)) { - LOGGER.info("Outside the RWT Results submission window. Not collecting data."); + if (!isDateInResultsDataCollectionWindow(LocalDate.now(), rwtEligibilityYear)) { + LOGGER.info("Outside the RWT Results data collection window. Not collecting data."); return; } - List rwtSummaryReports = new ArrayList(); + List rwtSummaryReports = new ArrayList(); rwtReportService.getResultsStartDate(rwtEligibilityYear).datesUntil(LocalDate.now()).forEach(reportDate -> { certificationBodyManager.getAllActive().forEach(acb -> { @@ -94,17 +104,17 @@ private void processRwtResultsCounts(List reportRows) { && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) .collect(Collectors.counting()); - rwtSummaryReports.add(RealWorldTestingSummaryReport.builder() + rwtSummaryReports.add(RealWorldTestingSummaryByAcbReport.builder() .realWorldTestingYear(rwtEligibilityYear.longValue()) .certificationBody(acb) .checkedDate(reportDate) - .checkedCount(calculateResulltsCount(reportRows, rwtEligibilityYear, acb, reportDate).longValue()) + .checkedCount(calculateResultsCount(reportRows, rwtEligibilityYear, acb, reportDate).longValue()) .requiresCheckCount(eligibleListingCountForAcb) .build()); }); }); - rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryReport::getCheckedDate) + rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryByAcbReport::getCheckedDate) .thenComparing((o1, o2) -> o1.getCertificationBody().getId().compareTo(o2.getCertificationBody().getId()))); rwtSummaryReports.forEach(value -> { @@ -118,7 +128,54 @@ && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) LOGGER.info("Completed gathering RWT Results submissions."); } - private void processRwtPlanCounts(List reportRows) { + private void processRwtResultsCountsByDeveloper(List reportRows) { + Integer rwtEligibilityYear = LocalDate.now().getYear() - 1; + + if (!isDateInResultsDataCollectionWindow(LocalDate.now(), rwtEligibilityYear)) { + LOGGER.info("Outside the RWT Results data collection window. Not collecting data."); + return; + } + + List rwtSummaryReports = new ArrayList(); + + List developersWithActiveListings = developerSearchService.getAllPagesOfSearchResults(DeveloperSearchRequest.builder() + .activeListingsOptions(Stream.of(ActiveListingSearchOptions.HAS_ANY_ACTIVE).collect(Collectors.toSet())) + .build(), LOGGER); + + rwtReportService.getResultsStartDate(rwtEligibilityYear).datesUntil(LocalDate.now()).forEach(reportDate -> { + developersWithActiveListings.stream().forEach(dev -> { + Long eligibleListingCountForDeveloper = reportRows.stream() + .filter(row -> row.getDeveloperId().equals(dev.getId()) + && row.getRwtEligibilityYear() != null + && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) + .collect(Collectors.counting()); + + rwtSummaryReports.add(RealWorldTestingSummaryByDeveloperReport.builder() + .realWorldTestingYear(rwtEligibilityYear.longValue()) + .developerId(dev.getId()) + .developerName(dev.getName()) + .checkedDate(reportDate) + .checkedCount(calculateResultsCount(reportRows, rwtEligibilityYear, dev, reportDate).longValue()) + .requiresCheckCount(eligibleListingCountForDeveloper) + .build()); + }); + }); + + rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryByDeveloperReport::getCheckedDate) + .thenComparing((o1, o2) -> o1.getDeveloperId().compareTo(o2.getDeveloperId()))); + + rwtSummaryReports.forEach(value -> { + LOGGER.info("{} - {} - {}", value.getCheckedCount(), value.getCheckedDate(), value.getDeveloperName()); + try { + realWorldTestingResultsSummaryReportDao.save(value); + } catch (Exception e) { + LOGGER.error("Could not save RealWorldTestingSummaryReport: {}", value.toString(), e); + } + }); + LOGGER.info("Completed gathering RWT Results submissions."); + } + + private void processRwtPlanCountsByAcb(List reportRows) { Integer rwtEligibilityYear = LocalDate.now().getYear() + 1; if (!isDateInPlansSubmissionWindow(LocalDate.now(), rwtEligibilityYear)) { @@ -126,7 +183,7 @@ private void processRwtPlanCounts(List reportRows) { return; } - List rwtSummaryReports = new ArrayList(); + List rwtSummaryReports = new ArrayList(); rwtReportService.getPlansStartDate(rwtEligibilityYear).datesUntil(LocalDate.now()).forEach(reportDate -> { certificationBodyManager.getAllActive().forEach(acb -> { @@ -136,7 +193,7 @@ private void processRwtPlanCounts(List reportRows) { && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) .collect(Collectors.counting()); - rwtSummaryReports.add(RealWorldTestingSummaryReport.builder() + rwtSummaryReports.add(RealWorldTestingSummaryByAcbReport.builder() .realWorldTestingYear(rwtEligibilityYear.longValue()) .certificationBody(acb) .checkedDate(reportDate) @@ -146,7 +203,7 @@ && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) }); }); - rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryReport::getCheckedDate) + rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryByAcbReport::getCheckedDate) .thenComparing((o1, o2) -> o1.getCertificationBody().getId().compareTo(o2.getCertificationBody().getId()))); rwtSummaryReports.forEach(value -> { @@ -170,7 +227,7 @@ private Integer calculatePlanCount(List reports, Integer .size(); } - private Integer calculateResulltsCount(List reports, Integer rwtYear, CertificationBody acb, LocalDate checkedDate) { + private Integer calculateResultsCount(List reports, Integer rwtYear, CertificationBody acb, LocalDate checkedDate) { return reports.stream() .filter(report -> report.getAcbName().equals(acb.getName()) && report.getRwtEligibilityYear() != null @@ -180,12 +237,22 @@ private Integer calculateResulltsCount(List reports, Int .size(); } + private Integer calculateResultsCount(List reports, Integer rwtYear, DeveloperSearchResult dev, LocalDate checkedDate) { + return reports.stream() + .filter(report -> report.getDeveloperId().equals(dev.getId()) + && report.getRwtEligibilityYear() != null + && DateUtil.isDateBetweenInclusive(Pair.of(rwtReportService.getResultsStartDate(rwtYear), checkedDate), + report.getRwtResultsCheckDate())) + .toList() + .size(); + } + private boolean isDateInPlansSubmissionWindow(LocalDate dateToTest, Integer rwtYear) { return DateUtil.isDateBetweenInclusive(Pair.of(rwtReportService.getPlansStartDate(rwtYear), rwtReportService.getPlansLateDate(rwtYear)), dateToTest); } - private boolean isDateInResultsSubmissionWindow(LocalDate dateToTest, Integer rwtYear) { - return DateUtil.isDateBetweenInclusive(Pair.of(rwtReportService.getResultsStartDate(rwtYear), rwtReportService.getResultsLateDate(rwtYear)), dateToTest); + private boolean isDateInResultsDataCollectionWindow(LocalDate dateToTest, Integer rwtYear) { + return DateUtil.isDateBetweenInclusive(Pair.of(rwtReportService.getResultsStartDate(rwtYear), rwtReportService.getResultsDataGatheringStopDate(rwtYear)), dateToTest); } private Boolean isListingValidAsOfDate(LocalDate listingCertificationDate, LocalDate date) { diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/onetime/RwtPopulateMissingDataCreatorJob.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/onetime/RwtPopulateMissingDataCreatorJob.java index e405adb94f..268905ec6e 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/onetime/RwtPopulateMissingDataCreatorJob.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/onetime/RwtPopulateMissingDataCreatorJob.java @@ -22,7 +22,7 @@ import gov.healthit.chpl.realworldtesting.domain.RealWorldTestingReport; import gov.healthit.chpl.realworldtesting.manager.RealWorldTestingReportService; import gov.healthit.chpl.report.realworldtesting.RealWorldTestingResultsSummaryReportDao; -import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryReport; +import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByAcbReport; import gov.healthit.chpl.scheduler.job.QuartzJob; import gov.healthit.chpl.util.DateUtil; import lombok.extern.log4j.Log4j2; @@ -76,7 +76,7 @@ private void processRwtResultsCounts(List reportRows) { return; } - List rwtSummaryReports = new ArrayList(); + List rwtSummaryReports = new ArrayList(); rwtReportService.getResultsStartDate(rwtEligibilityYear).datesUntil(lastDayOfResultsSubmissionWindow.plusDays(1)).forEach(reportDate -> { certificationBodyManager.getAllActive().forEach(acb -> { @@ -86,7 +86,7 @@ private void processRwtResultsCounts(List reportRows) { && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) .collect(Collectors.counting()); - rwtSummaryReports.add(RealWorldTestingSummaryReport.builder() + rwtSummaryReports.add(RealWorldTestingSummaryByAcbReport.builder() .realWorldTestingYear(rwtEligibilityYear.longValue()) .certificationBody(acb) .checkedDate(reportDate) @@ -96,7 +96,7 @@ && isListingValidAsOfDate(row.getCertificationDate(), reportDate)) }); }); - rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryReport::getCheckedDate) + rwtSummaryReports.sort(Comparator.comparing(RealWorldTestingSummaryByAcbReport::getCheckedDate) .thenComparing((o1, o2) -> o1.getCertificationBody().getId().compareTo(o2.getCertificationBody().getId()))); rwtSummaryReports.forEach(value -> {