Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
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.SurveillanceByCriteria;
import gov.healthit.chpl.report.surveillance.SurveillanceByDeveloper;
import gov.healthit.chpl.report.surveillance.SurveillanceByRequirementType;
import gov.healthit.chpl.scheduler.job.summarystatistics.data.CertificationBodyStatistic;
import gov.healthit.chpl.search.domain.ListingSearchResult;
import gov.healthit.chpl.util.LogMethodUsage;
Expand Down Expand Up @@ -72,6 +74,28 @@ public ReportDataController(ReportDataManager reportDataManager,
return reportDataManager.getSurveillanceOpenDuringTheLastYearForActiveDevelopers();
}

@Operation(summary = "Retrieves the data about each surveillance including the related criterion.",
description = "Retrieves the data about each surveillance including the related criterion.",
security = {
@SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY)
})
@LogMethodUsage
@RequestMapping(value = "/surveillance-by-criteria", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public @ResponseBody List<SurveillanceByCriteria> getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings() {
return reportDataManager.getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings();
}

@Operation(summary = "Retrieves the data about each surveillance including the requirement type.",
description = "Retrieves the data about each surveillance including the requirement type.",
security = {
@SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY)
})
@LogMethodUsage
@RequestMapping(value = "/surveillance-by-requirement-type", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public @ResponseBody List<SurveillanceByRequirementType> getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings() {
return reportDataManager.getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings();
}

@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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.springframework.stereotype.Repository;

import gov.healthit.chpl.caching.CacheNames;
import gov.healthit.chpl.certificationCriteria.CertificationCriterion;
import gov.healthit.chpl.compliance.surveillance.entity.NonconformityTypeEntity;
import gov.healthit.chpl.compliance.surveillance.entity.RequirementGroupTypeEntity;
import gov.healthit.chpl.compliance.surveillance.entity.RequirementTypeEntity;
Expand All @@ -21,6 +22,7 @@
import gov.healthit.chpl.compliance.surveillance.entity.SurveillanceResultTypeEntity;
import gov.healthit.chpl.compliance.surveillance.entity.SurveillanceTypeEntity;
import gov.healthit.chpl.dao.impl.BaseDAOImpl;
import gov.healthit.chpl.domain.CertificationEdition;
import gov.healthit.chpl.domain.NonconformityType;
import gov.healthit.chpl.domain.surveillance.RequirementGroupType;
import gov.healthit.chpl.domain.surveillance.RequirementType;
Expand All @@ -32,15 +34,21 @@
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.SurveillanceByCriteria;
import gov.healthit.chpl.report.surveillance.SurveillanceByDeveloper;
import gov.healthit.chpl.report.surveillance.SurveillanceByRequirementType;
import gov.healthit.chpl.search.entity.ListingSearchEntity;
import gov.healthit.chpl.service.CertificationCriterionService;
import gov.healthit.chpl.util.CertificationStatusUtil;
import gov.healthit.chpl.util.NullSafeEvaluator;
import jakarta.persistence.Query;
import lombok.extern.log4j.Log4j2;

@Repository("surveillanceDAO")
@Log4j2
public class SurveillanceDAO extends BaseDAOImpl {
private static final Long ICS_REQUIREMENT_OFFSET = 70000L;

private static final String SURVEILLANCE_FULL_HQL =
"SELECT DISTINCT surv "
+ "FROM SurveillanceEntity surv "
Expand All @@ -52,13 +60,16 @@
+ "LEFT OUTER JOIN FETCH ncs.type nct "
+ "WHERE surv.deleted <> true ";

private CertificationCriterionService criteriaService;
private String unformattedListingDetailsUrl;
private String unformattedDeveloperDetailsUrl;

@Autowired
public SurveillanceDAO(@Value("${chplUrlBegin}") String chplUrlBegin,
public SurveillanceDAO(CertificationCriterionService criteriaService,
@Value("${chplUrlBegin}") String chplUrlBegin,
@Value("${developerUrlPart}") String developerUrlPart,
@Value("${listingDetailsUrlPart}") String listingDetailsUrlPart) {
this.criteriaService = criteriaService;
this.unformattedDeveloperDetailsUrl = chplUrlBegin + developerUrlPart;
this.unformattedListingDetailsUrl = chplUrlBegin + listingDetailsUrlPart;
}
Expand Down Expand Up @@ -366,7 +377,113 @@
.build());
}
return results;
}

