Skip to content
Open
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 @@ -366,10 +366,10 @@ realWorldTestingCriteriaKeys={2021: 'criterion.170_315_b_1_old,\
criterion.170_315_g_7,\
criterion.170_315_g_8,\
criterion.170_315_g_9_cures,\
criterion.170_315_g_10,\
criterion.170_315_g_31,\
criterion.170_315_g_32,\
criterion.170_315_g_33,\
criterion.170_315_g_10,\
criterion.170_315_h_1,\
criterion.170_315_h_2,\
criterion.170_315_j_20,\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ private FeatureList() {

public static final String CMS_DISABLED = "cms-disabled";
public static final String DEMOGRAPHIC_CHANGE_REQUEST = "demographic-change-request";
public static final String HTI_5_ERD = "hti-5-erd";
public static final String INSIGHTS_DISPLAY = "insights-display";
public static final String SERVICE_BASE_URL_LIST_CHANGE_REQUEST = "sbul-change-request";
public static final String RWT_CHANGE_REQUEST = "rwt-change-request";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package gov.healthit.chpl.realworldtesting.domain;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;

import gov.healthit.chpl.util.DateUtil;
import gov.healthit.chpl.certificationCriteria.CertificationCriterion;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
Expand All @@ -34,57 +30,16 @@ public class RealWorldTestingReport {
private LocalDate rwtResultsCheckDate;
private String rwtPlansMessage;
private String rwtResultsMessage;
private Boolean attestsG7;
private Boolean attestsG9;
private Boolean attestsG10;

public List<String> toListOfStrings() {
List<String> results = new ArrayList<String>();
results.add(acbName);
results.add(chplProductNumber);
results.add(currentStatus);
results.add(DateUtil.format(certificationDate));
results.add(productName);
results.add(productId == null ? null : productId.toString());
results.add(developerName);
results.add(developerId == null ? null : developerId.toString());
results.add(!CollectionUtils.isEmpty(developerUsers) ? developerUsers.stream().collect(Collectors.joining("; ")) : "");
results.add(rwtEligibilityYear == null ? null : rwtEligibilityYear.toString());
results.add(BooleanUtils.isTrue(ics) ? "Yes" : "");
results.add(rwtPlansUrl);
results.add(rwtPlansCheckDate == null ? null : rwtPlansCheckDate.toString());
results.add(rwtResultsUrl);
results.add(rwtResultsCheckDate == null ? null : rwtResultsCheckDate.toString());
results.add(rwtPlansMessage);
results.add(rwtResultsMessage);
results.add(BooleanUtils.isTrue(attestsG7) ? "TRUE" : "FALSE");
results.add(BooleanUtils.isTrue(attestsG9) ? "TRUE" : "FALSE");
results.add(BooleanUtils.isTrue(attestsG10) ? "TRUE" : "FALSE");
return results;
}
private List<CriterionAndSvapData> criterionAndSvapData;

public static List<String> getHeaders() {
List<String> results = new ArrayList<String>();
results.add("ONC-ACB Name");
results.add("CHPL ID");
results.add("Current Status");
results.add("Certification Date");
results.add("Product Name");
results.add("Product DBID");
results.add("Developer Name");
results.add("Developer DBID");
results.add("Developer Users");
results.add("Initial RWT Year");
results.add("ICS");
results.add("RWT Plans URL");
results.add("RWT Plans Submission Confirmed");
results.add("RWT Results URL");
results.add("RWT Results Submission Confirmed");
results.add("RWT Plans Message");
results.add("RWT Results Message");
results.add("170.315 (g)(7)");
results.add("170.315 (g)(9)");
results.add("170.315 (g)(10)");
return results;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class CriterionAndSvapData {
private CertificationCriterion criterion;
private boolean isAttested;
private boolean isGCriterion;
private boolean usesSvap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import gov.healthit.chpl.certificationCriteria.CertificationCriterion;
import gov.healthit.chpl.certifiedproduct.service.CertificationStatusEventsService;
import gov.healthit.chpl.dao.CertifiedProductDAO;
import gov.healthit.chpl.domain.CertificationResult;
import gov.healthit.chpl.domain.CertificationStatusEvent;
import gov.healthit.chpl.domain.Developer;
import gov.healthit.chpl.domain.auth.User;
Expand All @@ -25,8 +25,8 @@
import gov.healthit.chpl.exception.EntityRetrievalException;
import gov.healthit.chpl.permissions.ResourcePermissionsFactory;
import gov.healthit.chpl.realworldtesting.domain.RealWorldTestingReport;
import gov.healthit.chpl.realworldtesting.domain.RealWorldTestingReport.CriterionAndSvapData;
import gov.healthit.chpl.service.CertificationCriterionService;
import gov.healthit.chpl.service.CertificationCriterionService.Criteria2015;
import gov.healthit.chpl.service.realworldtesting.RealWorldTestingEligibility;
import gov.healthit.chpl.service.realworldtesting.RealWorldTestingEligiblityReason;
import gov.healthit.chpl.service.realworldtesting.RealWorldTestingEligiblityService;
Expand All @@ -41,23 +41,23 @@ public class RealWorldTestingReportService {
private ErrorMessageUtil errorMsg;
private Environment env;
private CertificationStatusEventsService certificationStatusEventsService;
private CertificationCriterionService criteriaService;
private RealWorldTestingEligiblityServiceFactory rwtEligServiceFactory;
private ResourcePermissionsFactory resourcePermissionsFactory;
private List<CertificationStatusType> withdrawnStatuses;
private CertificationCriterion g7, g9, g10;

@Autowired
public RealWorldTestingReportService(CertifiedProductDAO certifiedProductDAO,
ErrorMessageUtil errorMsg, Environment env,
CertificationStatusEventsService certificationStatusEventsService,
CertificationCriterionService criteriaService,
CertificationCriterionService certificationCriterionService,
RealWorldTestingEligiblityServiceFactory rwtEligServiceFactory,
ResourcePermissionsFactory resourcePermissionsFactory) {

this.certifiedProductDAO = certifiedProductDAO;
this.errorMsg = errorMsg;
this.env = env;
this.criteriaService = criteriaService;
this.certificationStatusEventsService = certificationStatusEventsService;
this.rwtEligServiceFactory = rwtEligServiceFactory;
this.resourcePermissionsFactory = resourcePermissionsFactory;
Expand All @@ -67,9 +67,6 @@ public RealWorldTestingReportService(CertifiedProductDAO certifiedProductDAO,
CertificationStatusType.WithdrawnByDeveloperUnderReview,
CertificationStatusType.Retired,
CertificationStatusType.TerminatedByOnc);
g7 = criteriaService.get(Criteria2015.G_7);
g9 = criteriaService.get(Criteria2015.G_9_CURES);
g10 = criteriaService.get(Criteria2015.G_10);
}

public List<RealWorldTestingReport> getRealWorldTestingReports(List<Long> acbIds, Logger logger) {
Expand Down Expand Up @@ -146,9 +143,7 @@ private RealWorldTestingReport getRealWorldTestingReport(CertifiedProductDetails
.rwtPlansCheckDate(listing.getRwtPlansCheckDate())
.rwtResultsUrl(listing.getRwtResultsUrl())
.rwtResultsCheckDate(listing.getRwtResultsCheckDate())
.attestsG7(attestsCriteria(rwtElig, g7))
.attestsG9(attestsCriteria(rwtElig, g9))
.attestsG10(attestsCriteria(rwtElig, g10))
.criterionAndSvapData(buildCriterionAndSvapData(rwtElig.getAttestedCertificationResults()))
.build();

if (rwtElig.getEligibilityYear() != null) {
Expand All @@ -158,11 +153,15 @@ private RealWorldTestingReport getRealWorldTestingReport(CertifiedProductDetails
}
}

private boolean attestsCriteria(RealWorldTestingEligibility eligibility, CertificationCriterion criterion) {
return eligibility.getAttestedCriteria().stream()
.filter(crit -> crit.getId().equals(criterion.getId()))
.findAny()
.isPresent();
private List<CriterionAndSvapData> buildCriterionAndSvapData(List<CertificationResult> certResults) {
return certResults.stream()
.map(certResult -> CriterionAndSvapData.builder()
.criterion(certResult.getCriterion())
.isAttested(certResult.getSuccess())
.isGCriterion(criteriaService.isGCriterion(certResult.getCriterion()))
.usesSvap(!CollectionUtils.isEmpty(certResult.getSvaps()))
.build())
.collect(Collectors.toList());
}

private List<String> getDeveloperUsers(Developer developer) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
package gov.healthit.chpl.scheduler.job;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
Expand All @@ -32,6 +27,7 @@
import gov.healthit.chpl.realworldtesting.domain.RealWorldTestingReportSummary;
import gov.healthit.chpl.realworldtesting.manager.RealWorldTestingReportService;
import gov.healthit.chpl.scheduler.job.realworldtesting.RealWorldTestingReportSummaryCalculator;
import gov.healthit.chpl.scheduler.presenter.RealWorldTestingCsvPresenter;
import gov.healthit.chpl.util.NullSafeEvaluator;
import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -50,6 +46,9 @@ public class RealWorldTestingReportEmailJob implements Job {
@Autowired
private Environment env;

@Autowired
private RealWorldTestingCsvPresenter csvPresenter;

@Autowired
private ChplHtmlEmailBuilder chplHtmlEmailBuilder;

Expand Down Expand Up @@ -103,7 +102,7 @@ private String createHtmlMessage(JobExecutionContext context, List<RealWorldTest
private File generateCsvFile(JobExecutionContext context, List<RealWorldTestingReport> rows) {
LOGGER.info("Generating CSV attachment");
File outputFile = getOutputFile(env.getProperty("rwt.report.filename") + LocalDate.now().toString());
outputFile = writeToFile(rows, outputFile);
writeToFile(rows, outputFile);
LOGGER.info("Completed Generating CSV attachment");
return outputFile;
}
Expand All @@ -120,25 +119,8 @@ private File getOutputFile(String reportFilename) {
return temp;
}

private File writeToFile(List<RealWorldTestingReport> rows, File outputFile) {
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outputFile),
Charset.forName("UTF-8").newEncoder());
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.EXCEL)) {
writer.write('\ufeff');
csvPrinter.printRecord(RealWorldTestingReport.getHeaders());
rows.stream()
.forEach(row -> {
try {
csvPrinter.printRecord(row.toListOfStrings());
} catch (Exception e) {
LOGGER.error(e);
}

});
} catch (Exception e) {
LOGGER.error(e);
}
return outputFile;
private void writeToFile(List<RealWorldTestingReport> rows, File outputFile) {
csvPresenter.presentAsFile(rows, outputFile);
}

private String getAcbNamesAsBrSeparatedList(JobExecutionContext jobContext) {
Expand Down Expand Up @@ -169,7 +151,7 @@ private String getEmailSummaryParagraph(List<RealWorldTestingReport> rows) {
RealWorldTestingReportSummary summary = RealWorldTestingReportSummaryCalculator.calculateSummariesByEligibityYear(rows, year);
paragraph.append("Real World Testing for ").append(summary.getRwtEligibilityYear()).append(":<br/>")
.append("<ul>")
.append("<li>").append(NullSafeEvaluator.eval(() -> summary.getTotalListings(), 0)).append(" Total listing for this period</li>")
.append("<li>").append(NullSafeEvaluator.eval(() -> summary.getTotalListings(), 0)).append(" Total listing(s) for this period</li>")
.append("<li>").append(NullSafeEvaluator.eval(() -> summary.getTotalWithdrawn(), 0)).append(" Withdrawn, no longer eligible</li>")
.append("<li>").append(NullSafeEvaluator.eval(() -> summary.getTotalActive(), 0)).append(" Active</li>")
.append("<li>").append(NullSafeEvaluator.eval(() -> summary.getTotalEligibleViaIcs(), 0)).append(" Eligible via ICS</li>")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.stream.Stream;

import org.apache.commons.lang3.tuple.Pair;
import org.ff4j.FF4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -18,6 +19,8 @@
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import gov.healthit.chpl.FeatureList;
import gov.healthit.chpl.certificationCriteria.CertificationCriterion;
import gov.healthit.chpl.developer.search.ActiveListingSearchOptions;
import gov.healthit.chpl.developer.search.DeveloperSearchRequest;
import gov.healthit.chpl.developer.search.DeveloperSearchResult;
Expand All @@ -30,6 +33,8 @@
import gov.healthit.chpl.report.realworldtesting.RealWorldTestingResultsSummaryReportDao;
import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByAcbReport;
import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryByDeveloperReport;
import gov.healthit.chpl.service.CertificationCriterionService;
import gov.healthit.chpl.service.CertificationCriterionService.Criteria2015;
import gov.healthit.chpl.util.DateUtil;
import lombok.extern.log4j.Log4j2;

Expand All @@ -54,10 +59,22 @@ public class RealWorldTestingSummaryReportCreatorJob extends QuartzJob {
@Autowired
private PlatformTransactionManager transactionManager;

@Autowired
private FF4j ff4j;

@Autowired
private CertificationCriterionService criteriaService;

private CertificationCriterion g7, g9, g10;

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
LOGGER.info("********* Starting the Real World Testing Summary Report Creator job *********");
g7 = criteriaService.get(Criteria2015.G_7);
g9 = criteriaService.get(Criteria2015.G_9_CURES);
g10 = criteriaService.get(Criteria2015.G_10);

try {
List<Long> activeAcbIds = certificationBodyManager.getAllActive().stream()
.map(acb -> acb.getId())
Expand All @@ -66,9 +83,8 @@ public void execute(JobExecutionContext context) throws JobExecutionException {
List<RealWorldTestingReport> rwtPlansReports = rwtReportService.getRealWorldTestingReports(activeAcbIds, LOGGER).stream()
.collect(Collectors.toList());

//RWT Results requirement is only enforced for listings with g7, g9, or g10 so we only want to report on those
List<RealWorldTestingReport> rwtResultReports = rwtPlansReports.stream()
.filter(report -> report.getAttestsG7() || report.getAttestsG9() || report.getAttestsG10())
.filter(report -> isRwtResultsRequired(report))
.collect(Collectors.toList());

TransactionOperations transactionOperations = new TransactionTemplate(transactionManager,
Expand All @@ -86,6 +102,24 @@ public void execute(JobExecutionContext context) throws JobExecutionException {

}

private boolean isRwtResultsRequired(RealWorldTestingReport rwtReport) {
//RWT Results requirement is only enforced for listings with g7, g9, or g10 pre HTI-5.
//Post HTI-5 listings with any g-criteria or non-g-criteria using svap have the RWT Results requirement.
if (ff4j.check(FeatureList.HTI_5_ERD)) {
return rwtReport.getCriterionAndSvapData().stream()
.filter(item -> item.isAttested() && (item.isGCriterion() || item.isUsesSvap()))
.findAny()
.isPresent();
} else {
return rwtReport.getCriterionAndSvapData().stream()
.filter(item -> item.getCriterion().getId().equals(g7.getId())
|| item.getCriterion().getId().equals(g9.getId())
|| item.getCriterion().getId().equals(g10.getId()))
.findAny()
.isPresent();
}
}

private void processRwtResultsCountsByAcb(List<RealWorldTestingReport> reportRows) {
Integer rwtEligibilityYear = LocalDate.now().getYear() - 1;

Expand Down
Loading
Loading