From 1e4f99b6523c57d90645e679b7a790ab3b346932 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Mon, 15 Jun 2026 14:21:06 -0400 Subject: [PATCH 1/4] feat!: Create endpoint to get surveillance by developer [#OCD-5335] --- .../web/controller/ReportDataController.java | 18 ++++-- .../gov/healthit/chpl/CHPLServiceConfig.java | 4 +- .../surveillance/SurveillanceDAO.java | 64 +++++++++++++------ .../chpl/report/ReportDataManager.java | 6 ++ .../surveillance/SurveillanceByDeveloper.java | 32 ++++++++++ .../SurveillanceReportsService.java | 14 +++- 6 files changed, 111 insertions(+), 27 deletions(-) create mode 100644 chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceByDeveloper.java diff --git a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java index d970488a1d..679572277d 100644 --- a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java +++ b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.RestController; import gov.healthit.chpl.developer.search.DeveloperSearchResult; -import gov.healthit.chpl.developer.search.DeveloperSearchService; import gov.healthit.chpl.manager.StatisticsManager; import gov.healthit.chpl.report.ReportDataManager; import gov.healthit.chpl.report.criteriamigrationreport.CriteriaMigrationReportDenormalized; @@ -22,6 +21,7 @@ import gov.healthit.chpl.report.surveillance.CapCounts; import gov.healthit.chpl.report.surveillance.NonconformityCounts; import gov.healthit.chpl.report.surveillance.SurveillanceActivityCounts; +import gov.healthit.chpl.report.surveillance.SurveillanceByDeveloper; import gov.healthit.chpl.scheduler.job.summarystatistics.data.CertificationBodyStatistic; import gov.healthit.chpl.search.domain.ListingSearchResult; import gov.healthit.chpl.util.LogMethodUsage; @@ -41,15 +41,12 @@ public class ReportDataController { private ReportDataManager reportDataManager; private StatisticsManager statisticsManager; - private DeveloperSearchService developerSearchService; @Autowired public ReportDataController(ReportDataManager reportDataManager, - StatisticsManager statisticsManager, - DeveloperSearchService developerSearchService) { + StatisticsManager statisticsManager) { this.reportDataManager = reportDataManager; this.statisticsManager = statisticsManager; - this.developerSearchService = developerSearchService; } @Operation(summary = "Retrieves the data used to generate the HTI-1 Criteria Migration Report.", @@ -63,6 +60,17 @@ public ReportDataController(ReportDataManager reportDataManager, return reportDataManager.getHti1CriteriaMigrationReport(); } + @Operation(summary = "Retrieves the data about each surveillance including the related developer.", + description = "Retrieves the data about each surveillance including the related developer.", + security = { + @SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY) + }) + @LogMethodUsage + @RequestMapping(value = "/surveillance-by-developer", method = RequestMethod.GET, produces = "application/json; charset=utf-8") + public @ResponseBody List getAllSurveillanceByDeveloper() { + return reportDataManager.getAllSurveillanceByDeveloper(); + } + @Operation(summary = "Retrieves the data used to generate the Surveillance Activity Counts report.", description = "Retrieves the data used to generate the Surveillance Activity Counts report.", security = { diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java index 66d727c7a9..fcc5f28393 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java @@ -136,8 +136,8 @@ private Properties additionalProperties() { // Set the two below properties to true to see the generated SQL // Very useful for debugging - properties.setProperty("hibernate.show_sql", "false"); - properties.setProperty("hibernate.format_sql", "false"); + properties.setProperty("hibernate.show_sql", "true"); + properties.setProperty("hibernate.format_sql", "true"); return properties; } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java index 0b54d0d0df..0a9d40e29a 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java @@ -6,6 +6,8 @@ import java.util.Set; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Repository; @@ -27,8 +29,11 @@ import gov.healthit.chpl.domain.surveillance.SurveillanceRequirement; import gov.healthit.chpl.domain.surveillance.SurveillanceResultType; import gov.healthit.chpl.domain.surveillance.SurveillanceType; +import gov.healthit.chpl.entity.developer.DeveloperSearchResultEntity; import gov.healthit.chpl.exception.EntityRetrievalException; import gov.healthit.chpl.exception.UserPermissionRetrievalException; +import gov.healthit.chpl.report.surveillance.SurveillanceByDeveloper; +import gov.healthit.chpl.search.entity.ListingSearchEntity; import gov.healthit.chpl.util.NullSafeEvaluator; import jakarta.persistence.Query; import lombok.extern.log4j.Log4j2; @@ -47,6 +52,17 @@ public class SurveillanceDAO extends BaseDAOImpl { + "LEFT OUTER JOIN FETCH ncs.type nct " + "WHERE surv.deleted <> true "; + private String unformattedListingDetailsUrl; + private String unformattedDeveloperDetailsUrl; + + @Autowired + public SurveillanceDAO(@Value("${chplUrlBegin}") String chplUrlBegin, + @Value("${developerUrlPart}") String developerUrlPart, + @Value("${listingDetailsUrlPart}") String listingDetailsUrlPart) { + this.unformattedDeveloperDetailsUrl = chplUrlBegin + developerUrlPart; + this.unformattedListingDetailsUrl = chplUrlBegin + listingDetailsUrlPart; + } + public Long insertSurveillance(Long certifiedProductId, Surveillance surv) throws UserPermissionRetrievalException { SurveillanceEntity toInsert = new SurveillanceEntity(); populateSurveillanceEntity(certifiedProductId, toInsert, surv); @@ -268,7 +284,6 @@ public Long updateSurveillance(Long certifiedProductId, Surveillance updatedSurv return updatedSurveillance.getId(); } - public SurveillanceEntity getSurveillanceByCertifiedProductAndFriendlyId(Long certifiedProductId, String survFriendlyId) { Query query = entityManager.createQuery( @@ -287,7 +302,6 @@ public SurveillanceEntity getSurveillanceByCertifiedProductAndFriendlyId(Long ce return null; } - public SurveillanceEntity getSurveillanceById(Long id) throws EntityRetrievalException { SurveillanceEntity result = fetchSurveillanceById(id); return result; @@ -310,23 +324,6 @@ public SurveillanceEntity getSurveillanceByNonconformityId(Long nonconformityId) } } - public SurveillanceEntity getSurveillanceByDocumentId(Long documentId) - throws EntityRetrievalException { - entityManager.clear(); - Query query = entityManager.createQuery(SURVEILLANCE_FULL_HQL - + "AND docs.id = :entityid", - SurveillanceEntity.class); - query.setParameter("entityid", documentId); - - List results = query.getResultList(); - if (results == null || results.size() == 0) { - String msg = msgUtil.getMessage("surveillance.notFound"); - throw new EntityRetrievalException(msg); - } else { - return results.get(0); - } - } - public List getSurveillanceByCertifiedProductId(Long id) { entityManager.clear(); Query query = entityManager.createQuery(SURVEILLANCE_FULL_HQL @@ -338,6 +335,35 @@ public List getSurveillanceByCertifiedProductId(Long id) { return results; } + public List getAllSurveillanceByDeveloper() { + Query query = entityManager.createQuery("SELECT DISTINCT surv, listing, developer " + + "FROM SurveillanceEntity surv, ListingSearchEntity listing, DeveloperSearchResultEntity developer " + + "WHERE surv.certifiedProductId = listing.id " + + "AND listing.developerId = developer.id " + + "AND surv.deleted <> true "); + + List results = new ArrayList(); + List entities = query.getResultList(); + for (Object[] entity : entities) { + SurveillanceEntity surveillance = (SurveillanceEntity) entity[0]; + ListingSearchEntity listing = (ListingSearchEntity) entity[1]; + DeveloperSearchResultEntity developer = (DeveloperSearchResultEntity) entity[2]; + results.add(SurveillanceByDeveloper.builder() + .developerId(developer.getId()) + .developerName(developer.getDeveloperName()) + .developerDetailsUrl(String.format(unformattedDeveloperDetailsUrl, developer.getId() + "")) + .developerHasActiveListings(developer.getCurrentActiveListingCount() > 0) + .listingId(listing.getId()) + .chplProductNumber(listing.getChplProductNumber()) + .listingDetailsUrl(String.format(unformattedListingDetailsUrl, listing.getId() + "")) + .surveillanceId(surveillance.getId()) + .surveillanceStartDate(surveillance.getStartDate()) + .surveillanceEndDate(surveillance.getEndDate()) + .build()); + } + return results; + + } public void deleteSurveillance(Surveillance surv) throws EntityRetrievalException { LOGGER.debug("Looking for surveillance with id " + surv.getId() + " to delete."); diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java index ce0fb4719b..ebb0c9ac7f 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java @@ -32,6 +32,7 @@ import gov.healthit.chpl.report.surveillance.CapCounts; import gov.healthit.chpl.report.surveillance.NonconformityCounts; import gov.healthit.chpl.report.surveillance.SurveillanceActivityCounts; +import gov.healthit.chpl.report.surveillance.SurveillanceByDeveloper; import gov.healthit.chpl.report.surveillance.SurveillanceReportsService; import gov.healthit.chpl.report.svap.SvapReportService; import gov.healthit.chpl.scheduler.job.summarystatistics.data.CertificationBodyStatistic; @@ -112,6 +113,11 @@ public List getHti1CriteriaMigrationReport( return criteriaMigrationReportService.getHtiReportData(CriteriaMigrationReportService.HTI1_REPORT_ID); } + @Synchronized("lock") + public List getAllSurveillanceByDeveloper() { + return surveillanceReportsService.getAllSurveillanceByDeveloper(); + } + @Synchronized("lock") public SurveillanceActivityCounts getSurveillanceActivityCounts() { return surveillanceReportsService.getSurveiilanceActivityCounts(); diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceByDeveloper.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceByDeveloper.java new file mode 100644 index 0000000000..f5cec0fed3 --- /dev/null +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceByDeveloper.java @@ -0,0 +1,32 @@ +package gov.healthit.chpl.report.surveillance; + +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 SurveillanceByDeveloper { + private Long developerId; + private String developerName; + private String developerDetailsUrl; + private boolean developerHasActiveListings; + private Long listingId; + private String chplProductNumber; + private String listingDetailsUrl; + + private Long surveillanceId; + + @JsonDeserialize(using = LocalDateDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) + private LocalDate surveillanceStartDate; + + @JsonDeserialize(using = LocalDateDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) + private LocalDate surveillanceEndDate; +} diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java index b2c64a1ff0..6732df8d25 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java @@ -7,6 +7,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import gov.healthit.chpl.compliance.surveillance.SurveillanceDAO; import gov.healthit.chpl.dao.statistics.SummaryStatisticsDAO; import gov.healthit.chpl.entity.CertificationStatusType; import gov.healthit.chpl.exception.ValidationException; @@ -19,6 +20,7 @@ import gov.healthit.chpl.search.domain.ListingSearchResult; import gov.healthit.chpl.search.domain.NonConformitySearchOptions; import gov.healthit.chpl.search.domain.SearchRequest; +import jakarta.transaction.Transactional; import lombok.extern.log4j.Log4j2; @Log4j2 @@ -26,11 +28,21 @@ public class SurveillanceReportsService extends SummaryStatisticsReportBaseService { private ListingSearchService listingSearchService; + private SurveillanceDAO surveillanceDao; @Autowired - public SurveillanceReportsService(SummaryStatisticsDAO summaryStatisticsDAO, ListingSearchService listingSearchService, CertificationBodyManager certificationBodyManager) { + public SurveillanceReportsService(SummaryStatisticsDAO summaryStatisticsDAO, + ListingSearchService listingSearchService, + CertificationBodyManager certificationBodyManager, + SurveillanceDAO surveillanceDao) { super(summaryStatisticsDAO, certificationBodyManager); this.listingSearchService = listingSearchService; + this.surveillanceDao = surveillanceDao; + } + + @Transactional + public List getAllSurveillanceByDeveloper() { + return surveillanceDao.getAllSurveillanceByDeveloper(); } public SurveillanceActivityCounts getSurveiilanceActivityCounts() { From 32710647d69d8b020e30b350c2cf305631439215 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Mon, 15 Jun 2026 14:29:02 -0400 Subject: [PATCH 2/4] feat: Reduce queries to only surveillance from active devs w/in last yr [#OCD-5335] --- .../chpl/web/controller/ReportDataController.java | 2 +- .../main/java/gov/healthit/chpl/CHPLServiceConfig.java | 4 ++-- .../chpl/compliance/surveillance/SurveillanceDAO.java | 10 +++++++--- .../surveillance/SurveillanceReportsService.java | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java index 679572277d..9a9926aa60 100644 --- a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java +++ b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java @@ -67,7 +67,7 @@ public ReportDataController(ReportDataManager reportDataManager, }) @LogMethodUsage @RequestMapping(value = "/surveillance-by-developer", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - public @ResponseBody List getAllSurveillanceByDeveloper() { + public @ResponseBody List getSurveillanceOpenDuringTheLastYearForActiveDevelopers() { return reportDataManager.getAllSurveillanceByDeveloper(); } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java index fcc5f28393..66d727c7a9 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java @@ -136,8 +136,8 @@ private Properties additionalProperties() { // Set the two below properties to true to see the generated SQL // Very useful for debugging - properties.setProperty("hibernate.show_sql", "true"); - properties.setProperty("hibernate.format_sql", "true"); + properties.setProperty("hibernate.show_sql", "false"); + properties.setProperty("hibernate.format_sql", "false"); return properties; } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java index 0a9d40e29a..bec8740204 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java @@ -335,15 +335,19 @@ public List getSurveillanceByCertifiedProductId(Long id) { return results; } - public List getAllSurveillanceByDeveloper() { + public List getSurveillanceOpenDuringTheLastYearForActiveDevelopers() { Query query = entityManager.createQuery("SELECT DISTINCT surv, listing, developer " + "FROM SurveillanceEntity surv, ListingSearchEntity listing, DeveloperSearchResultEntity developer " - + "WHERE surv.certifiedProductId = listing.id " + + "WHERE (surv.endDate IS NULL OR surv.startDate >= :oneYearAgo) " + + "AND surv.certifiedProductId = listing.id " + "AND listing.developerId = developer.id " + + "AND developer.currentActiveListingCount > 0 " + "AND surv.deleted <> true "); List results = new ArrayList(); - List entities = query.getResultList(); + List entities = query + .setParameter("oneYearAgo", LocalDate.now().minusYears(1)) + .getResultList(); for (Object[] entity : entities) { SurveillanceEntity surveillance = (SurveillanceEntity) entity[0]; ListingSearchEntity listing = (ListingSearchEntity) entity[1]; diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java index 6732df8d25..b56c4e9527 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/surveillance/SurveillanceReportsService.java @@ -41,8 +41,8 @@ public SurveillanceReportsService(SummaryStatisticsDAO summaryStatisticsDAO, } @Transactional - public List getAllSurveillanceByDeveloper() { - return surveillanceDao.getAllSurveillanceByDeveloper(); + public List getSurveillanceOpenDuringTheLastYearForActiveDevelopers() { + return surveillanceDao.getSurveillanceOpenDuringTheLastYearForActiveDevelopers(); } public SurveillanceActivityCounts getSurveiilanceActivityCounts() { From 60843b8c750e8d83d428de2a76829ca37d72c5b5 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Mon, 15 Jun 2026 14:36:02 -0400 Subject: [PATCH 3/4] fix: Compilation issues [#OCD-5335] --- .../healthit/chpl/web/controller/ReportDataController.java | 2 +- .../main/java/gov/healthit/chpl/report/ReportDataManager.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java index 9a9926aa60..78c09fd1d1 100644 --- a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java +++ b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ReportDataController.java @@ -68,7 +68,7 @@ public ReportDataController(ReportDataManager reportDataManager, @LogMethodUsage @RequestMapping(value = "/surveillance-by-developer", method = RequestMethod.GET, produces = "application/json; charset=utf-8") public @ResponseBody List getSurveillanceOpenDuringTheLastYearForActiveDevelopers() { - return reportDataManager.getAllSurveillanceByDeveloper(); + return reportDataManager.getSurveillanceOpenDuringTheLastYearForActiveDevelopers(); } @Operation(summary = "Retrieves the data used to generate the Surveillance Activity Counts report.", diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java index ebb0c9ac7f..dd60fa3b91 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/report/ReportDataManager.java @@ -114,8 +114,8 @@ public List getHti1CriteriaMigrationReport( } @Synchronized("lock") - public List getAllSurveillanceByDeveloper() { - return surveillanceReportsService.getAllSurveillanceByDeveloper(); + public List getSurveillanceOpenDuringTheLastYearForActiveDevelopers() { + return surveillanceReportsService.getSurveillanceOpenDuringTheLastYearForActiveDevelopers(); } @Synchronized("lock") From 001946caf4cb0d233e44131500d5d040aa01a555 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Wed, 17 Jun 2026 09:33:03 -0400 Subject: [PATCH 4/4] fix: Use correct hql logic [#OCD-5335] --- .../healthit/chpl/compliance/surveillance/SurveillanceDAO.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java index bec8740204..0d1ea0d1d6 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java @@ -338,7 +338,7 @@ public List getSurveillanceByCertifiedProductId(Long id) { public List getSurveillanceOpenDuringTheLastYearForActiveDevelopers() { Query query = entityManager.createQuery("SELECT DISTINCT surv, listing, developer " + "FROM SurveillanceEntity surv, ListingSearchEntity listing, DeveloperSearchResultEntity developer " - + "WHERE (surv.endDate IS NULL OR surv.startDate >= :oneYearAgo) " + + "WHERE (surv.endDate IS NULL OR surv.endDate >= :oneYearAgo) " + "AND surv.certifiedProductId = listing.id " + "AND listing.developerId = developer.id " + "AND developer.currentActiveListingCount > 0 "