public List<SurveillanceByCriteria> getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings() {
Query query = entityManager.createQuery("SELECT DISTINCT surv, reqType "
+ "FROM SurveillanceEntity surv "
+ "JOIN ListingSearchEntity listing ON listing.id = surv.certifiedProductId AND listing.certificationStatus IN (:activeCertificationStatusNames) "
+ "JOIN SurveillanceTypeEntity surveillanceType ON surv.surveillanceTypeId = surveillanceType.id "
+ "JOIN SurveillanceRequirementEntity req ON req.surveillanceId = surv.id AND req.deleted = false "
+ "JOIN RequirementTypeEntity reqType ON req.requirementType.id = reqType.id "
+ "WHERE (surv.endDate IS NULL OR surv.endDate >= :oneYearAgo) "
+ "AND reqType.requirementGroupType.id = :criterionGroupTypeId "
+ "AND surv.deleted = false");

List<SurveillanceByCriteria> results = new ArrayList<SurveillanceByCriteria>();
List<Object[]> entities = query
.setParameter("oneYearAgo", LocalDate.now().minusYears(1))
.setParameter("activeCertificationStatusNames", CertificationStatusUtil.getActiveStatusNames())
.setParameter("criterionGroupTypeId", RequirementGroupType.CERTIFIED_CAPABILITY_ID)
.getResultList();

for (Object[] entity : entities) {
SurveillanceEntity surveillance = (SurveillanceEntity) entity[0];
RequirementTypeEntity requirementType = (RequirementTypeEntity) entity[1];
results.add(SurveillanceByCriteria.builder()
// RequirementType "id" matches the criterion ID in the db view
.criterion(criteriaService.get(requirementType.getId()))
.criterionSortOrder(criteriaService.getCriterionSortIndex(requirementType.getId()))
.surveillanceId(surveillance.getId())
.surveillanceStartDate(surveillance.getStartDate())
.surveillanceEndDate(surveillance.getEndDate())
.build());
}
return results;
}

public List<SurveillanceByRequirementType> getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings() {
Query query = entityManager.createQuery("SELECT DISTINCT surv, reqGroupType, reqType "
+ "FROM SurveillanceEntity surv "
+ "JOIN ListingSearchEntity listing ON listing.id = surv.certifiedProductId AND listing.certificationStatus IN (:activeCertificationStatusNames) "
+ "JOIN SurveillanceTypeEntity surveillanceType ON surv.surveillanceTypeId = surveillanceType.id "
+ "JOIN SurveillanceRequirementEntity req ON req.surveillanceId = surv.id AND req.deleted = false "
+ "JOIN RequirementTypeEntity reqType ON req.requirementType.id = reqType.id "
+ "JOIN RequirementGroupTypeEntity reqGroupType ON reqType.requirementGroupType.id = reqGroupType.id "
+ "WHERE (surv.endDate IS NULL OR surv.endDate >= :oneYearAgo) "
+ "AND surv.deleted = false");

List<SurveillanceByRequirementType> results = new ArrayList<SurveillanceByRequirementType>();
List<Object[]> entities = query
.setParameter("oneYearAgo", LocalDate.now().minusYears(1))
.setParameter("activeCertificationStatusNames", CertificationStatusUtil.getActiveStatusNames())
.getResultList();

for (Object[] entity : entities) {
SurveillanceEntity surveillance = (SurveillanceEntity) entity[0];
RequirementGroupTypeEntity requirementGroup = (RequirementGroupTypeEntity) entity[1];
RequirementTypeEntity requirementType = (RequirementTypeEntity) entity[2];

RequirementType fullReqType = buildRequirementType(requirementGroup, requirementType);
results.add(SurveillanceByRequirementType.builder()
.requirementType(fullReqType)
.criterionSortOrder(isCriterion(fullReqType) ? criteriaService.getCriterionSortIndex(fullReqType.getId()) : null)
.surveillanceId(surveillance.getId())
.surveillanceStartDate(surveillance.getStartDate())
.surveillanceEndDate(surveillance.getEndDate())
.build());
}
return results;
}

