Skip to content

Commit

Permalink
LNK-2187 Refactor MVP tenant and report summaries (#723)
Browse files Browse the repository at this point in the history
* * Facilities in UI only displays a single line per facility, not a separate line/row per report
* Activities in UI only shows the number of patients (in-ip out of total).
* Changed ApiConfig to capture short/long name for each measure def - the api config will need to be udpated when deployed

* Removing run configuration from git

* Updated Javadocs

* Simplifying the process for getting a unique list of measures scheduled for a tenant

* Simplfying reading get-reports.sql as a string

* Using `StringUtils.containsIgnoreCase` instead of `property.toLowerCase().contains(value.toLowerCase())`

* Correct SQL for getting reports including patient counts to not duplicate records
Fixing bug in use of `IOUtils.resourceToString()`

* De-duplicating logic that looks up a measure definition by id from the ApiConfig
  • Loading branch information
seanmcilvenna committed Mar 15, 2024
1 parent ace3451 commit 002681a
Show file tree
Hide file tree
Showing 19 changed files with 238 additions and 182 deletions.
19 changes: 0 additions & 19 deletions .idea/runConfigurations/API_DEV___No_Scheduling.xml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
import com.lantanagroup.link.config.api.ApiConfig;
import com.lantanagroup.link.db.SharedService;
import com.lantanagroup.link.db.model.User;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -52,7 +52,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
logger.debug("Validating the requesting IP address against the token IP address.");
DecodedJWT jwt = JWT.decode(authHeader.substring(7));

if (!jwt.getClaim("ip").isNull() && !"0:0:0:0:0:0:0:1(0:0:0:0:0:0:0:1)".equals(ipAddress) && !jwt.getClaim("ip").asString().equals(ipAddress)) {
if (!jwt.getClaim("ip").isNull() && !"127.0.0.1(127.0.0.1)".equals(ipAddress) && !"0:0:0:0:0:0:0:1(0:0:0:0:0:0:0:1)".equals(ipAddress) && !jwt.getClaim("ip").asString().equals(ipAddress)) {
throw new JWTVerificationException("IP Address does not match.");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ public List<GlobalReportResponse> getReports(
reports = reports.stream().filter(x -> x.getStatus().equals(ReportStatuses.valueOf(status))).collect(Collectors.toList());
}

if(measureIds.length() > 0)
{
if (!measureIds.isEmpty()) {
String[] ids = measureIds.split(",");
reports = reports.stream().filter(x -> {
HashSet<String> measureIdsSet = new HashSet<>(x.getMeasureIds());
Expand All @@ -72,7 +71,11 @@ public List<GlobalReportResponse> getReports(
}).collect(Collectors.toList());
}

reports = reports.stream().skip((long) (page -1) * count).limit(count).collect(Collectors.toList());
reports = reports
.stream()
.skip((long) (page - 1) * count)
.limit(count)
.collect(Collectors.toList());

return reports;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import ca.uhn.fhir.rest.api.EncodingEnum;
import com.lantanagroup.link.FhirContextProvider;
import com.lantanagroup.link.FhirDataProvider;
import com.lantanagroup.link.FhirHelper;
import com.lantanagroup.link.Helper;
import com.lantanagroup.link.api.MeasureServiceWrapper;
import com.lantanagroup.link.config.api.ApiConfig;
import com.lantanagroup.link.config.api.MeasureDefConfig;
import com.lantanagroup.link.db.SharedService;
import com.lantanagroup.link.db.model.MeasureDefinition;
import com.lantanagroup.link.db.model.MeasurePackage;
Expand Down Expand Up @@ -86,11 +86,12 @@ public void createOrUpdateMeasureDef(@RequestBody(required = false) Bundle bundl
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Either a Bundle must be specified in a JSON body of the request, or a \"measureId\" query parameter must be specified");
}

if (StringUtils.isNotEmpty(measureId) && this.apiConfig.getMeasureDefUrls().get(measureId) == null) {
MeasureDefConfig foundMeasureDef = this.apiConfig.getMeasureDefinition(measureId);
if (StringUtils.isNotEmpty(measureId) && foundMeasureDef == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "The specified measureId is not configured with a measure definition URL");
}

String url = StringUtils.isNotEmpty(measureId) ? this.apiConfig.getMeasureDefUrls().get(measureId) : null;
String url = foundMeasureDef != null ? foundMeasureDef.getDefinitionUrl() : null;
Bundle bundle = bundleBody == null ? this.getBundleFromUrl(url) : bundleBody;

if (bundle == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.lantanagroup.link.api.scheduling.Scheduler;
import com.lantanagroup.link.db.SharedService;
import com.lantanagroup.link.db.TenantService;
import com.lantanagroup.link.db.model.Report;
import com.lantanagroup.link.db.model.tenant.Tenant;
import com.lantanagroup.link.db.model.tenant.TenantVendors;
import com.lantanagroup.link.model.SearchTenantResponse;
Expand Down Expand Up @@ -49,7 +50,7 @@ public List<SearchTenantResponse> searchTenants() {
}

@GetMapping("summary")
public TenantSummaryResponse searchTenantSummaries(@RequestParam(required = false) String searchCriteria, @RequestParam(defaultValue = "NAME", required = false) String sort, @RequestParam(defaultValue = "1", required = false) int page, @RequestParam(defaultValue = "true", required = false) boolean sortAscend) {
public TenantSummaryResponse searchTenants(@RequestParam(required = false) String searchCriteria, @RequestParam(defaultValue = "NAME", required = false) String sort, @RequestParam(defaultValue = "1", required = false) int page, @RequestParam(defaultValue = "true", required = false) boolean sortAscend) {
// validation
int itemsPerPage = 5;

Expand All @@ -67,9 +68,22 @@ public TenantSummaryResponse searchTenantSummaries(@RequestParam(required = fals
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid sort criteria. Valid values are NAME, NHSN_ORG_ID, SUBMISSION_DATE");
}
}
List<TenantSummary> tenants = this.sharedService.getTenantSummary(searchCriteria, TenantSummarySort.valueOf(sort.trim()), sortAscend).stream().collect(Collectors.toList());

List<TenantSummary> results = tenants.stream().skip(skip).limit(itemsPerPage).collect(Collectors.toList());
List<TenantSummary> tenants = this.sharedService.searchTenants(searchCriteria, TenantSummarySort.valueOf(sort.trim()), sortAscend);
List<TenantSummary> results = tenants.stream()
.skip(skip)
.limit(itemsPerPage)
.collect(Collectors.toList());

results.forEach(t -> {
TenantService tenantService = TenantService.create(this.sharedService.getTenantConfig(t.getId()));
Report lastReport = tenantService.findLastReport();

if (lastReport != null) {
t.setLastSubmissionId(lastReport.getId());
t.setLastSubmissionDate(String.valueOf(lastReport.getSubmittedTime()));
}
});

TenantSummaryResponse response = new TenantSummaryResponse();
response.setTotal(tenants.size());
Expand Down
23 changes: 17 additions & 6 deletions core/src/main/java/com/lantanagroup/link/config/api/ApiConfig.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package com.lantanagroup.link.config.api;

import com.lantanagroup.link.config.YamlPropertySourceFactory;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.validation.annotation.Validated;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Getter
Expand Down Expand Up @@ -159,9 +158,9 @@ public class ApiConfig {
private boolean noScheduling = false;

/**
* <strong>api.measure-def-urls</strong><br>A set of URLs representing the latest measure definition, keyed by measure ID
* <strong>api.measure-definitions</strong><br>Each of the measure definitions supported by the installation, by identifier, short name, long name and the url for where the definition lives.
*/
private HashMap<String, String> measureDefUrls = new HashMap<>();
private List<MeasureDefConfig> measureDefinitions = new ArrayList<>();

/**
* Allows use of QA debugging endpoints. DO NOT ALLOW IN PRODUCTION!!!!
Expand All @@ -175,12 +174,24 @@ public class ApiConfig {
*/
private String validationPackagesPath = "classpath:/packages/**";


/**
* Configuration for how to query the MRP (reporting plan) interface at CDC/NHSN to determine if a facility/tenant
* is signed up to report during the calculated reporting period.
*/
private ReportingPlan reportingPlan;

/**
* Finds a single measure definition by it's ID
* @param measureId The id of the measure to find
* @return MeasureDefConfig
*/
public MeasureDefConfig getMeasureDefinition(String measureId) {
return measureDefinitions
.stream()
.filter(m -> m.getId().equals(measureId))
.findFirst()
.orElse(null);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.lantanagroup.link.config.api;

import lombok.Getter;
import lombok.Setter;

/**
* Configuration for a supported measure definition within the installation
*/
@Getter
@Setter
public class MeasureDefConfig {
/**
* The unique identifier for the measure definition
*/
private String id;

/**
* The short name for the measure definition (i.e. "Hypo")
*/
private String shortName;

/**
* The long name for the measure definition (i.e. "Hypoglycemia")
*/
private String longName;

/**
* The URL where the latest version of the measure definition lives
*/
private String definitionUrl;
}

0 comments on commit 002681a

Please sign in to comment.