Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCD-4517 - Build "Real World Testing" report in PowerBI #1771

Draft
wants to merge 13 commits into
base: staging
Choose a base branch
from
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
@@ -0,0 +1,51 @@
package gov.healthit.chpl.web.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import gov.healthit.chpl.report.ReportDataManager;
import gov.healthit.chpl.report.realworldtesting.RealWorldTestingSummaryReport;
import gov.healthit.chpl.util.SwaggerSecurityRequirement;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.log4j.Log4j2;

@Log4j2
@Tag(name = "report-data/real-world-testing", description = "Allows retrieval of data used by Real World Testing report.")
@RestController
@RequestMapping("/report-data/real-world-testing")
public class RealWorldTestingReportController {

private ReportDataManager reportDataManager;

@Autowired
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.",
security = {
@SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY)
})
@RequestMapping(value = "/plans", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public @ResponseBody List<RealWorldTestingSummaryReport> getRealWorldTestingPlanReports() {
return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingPlanSummaryReports();
}

@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.",
security = {
@SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY)
})
@RequestMapping(value = "/results", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public @ResponseBody List<RealWorldTestingSummaryReport> getRealWorldTestingResultsReports() {
return reportDataManager.getRealWorldTestingReportDataService().getRealWorldTestingResultsSummaryReports();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,18 @@
interval="1" modulate="true" />
</Policies>
</RollingFile>
<RollingFile name="realWorldTestingSummaryReportCreatorJob"
fileName="${logDir}/scheduler/realWorldTestingSummaryReportCreatorJob.log"
filePattern="${logDir}/scheduler/history/realWorldTestingSummaryReportCreatorJob-%d{yyyy-MM-dd}.log"
filePermissions="rw-rw-r--">
<PatternLayout>
<Pattern>%d{ISO8601} %-5p (%t) [%C{1}(%M:%L)] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy
interval="1" modulate="true" />
</Policies>
</RollingFile>
<RollingFile name="attestationReportCreatorJob"
fileName="${logDir}/scheduler/attestationReportCreatorJob.log"
filePattern="${logDir}/scheduler/history/attestationReportCreatorJob-%d{yyyy-MM-dd}.log"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,9 @@
<Logger name="updatedSedFriendlyIdsJobLogger" level="INFO" additivity="false">
<AppenderRef ref="updatedSedFriendlyIdsJob" />
</Logger>
<Logger name="realWorldTestingSummaryReportCreatorJobLogger" level="INFO" additivity="false">
<AppenderRef ref="realWorldTestingSummaryReportCreatorJob" />
</Logger>
<Logger name="attestationReportCreatorJobLogger" level="INFO" additivity="false">
<AppenderRef ref="attestationReportCreatorJob" />
</Logger>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@
<AppenderRef ref="updatedSedFriendlyIdsJob" />
<AppenderRef ref="updatedSedFriendlyIdsJobJson" />
</Logger>
<Logger name="realWorldTestingSummaryReportCreatorJobLogger" level="INFO" additivity="false">
<AppenderRef ref="realWorldTestingSummaryReportCreatorJob" />
<AppenderRef ref="realWorldTestingSummaryReportCreatorJobJson" />
</Logger>
<Logger name="attestationReportCreatorJobLogger" level="INFO" additivity="false">
<AppenderRef ref="attestationReportCreatorJob" />
<AppenderRef ref="attestationReportCreatorJobJson" />
Expand Down
9 changes: 9 additions & 0 deletions chpl/chpl-resources/src/main/resources/jobs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,15 @@
<recover>false</recover>
</job>

<job>
<name>realWorldTestingSummaryReportCreatorJob</name>
<group>systemJobs</group>
<description>Populate RWT reporting tables</description>
<job-class>gov.healthit.chpl.scheduler.job.RealWorldTestingSummaryReportCreatorJob</job-class>
<durability>true</durability>
<recover>false</recover>
</job>

<job>
<name>attestationReportCreatorJob</name>
<group>systemJobs</group>
Expand Down
11 changes: 11 additions & 0 deletions chpl/chpl-resources/src/main/resources/system-triggers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,17 @@
<cron-expression>0 30 6 * * ?</cron-expression> <!-- At 0630 UTC every day -->
</cron>
</trigger>
<trigger>
<cron>
<name>realWorldTestingSummaryReportCreator</name>
<group>realWorldTestingSummaryReportCreatorJobTrigger</group>
<job-name>realWorldTestingSummaryReportCreatorJob</job-name>
<job-group>systemJobs</job-group>
<misfire-instruction>MISFIRE_INSTRUCTION_DO_NOTHING</misfire-instruction>
<cron-expression>0 15 6 * * ?</cron-expression> <!-- At 0615 UTC every day -->
</cron>
</trigger>

Check warning on line 258 in chpl/chpl-resources/src/main/resources/system-triggers.xml

View workflow job for this annotation

GitHub Actions / Checkstyle job

[reviewdog] reported by reviewdog 🐶 Line has trailing spaces. Raw Output: /github/workspace/./chpl/chpl-resources/src/main/resources/system-triggers.xml:258:0: warning: Line has trailing spaces. (com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck)
<trigger>
<cron>
<name>attestationReportCreator</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,28 +249,28 @@ private boolean isLocalDateEqualOrAfter(LocalDate date1, LocalDate date2) {
return date1.isEqual(date2) || date1.isAfter(date2);
}

private LocalDate getPlansStartDate(Integer rwtEligYear) {
public LocalDate getPlansStartDate(Integer rwtEligYear) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String mmdd = env.getProperty("rwtPlanStartDayOfYear");
String mmddyyyy = mmdd + "/" + String.valueOf(rwtEligYear - 1);
return LocalDate.parse(mmddyyyy, formatter);
}

private LocalDate getPlansLateDate(Integer rwtEligYear) {
public LocalDate getPlansLateDate(Integer rwtEligYear) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String mmdd = env.getProperty("rwtPlanDueDate");
String mmddyyyy = mmdd + "/" + String.valueOf(rwtEligYear - 1);
return LocalDate.parse(mmddyyyy, formatter);
}

private LocalDate getResultsStartDate(Integer rwtEligYear) {
public LocalDate getResultsStartDate(Integer rwtEligYear) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String mmdd = env.getProperty("rwtResultsStartDayOfYear");
String mmddyyyy = mmdd + "/" + String.valueOf(rwtEligYear + 1);
return LocalDate.parse(mmddyyyy, formatter);
}

private LocalDate getResultsLateDate(Integer rwtEligYear) {
public LocalDate getResultsLateDate(Integer rwtEligYear) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String mmdd = env.getProperty("rwtResultsDueDate");
String mmddyyyy = mmdd + "/" + String.valueOf(rwtEligYear + 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import gov.healthit.chpl.report.product.ProductByAcb;
import gov.healthit.chpl.report.product.ProductReportsService;
import gov.healthit.chpl.report.product.UniqueProductCount;
import gov.healthit.chpl.report.realworldtesting.RealWorldTestingReportDataService;
import gov.healthit.chpl.report.servicebaseurllistreport.ServiceBaseUrlListReportService;
import gov.healthit.chpl.report.servicebaseurllistreport.UrlUptimeMonitorEx;
import gov.healthit.chpl.report.surveillance.CapCounts;
Expand Down Expand Up @@ -47,6 +48,8 @@ public class ReportDataManager {
private ReportMetadataDAO reportMetadataDAO;
private CriteriaAttributeReportService criteriaAttributeReportService;
private ServiceBaseUrlListReportService serviceBaseUrlListReportService;
private RealWorldTestingReportDataService realWorldTestingReportDataService;



@Autowired
Expand All @@ -59,7 +62,9 @@ public ReportDataManager(CriteriaMigrationReportService criteriaMigrationReportS
AttestationReportService attestationReportService,
ReportMetadataDAO reportMetadataDAO,
CriteriaAttributeReportService criteriaAttributeReportService,
ServiceBaseUrlListReportService serviceBaseUrlListReportService) {
ServiceBaseUrlListReportService serviceBaseUrlListReportService,
RealWorldTestingReportDataService realWorldTestingReportDataService) {

this.criteriaMigrationReportService = criteriaMigrationReportService;
this.developerReportsService = developerReportsService;
this.surveillanceReportsService = surveillanceReportsService;
Expand All @@ -71,6 +76,7 @@ public ReportDataManager(CriteriaMigrationReportService criteriaMigrationReportS
this.reportMetadataDAO = reportMetadataDAO;
this.criteriaAttributeReportService = criteriaAttributeReportService;
this.serviceBaseUrlListReportService = serviceBaseUrlListReportService;
this.realWorldTestingReportDataService = realWorldTestingReportDataService;
}

public List<ReportMetadata> getReportMetadataByReportGroup(String reportGroup) {
Expand Down Expand Up @@ -258,6 +264,11 @@ public List<UrlUptimeMonitorEx> getUrlUptimeMonitors() {
return serviceBaseUrlListReportService.getUrlUptimeMonitors();
}

@Synchronized("lock")
public RealWorldTestingReportDataService getRealWorldTestingReportDataService() {
return realWorldTestingReportDataService;
}

@Synchronized("lock")
public List<AttestationReport> getAttestationReports() {
return attestationReportService.getAttestationReports();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package gov.healthit.chpl.report.realworldtesting;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Component;

import gov.healthit.chpl.dao.impl.BaseDAOImpl;
import gov.healthit.chpl.entity.CertificationBodyEntity;
import gov.healthit.chpl.exception.EntityRetrievalException;
import jakarta.persistence.Query;

@Component
public class RealWorldTestingPlanSummaryReportDao extends BaseDAOImpl {

public void save(RealWorldTestingSummaryReport realWorldTestingSummaryReport) throws EntityRetrievalException {
RealWorldTestingPlanSummaryReportEntity entity = getEntityByCheckedDateAndAcb(realWorldTestingSummaryReport.getCheckedDate(),
realWorldTestingSummaryReport.getCertificationBody().getId());
if (entity == null) {
entity = RealWorldTestingPlanSummaryReportEntity.builder()
.realWorldTestingYear(realWorldTestingSummaryReport.getRealWorldTestingYear())
.certificationBody(CertificationBodyEntity.builder()
.id(realWorldTestingSummaryReport.getCertificationBody().getId())
.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<Long> getMaxRealWorldTestingYear() {
return Optional.ofNullable(entityManager.createQuery(
"select MAX(rwtpsr.realWorldTestingYear) "
+ "from RealWorldTestingPlanSummaryReportEntity rwtpsr "
+ "where (NOT deleted = true)", Long.class)
.getSingleResult());

}

public List<RealWorldTestingSummaryReport> getRealWorldTestingReportsByTestingYear(Long realWorldTestingYear) {
return getEntitiesByRealWorldTestingYear(realWorldTestingYear).stream()
.map(entity -> entity.toDomain())
.toList();
}

private RealWorldTestingPlanSummaryReportEntity getEntity(Long id) throws EntityRetrievalException {
Query query = entityManager.createQuery(
"from RealWorldTestingPlanSummaryReportEntity where (NOT deleted = true) and id = :id", RealWorldTestingPlanSummaryReportEntity.class);
query.setParameter("id", id);
List<RealWorldTestingPlanSummaryReportEntity> result = query.getResultList();

if (result.size() > 1) {
throw new EntityRetrievalException("Data error. Duplicate id in real_world_testing_plan_summary_report table.");
}

if (result.size() > 0) {
return result.get(0);
}
return null;
}

private RealWorldTestingPlanSummaryReportEntity getEntityByCheckedDateAndAcb(LocalDate checkedDate, Long certificationBodyId) throws EntityRetrievalException {
Query query = entityManager.createQuery(
"from RealWorldTestingPlanSummaryReportEntity rwtps "
+ "where (NOT deleted = true) "
+ "and checkedDate = :checkedDate "
+ "and rwtps.certificationBody.id = :certificationBodyId", RealWorldTestingPlanSummaryReportEntity.class);
query.setParameter("checkedDate", checkedDate);
query.setParameter("certificationBodyId", certificationBodyId);
List<RealWorldTestingPlanSummaryReportEntity> result = query.getResultList();

if (result.size() > 1) {
throw new EntityRetrievalException("Data error. Duplicate checked_date in real_world_testing_plan_summary_report table.");
}

if (result.size() > 0) {
return result.get(0);
}
return null;
}

private List<RealWorldTestingPlanSummaryReportEntity> getEntitiesByRealWorldTestingYear(Long testingYear) {
return entityManager.createQuery(
"from RealWorldTestingPlanSummaryReportEntity rwtps "
+ "where (NOT deleted = true) "
+ "and rwtps.realWorldTestingYear = :realWorldTestingYear", RealWorldTestingPlanSummaryReportEntity.class)
.setParameter("realWorldTestingYear", testingYear)
.getResultList();
}

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

import java.time.LocalDate;

import gov.healthit.chpl.entity.CertificationBodyEntity;
import gov.healthit.chpl.entity.EntityAudit;
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_plan_summary_report")
public class RealWorldTestingPlanSummaryReportEntity extends EntityAudit {
private static final long serialVersionUID = -1208758504334058893L;

@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 = "certification_body_id")
private CertificationBodyEntity certificationBody;

@Column(name = "checked_date")
private LocalDate checkedDate;

@Column(name = "checked_count")
private Long checkedCount;

@Column(name = "requires_check_count")
private Long requiresCheckCount;

public RealWorldTestingSummaryReport toDomain() {
return RealWorldTestingSummaryReport.builder()
.id(id)
.realWorldTestingYear(realWorldTestingYear)
.certificationBody(certificationBody.toDomain())
.checkedDate(checkedDate)
.checkedCount(checkedCount)
.requiresCheckCount(requiresCheckCount)
.build();
}
}
Loading
Loading