private RequirementType buildRequirementType(RequirementGroupTypeEntity requirementGroup, RequirementTypeEntity requirementType) {
RequirementType type = RequirementType.builder()
.requirementGroupType(RequirementGroupType.builder()
.id(requirementGroup.getId())
.name(requirementGroup.getName())
.build())
.title(requirementType.getTitle())
.build();
if (!StringUtils.isEmpty(requirementType.getNumber())) {
//it's a criterion, but does it have the ICS offset?
CertificationCriterion criterion = null;
if (requirementGroup.getId().equals(RequirementGroupType.INHERITED_CERTIFIED_STATUS_ID)) {
criterion = criteriaService.get(requirementType.getId() - ICS_REQUIREMENT_OFFSET);
} else {
criterion = criteriaService.get(requirementType.getId());
}

if (criterion != null) {
type.setCertificationEdition(CertificationEdition.builder()
.id(criterion.getCertificationEditionId())
.name(criterion.getCertificationEdition())
.build());
type.setEdition(criterion.getCertificationEdition());
type.setEndDay(criterion.getEndDay());
type.setId(criterion.getId());
type.setNumber(criterion.getNumber());
type.setStartDay(criterion.getStartDay());
} else {
LOGGER.error("Unable to find criterion for requirement group ID " + requirementGroup.getId() + " and requirement type ID " + requirementType.getId());;

Check warning on line 477 in chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java

View workflow job for this annotation

GitHub Actions / Checkstyle job

[reviewdog] reported by reviewdog 🐶 Empty statement. Raw Output: /github/workspace/./chpl/chpl-service/src/main/java/gov/healthit/chpl/compliance/surveillance/SurveillanceDAO.java:477:167: warning: Empty statement. (com.puppycrawl.tools.checkstyle.checks.coding.EmptyStatementCheck)
}
}
return type;
}

private boolean isCriterion(RequirementType type) {
return type.getRequirementGroupType().getId().equals(RequirementGroupType.CERTIFIED_CAPABILITY_ID)
|| (type.getRequirementGroupType().getId().equals(RequirementGroupType.INHERITED_CERTIFIED_STATUS_ID)
&& !StringUtils.isEmpty(type.getNumber()));
}

