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

Sync release with master #183

Closed
wants to merge 3 commits into from
Closed
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
@@ -1,12 +1,12 @@
package edu.harvard.dbmi.avillach.data.entity;

import java.io.StringReader;
import java.util.Optional;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.*;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand All @@ -18,6 +18,9 @@ public class Resource extends BaseEntity{
@Column(length = 8192)
private String description;
private String targetURL;


@Convert(converter = ResourcePathConverter.class)
private String resourceRSPath;

@Column(length = 8192)
Expand Down Expand Up @@ -102,4 +105,31 @@ public String toString() {
.add("metadata", metadataObj)
.build().toString();
}

/**
* This resource path converter allows resource paths to contain a reference to a specific stack that is
* imputed at runtime based an an environment parameter. This allows multiple stacks to share the same database.
*
* The ___target_stack___ token in any resource path value will be replaced with the TARGET_STACK environment variable
*/
@Converter
public static class ResourcePathConverter implements AttributeConverter<String, String> {

public ResourcePathConverter() {
}

private static final Optional<String> targetStack = Optional.ofNullable(System.getProperty("TARGET_STACK", null));

@Override
public String convertToDatabaseColumn(String attribute) {
return attribute;
}

@Override
public String convertToEntityAttribute(String dbData) {
return targetStack
.map(stack -> dbData.replace("___target_stack___", stack))
.orElse(dbData);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@
import edu.harvard.dbmi.avillach.data.entity.Query;

import javax.enterprise.context.ApplicationScoped;
import javax.persistence.PersistenceException;
import javax.transaction.Transactional;
import java.util.UUID;

@Transactional
@ApplicationScoped
public class QueryRepository extends BaseRepository<Query, UUID>{
public class QueryRepository extends BaseRepository<Query, UUID> {

protected QueryRepository() {super(Query.class);}
protected QueryRepository() {
super(Query.class);
}

public Query getQueryUUIDFromCommonAreaUUID(UUID caID) {
String caIDRegex = "%commonAreaUUID\":\"" + caID + "\"%";
String query = "SELECT * FROM query WHERE CONVERT(metadata USING utf8) LIKE ?";
try {
return (Query) em().createNativeQuery(query, Query.class).setParameter(1, caIDRegex).getSingleResult();
} catch (PersistenceException ignored) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ protected ResourceRepository() {
super(Resource.class);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -21,55 +21,55 @@

public class PicsureInfoService {

private final Logger logger = LoggerFactory.getLogger(PicsureQueryService.class);
private final Logger logger = LoggerFactory.getLogger(PicsureQueryService.class);

private final static ObjectMapper mapper = new ObjectMapper();
private final static ObjectMapper mapper = new ObjectMapper();

@Inject
ResourceRepository resourceRepo;
@Inject
ResourceRepository resourceRepo;

@Inject
ResourceWebClient resourceWebClient;
@Inject
ResourceWebClient resourceWebClient;

/**
* Retrieve resource info for a specific resource.
*
* @param resourceId - Resource UUID
* @param credentialsQueryRequest - Contains resource specific credentials map
* @return a {@link edu.harvard.dbmi.avillach.domain.ResourceInfo ResourceInfo}
*/
public ResourceInfo info(UUID resourceId, QueryRequest credentialsQueryRequest, HttpHeaders headers) {
Resource resource = resourceRepo.getById(resourceId);
if (resource == null){
throw new ProtocolException(ProtocolException.RESOURCE_NOT_FOUND + resourceId.toString());
}
if (resource.getResourceRSPath() == null){
throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH);
}
if (credentialsQueryRequest == null){
credentialsQueryRequest = new GeneralQueryRequest();
}
if (credentialsQueryRequest.getResourceCredentials() == null){
credentialsQueryRequest.setResourceCredentials(new HashMap<String, String>());
}
/**
* Retrieve resource info for a specific resource.
*
* @param resourceId - Resource UUID
* @param credentialsQueryRequest - Contains resource specific credentials map
* @return a {@link edu.harvard.dbmi.avillach.domain.ResourceInfo ResourceInfo}
*/
public ResourceInfo info(UUID resourceId, QueryRequest credentialsQueryRequest, HttpHeaders headers) {
Resource resource = resourceRepo.getById(resourceId);
if (resource == null) {
throw new ProtocolException(ProtocolException.RESOURCE_NOT_FOUND + resourceId.toString());
}
if (resource.getResourceRSPath() == null) {
throw new ApplicationException(ApplicationException.MISSING_RESOURCE_PATH);
}
if (credentialsQueryRequest == null) {
credentialsQueryRequest = new GeneralQueryRequest();
}
if (credentialsQueryRequest.getResourceCredentials() == null) {
credentialsQueryRequest.setResourceCredentials(new HashMap<String, String>());
}

logger.info("path=/info/{resourceId}, resourceId={}, requestSource={}, credentialsQueryRequest={}",
resourceId,
Utilities.getRequestSourceFromHeader(headers),
Utilities.convertQueryRequestToString(mapper, credentialsQueryRequest)
);
logger.info(
"path=/info/{resourceId}, resourceId={}, requestSource={}, credentialsQueryRequest={}", resourceId,
Utilities.getRequestSourceFromHeader(headers), Utilities.convertQueryRequestToString(mapper, credentialsQueryRequest)
);

credentialsQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken());
return resourceWebClient.info(resource.getResourceRSPath(), credentialsQueryRequest);
}
credentialsQueryRequest.getResourceCredentials().put(ResourceWebClient.BEARER_TOKEN_KEY, resource.getToken());
return resourceWebClient.info(resource.getResourceRSPath(), credentialsQueryRequest);
}

/**
* Retrieve a list of all available resources.
*
* @return List containing limited metadata about all available resources and ids.
*/
public Map<UUID, String> resources(HttpHeaders headers) {
logger.info("path=/info/resources, requestSource={}", Utilities.getRequestSourceFromHeader(headers));
return resourceRepo.list().stream().collect(Collectors.toMap(Resource::getUuid, Resource::getName));
}
/**
* Retrieve a list of all available resources.
*
* @return List containing limited metadata about all available resources and ids.
*/
public Map<UUID, String> resources(HttpHeaders headers) {
logger.info("path=/info/resources, requestSource={}", Utilities.getRequestSourceFromHeader(headers));
return resourceRepo.list().stream().filter(resource -> !resource.getHidden())
.collect(Collectors.toMap(Resource::getUuid, Resource::getName));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ public Response querySync(QueryRequest queryRequest, HttpHeaders headers) {
*/
public QueryStatus queryMetadata(UUID queryId, HttpHeaders headers) {
Query query = queryRepo.getById(queryId);
query = query == null ? queryRepo.getQueryUUIDFromCommonAreaUUID(queryId) : query;
if (query == null) {
throw new ProtocolException(ProtocolException.QUERY_NOT_FOUND + queryId.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,8 @@ private static Map<String, Integer> bucketData(Map<String, Integer> originalMap)
int numBins = calcNumBins(data);
double min = data.keySet().stream().min(Double::compareTo).orElse(0.0);
double max = data.keySet().stream().max(Double::compareTo).orElse(0.0);

if ((min == 0.0 && max == 0.0) || numBins == 0) return new HashMap<>();
// The min and max can both be 0, but we could still have a numBins of 1 if there are values in the data.
if (min == 0.0 && max == 0.0 && numBins == 0) return new HashMap<>();

int binSize = (int) Math.ceil((max - min) / numBins);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package edu.harvard.hms.dbmi.avillach.resource.visualization.service;

import edu.harvard.dbmi.avillach.util.VisualizationUtil;
import org.junit.Test;
import org.junit.jupiter.api.DisplayName;

import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;

public class VisualizationUtilTests {

@Test
@DisplayName("Test limitKeySize")
public void testLimitKeySizeUniqueness() {
Map<String, Integer> axisMap = new HashMap<>(Map.of(
"Disease-Specific (Asthma, Allergy and Inflammation, PUB)", 1,
"Disease-Specific (Asthma, Allergy and Inflammation, PUB, NPU)", 1,
"Disease-Specific (Asthma, Allergy and Inflammation, NPU)", 1,
"Disease-Specific (Asthma, Allergy and Inflammation)", 1
));

Map<String, Integer> actual = VisualizationUtil.limitKeySize(axisMap);

Map<String, Integer> expected = Map.of(
"Disease-Specific (Asthma, Allergy an..., PUB)", 1,
"Disease-Specific (Asthma, Allergy an...ation)", 1,
"Disease-Specific (Asthma, Allergy an..., NPU)", 1,
"Disease-Specific (Asthma, Allergy a...B, NPU)", 1
);
assertEquals(expected, actual);
}

@Test
@DisplayName("Test Empty Map limitKeySize")
public void testEmptyMapLimitKeySize() {
Map<String, Integer> axisMap = new HashMap<>();
Map<String, Integer> actual = VisualizationUtil.limitKeySize(axisMap);
Map<String, Integer> expected = new HashMap<>();
assertEquals(expected, actual);
}

@Test
@DisplayName("Test null Map limitKeySize")
public void testNullMapLimitKeySize() {
Map<String, Integer> axisMap = null;
// this should throw a NullPointerException
try {
VisualizationUtil.limitKeySize(axisMap);
} catch (IllegalArgumentException e) {
assertEquals("axisMap cannot be null", e.getMessage());
}
}

@Test
@DisplayName("Test with no long keys limitKeySize")
public void testNoLongKeysLimitKeySize() {
// Test with no long keys
Map<String, Integer> axisMap = new HashMap<>();
for (int i = 0; i < 10; i++) {
axisMap.put("key" + i, 1);
}
Map<String, Integer> actual = VisualizationUtil.limitKeySize(axisMap);
Map<String, Integer> expected = new HashMap<>(axisMap);
assertEquals(expected, actual);
}

@Test
@DisplayName("Test with keys of greater than 45 characters and uniqueness is near middle limitKeySize")
public void testKeysOfGreaterLengthAndUniquenessNearMiddleLimitKeySize() {
Map<String, Integer> axisMap = new HashMap<>();
axisMap.put("Hello, this is a long key that is STRING1 greater than 45 characters and is unique", 1);
axisMap.put("Hello, this is a long key that is STRING2 greater than 45 characters and is unique", 1);
axisMap.put("Hello, this is a long key that is STRING3 greater than 45 characters and is unique", 1);

Map<String, Integer> actual = VisualizationUtil.limitKeySize(axisMap);

// loop through the keys and check if they are less than 45 characters
for (String key : actual.keySet()) {
assertEquals(45, key.length());
}

Map<String, Integer> expected = Map.of(
"Hello, this is a long key that is ST...unique", 1,
"Hello, this is a long key that is S... unique", 1,
"Hello, this is a long key that is ...s unique", 1
);

assertEquals(expected, actual);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -69,25 +69,50 @@ public static Map<String, Integer> doProcessResults(Map<String, Integer> axisMap
}

/**
* Replaces long column names with shorter version.
* This method is used to limit the size of the keys in the axisMap to a maximum of 45 characters. If the key is longer
* than 45 characters, it will be shortened to 45 characters and the last 3 characters will be replaced with "...".
* If the shortened key is not unique, we will create a unique one
* <p>
*
* @param axisMap
* @return
* @param axisMap - Map of the categories and their counts
* @return Map<String, Integer> - Map of the categories and their counts with the keys limited to 45 characters
*/
private static Map<String, Integer> limitKeySize(Map<String, Integer> axisMap) {
List<String> toRemove = new ArrayList<>();
Map<String, Integer> toAdd = new HashMap<>();
axisMap.keySet().forEach(key -> {
if (key.length() > MAX_X_LABEL_LINE_LENGTH) {
toRemove.add(key);
toAdd.put(
key.substring(0, MAX_X_LABEL_LINE_LENGTH - 3) + "...",
axisMap.get(key));
}
public static Map<String, Integer> limitKeySize(Map<String, Integer> axisMap) {
if (axisMap == null) {
throw new IllegalArgumentException("axisMap cannot be null");
}

Map<String, Integer> newAxisMap = new HashMap<>();
HashSet<String> keys = new HashSet<>();
axisMap.forEach((key, value) -> {
String adjustedKey = key.length() < MAX_X_LABEL_LINE_LENGTH ? key : createAdjustedKey(axisMap, keys, key);
newAxisMap.put(adjustedKey, value);
keys.add(adjustedKey);
});
toRemove.forEach(key -> axisMap.remove(key));
axisMap.putAll(toAdd);
return axisMap;
return newAxisMap;
}

private static String createAdjustedKey(Map<String, Integer> axisMap, HashSet<String> keys, String key) {
String keyPrefix = key.substring(0, MAX_X_LABEL_LINE_LENGTH);
return isKeyPrefixInAxisMap(axisMap, keyPrefix) ? generateUniqueKey(keys, key) : appendEllipsis(keyPrefix);
}

private static boolean isKeyPrefixInAxisMap(Map<String, Integer> axisMap, String keyPrefix) {
return axisMap.keySet().stream().anyMatch(k -> k.startsWith(keyPrefix));
}

private static String generateUniqueKey(HashSet<String> keys, String key) {
int countFromEnd = 6;
String proposedKey;
do {
proposedKey = String.format("%s...%s", key.substring(0, MAX_X_LABEL_LINE_LENGTH - 3 - countFromEnd), key.substring(key.length() - countFromEnd));
countFromEnd++;
} while (keys.contains(proposedKey));
return proposedKey;
}

private static String appendEllipsis(String keyPrefixAdjusted) {
return String.format("%s...", keyPrefixAdjusted);
}

}
Loading