public void deleteSurveillance(Surveillance surv) throws EntityRetrievalException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public class SurveillanceRequirementEntity extends EntityAudit {
@Column(name = "surveillance_id")
private Long surveillanceId;

@Column(name = "certification_criterion_id")
private Long criterionId;

@OneToOne(optional = true, fetch = FetchType.LAZY)
@JoinColumn(name = "requirement_type_id")
private RequirementTypeEntity requirementType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class RequirementGroupType implements Serializable {
public static final Long RWT_SUBMISSION_ID = 4L;
public static final Long ATTESTATION_SUBMISSION_ID = 5L;
public static final Long DEVELOPER_SUBMISSION_REQUIREMENT_ID = 6L;
public static final Long INHERITED_CERTIFIED_STATUS_ID = 7L;

@Schema(description = "Requirement group type internal ID")
private Long id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
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.SurveillanceByCriteria;
import gov.healthit.chpl.report.surveillance.SurveillanceByDeveloper;
import gov.healthit.chpl.report.surveillance.SurveillanceByRequirementType;
import gov.healthit.chpl.report.surveillance.SurveillanceReportsService;
import gov.healthit.chpl.report.svap.SvapReportService;
import gov.healthit.chpl.scheduler.job.summarystatistics.data.CertificationBodyStatistic;
Expand Down Expand Up @@ -119,6 +121,16 @@ public List<SurveillanceByDeveloper> getSurveillanceOpenDuringTheLastYearForActi
return surveillanceReportsService.getSurveillanceOpenDuringTheLastYearForActiveDevelopers();
}

@Synchronized("lock")
public List<SurveillanceByCriteria> getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings() {
return surveillanceReportsService.getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings();
}

@Synchronized("lock")
public List<SurveillanceByRequirementType> getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings() {
return surveillanceReportsService.getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings();
}

@Synchronized("lock")
public SurveillanceActivityCounts getSurveillanceActivityCounts() {
return surveillanceReportsService.getSurveiilanceActivityCounts();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public List<CriteriaWithAnySvap> getCriteriaWithAnySvap(CertificationStatus cert
.listingCountAttestingToCriteria(cc.getCount())
.listingCountAttestingToCriteriaAndAnySvap(lookupCountByCriteria(criteriaWithAnySvapCounts, cc.getCriterion()))
.certificationStatusName(certificationStatus.getName())
.sortOrder(certificationCriterionService.getCertificationResultSortIndex(cc.getCriterion().getId()))
.sortOrder(certificationCriterionService.getCriterionSortIndex(cc.getCriterion().getId()))
.build())
.peek(x -> LOGGER.info(x.toString()))
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public List<NonconformityTypeCount> getNonconformityCounts() {
.filter(nc -> nc.getType().getId().equals(ncType.getId()))
.count())
.nonconformityType(ncType.toDomain())
.displayOrder(certificationCriterionService.getCertificationResultSortIndex(ncType.getId()))
.displayOrder(certificationCriterionService.getCriterionSortIndex(ncType.getId()))
.build())
.filter(ncCount -> !ncCount.getCount().equals(0L))
.toList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package gov.healthit.chpl.report.surveillance;

import java.time.LocalDate;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access;

import gov.healthit.chpl.certificationCriteria.CertificationCriterion;
import gov.healthit.chpl.certificationCriteria.CriterionStatus;
import gov.healthit.chpl.domain.concept.CertificationEditionConcept;
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 SurveillanceByCriteria {

private CertificationCriterion criterion;
private Integer criterionSortOrder;
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;

@JsonProperty(access = Access.READ_ONLY)
public CriterionStatus getStatus() {
if (!StringUtils.isEmpty(criterion.getCertificationEdition())
&& (criterion.getCertificationEdition().equals(CertificationEditionConcept.CERTIFICATION_EDITION_2011.getYear())
|| criterion.getCertificationEdition().equals(CertificationEditionConcept.CERTIFICATION_EDITION_2014.getYear()))) {
return CriterionStatus.RETIRED;
} else {
LocalDate end = criterion.getEndDay() != null ? criterion.getEndDay() : LocalDate.MAX;
if (end.isBefore(LocalDate.now())) {
return CriterionStatus.REMOVED;
}
return CriterionStatus.ACTIVE;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gov.healthit.chpl.report.surveillance;

import java.time.LocalDate;

import gov.healthit.chpl.domain.surveillance.RequirementType;
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 SurveillanceByRequirementType {

private RequirementType requirementType;
private Integer criterionSortOrder;
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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ public List<SurveillanceByDeveloper> getSurveillanceOpenDuringTheLastYearForActi
return surveillanceDao.getSurveillanceOpenDuringTheLastYearForActiveDevelopers();
}

@Transactional
public List<SurveillanceByCriteria> getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings() {
return surveillanceDao.getSurveillanceByCriteriaOpenDuringTheLastYearForActiveListings();
}

@Transactional
public List<SurveillanceByRequirementType> getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings() {
return surveillanceDao.getSurveillanceByRequirementTypeOpenDuringTheLastYearForActiveListings();
}

public SurveillanceActivityCounts getSurveiilanceActivityCounts() {
StatisticsSnapshot stats = getStatistics();
return SurveillanceActivityCounts.builder()
Expand Down
Loading
Loading