Skip to content

Commit

Permalink
[ALS-7437] Switch study to dataset for concept node detail
Browse files Browse the repository at this point in the history
  • Loading branch information
Luke Sikina committed Oct 10, 2024
1 parent 2f38774 commit 7b239a5
Show file tree
Hide file tree
Showing 15 changed files with 290 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package edu.harvard.dbmi.avillach.dictionary.concept;

import edu.harvard.dbmi.avillach.dictionary.concept.model.Concept;
import edu.harvard.dbmi.avillach.dictionary.dataset.Dataset;
import edu.harvard.dbmi.avillach.dictionary.dataset.DatasetService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -9,6 +11,7 @@
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;

Expand All @@ -18,16 +21,18 @@ public class ConceptDecoratorService {
private static final Logger LOG = LoggerFactory.getLogger(ConceptDecoratorService.class);
private final boolean enabled;
private final ConceptService conceptService;
private final DatasetService datasetService;

private static final int COMPLIANT = 4, NON_COMPLIANT_TABLED = 3, NON_COMPLIANT_UNTABLED = 2;

@Autowired
public ConceptDecoratorService(
@Value("${dashboard.enable.extra_details}") boolean enabled,
@Lazy ConceptService conceptService // circular dep
@Lazy ConceptService conceptService, DatasetService datasetService // circular dep
) {
this.enabled = enabled;
this.conceptService = conceptService;
this.datasetService = datasetService;
}


Expand All @@ -51,16 +56,14 @@ public Concept populateParentConcepts(Concept concept) {
}

private Concept populateTabledConcept(Concept concept, List<String> conceptNodes) {
String studyPath = "\\" + String.join("\\", conceptNodes.subList(0, 1)) + "\\";
String tablePath = "\\" + String.join("\\", conceptNodes.subList(0, 2)) + "\\";
Concept study = conceptService.conceptDetailWithoutAncestors(concept.dataset(), studyPath).orElse(null);
Dataset study = datasetService.getDataset(concept.dataset()).orElse(null);
Concept table = conceptService.conceptDetailWithoutAncestors(concept.dataset(), tablePath).orElse(null);
return concept.withStudy(study).withTable(table);
}

private Concept populateNonCompliantTabledConcept(Concept concept, List<String> conceptNodes) {
String studyPath = String.join("\\", conceptNodes.subList(0, 1));
Concept study = conceptService.conceptDetail(concept.dataset(), studyPath).orElse(null);
Dataset study = datasetService.getDataset(concept.dataset()).orElse(null);
return concept.withStudy(study);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.harvard.dbmi.avillach.dictionary.concept.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import edu.harvard.dbmi.avillach.dictionary.dataset.Dataset;
import jakarta.annotation.Nullable;

import java.util.List;
Expand All @@ -22,7 +23,7 @@ public record CategoricalConcept(
Concept table,

@Nullable
Concept study
Dataset study

) implements Concept {

Expand Down Expand Up @@ -66,7 +67,7 @@ public Concept withTable(Concept table) {
}

@Override
public Concept withStudy(Concept study) {
public Concept withStudy(Dataset study) {
return new CategoricalConcept(
conceptPath, name, display, dataset, description, values, allowFiltering, studyAcronym, children, meta, table, study
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import edu.harvard.dbmi.avillach.dictionary.dataset.Dataset;
import jakarta.annotation.Nullable;

import java.util.List;
Expand Down Expand Up @@ -53,7 +54,7 @@ public sealed interface Concept

Concept table();

Concept study();
Dataset study();

Map<String, String> meta();

Expand All @@ -68,7 +69,7 @@ default boolean allowFiltering() {

Concept withTable(Concept table);

Concept withStudy(Concept study);
Concept withStudy(Dataset study);

default boolean conceptEquals(Object object) {
if (this == object) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.harvard.dbmi.avillach.dictionary.concept.model;

import edu.harvard.dbmi.avillach.dictionary.dataset.Dataset;
import jakarta.annotation.Nullable;

import java.util.List;
Expand Down Expand Up @@ -33,7 +34,7 @@ public Concept table() {
}

@Override
public Concept study() {
public Dataset study() {
return null;
}

Expand All @@ -58,7 +59,7 @@ public Concept withTable(Concept table) {
}

@Override
public Concept withStudy(Concept study) {
public Concept withStudy(Dataset study) {
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package edu.harvard.dbmi.avillach.dictionary.concept.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import edu.harvard.dbmi.avillach.dictionary.dataset.Dataset;
import jakarta.annotation.Nullable;

import java.util.ArrayList;
Expand All @@ -20,7 +21,7 @@ public record ContinuousConcept(
Concept table,

@Nullable
Concept study
Dataset study
) implements Concept {

public ContinuousConcept(
Expand Down Expand Up @@ -73,7 +74,7 @@ public Concept withTable(Concept table) {
}

@Override
public Concept withStudy(Concept study) {
public Concept withStudy(Dataset study) {
return new ContinuousConcept(
conceptPath, name, display, dataset, description, allowFiltering,
min, max, studyAcronym, meta, children, table, study
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package edu.harvard.dbmi.avillach.dictionary.dataset;

import jakarta.annotation.Nullable;

import java.util.Map;

public record Dataset(
String ref, String fullName, String abbreviation, String description, @Nullable Map<String, String> meta
) {

public Dataset(String ref, String fullName, String abbreviation, String description) {
this(ref, fullName, abbreviation, description, null);
}

public Dataset withMeta(Map<String, String> meta) {
return new Dataset(ref, fullName, abbreviation, description, meta);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package edu.harvard.dbmi.avillach.dictionary.dataset;

import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.SQLException;

@Component
public class DatasetMapper implements RowMapper<Dataset> {
@Override
public Dataset mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Dataset(
rs.getString("ref"), rs.getString("full_name"),
rs.getString("abbreviation"), rs.getString("description")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package edu.harvard.dbmi.avillach.dictionary.dataset;

import edu.harvard.dbmi.avillach.dictionary.concept.model.Concept;
import edu.harvard.dbmi.avillach.dictionary.util.MapExtractor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.Map;
import java.util.Optional;

@Repository
public class DatasetRepository {
private final NamedParameterJdbcTemplate template;
private final DatasetMapper mapper;
private final MapExtractor metaExtractor = new MapExtractor("key", "value");

@Autowired
public DatasetRepository(NamedParameterJdbcTemplate template, DatasetMapper mapper) {
this.template = template;
this.mapper = mapper;
}

public Optional<Dataset> getDataset(String ref) {
String sql = """
SELECT
ref, full_name, abbreviation, description
FROM
dataset
WHERE
dataset.REF = :ref
""";

MapSqlParameterSource params = new MapSqlParameterSource().addValue("ref", ref);

return template.query(sql, params, mapper).stream().findAny();
}

public Map<String, String> getDatasetMeta(String ref) {
String sql = """
SELECT
key, value
FROM
dataset_meta
LEFT JOIN dataset ON dataset_meta.dataset_id = dataset.dataset_id
WHERE
dataset.REF = :ref
""";
MapSqlParameterSource params = new MapSqlParameterSource().addValue("ref", ref);
return template.query(sql, params, metaExtractor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package edu.harvard.dbmi.avillach.dictionary.dataset;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Optional;

@Service
public class DatasetService {

private final DatasetRepository repository;


@Autowired
public DatasetService(DatasetRepository repository) {
this.repository = repository;
}

public Optional<Dataset> getDataset(String ref) {
Map<String, String> meta = repository.getDatasetMeta(ref);
return repository.getDataset(ref).map(ds -> ds.withMeta(meta));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import edu.harvard.dbmi.avillach.dictionary.concept.model.CategoricalConcept;
import edu.harvard.dbmi.avillach.dictionary.concept.model.Concept;
import edu.harvard.dbmi.avillach.dictionary.dataset.Dataset;
import edu.harvard.dbmi.avillach.dictionary.dataset.DatasetService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

import javax.print.attribute.DocAttributeSet;
import java.util.Optional;


Expand All @@ -18,18 +21,21 @@ class ConceptDecoratorServiceTest {
@MockBean
ConceptService conceptService;

@MockBean
DatasetService datasetService;

@Autowired
ConceptDecoratorService subject;

@Test
void shouldPopulateCompliantStudy() {
CategoricalConcept concept = new CategoricalConcept("\\study\\table\\idk\\concept\\", "dataset");
CategoricalConcept table = new CategoricalConcept("\\study\\table\\", "dataset");
CategoricalConcept study = new CategoricalConcept("\\study\\", "dataset");
Dataset study = new Dataset("dataset", "", "", "");

Mockito.when(conceptService.conceptDetail("dataset", table.dataset()))
.thenReturn(Optional.of(table));
Mockito.when(conceptService.conceptDetail("dataset", study.dataset()))
Mockito.when(datasetService.getDataset("dataset"))
.thenReturn(Optional.of(study));

Concept actual = subject.populateParentConcepts(concept);
Expand All @@ -42,11 +48,11 @@ void shouldPopulateCompliantStudy() {
void shouldPopulateNonCompliantTabledStudy() {
CategoricalConcept concept = new CategoricalConcept("\\study\\table\\concept\\", "dataset");
CategoricalConcept table = new CategoricalConcept("\\study\\table\\", "dataset");
CategoricalConcept study = new CategoricalConcept("\\study\\", "dataset");
Dataset study = new Dataset("dataset", "", "", "");

Mockito.when(conceptService.conceptDetail("dataset", table.dataset()))
.thenReturn(Optional.of(table));
Mockito.when(conceptService.conceptDetail("dataset", study.dataset()))
Mockito.when(datasetService.getDataset("dataset"))
.thenReturn(Optional.of(study));

Concept actual = subject.populateParentConcepts(concept);
Expand All @@ -58,9 +64,9 @@ void shouldPopulateNonCompliantTabledStudy() {
@Test
void shouldPopulateNonCompliantUnTabledStudy() {
CategoricalConcept concept = new CategoricalConcept("\\study\\concept\\", "dataset");
CategoricalConcept study = new CategoricalConcept("\\study\\", "dataset");
Dataset study = new Dataset("dataset", "", "", "");

Mockito.when(conceptService.conceptDetail("dataset", study.dataset()))
Mockito.when(datasetService.getDataset("dataset"))
.thenReturn(Optional.of(study));

Concept actual = subject.populateParentConcepts(concept);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ void shouldFilterConceptsByFacet() {
List<Concept> actual =
subject.getConcepts(new Filter(List.of(new Facet("phs000007", "", "", "", 1, null, "study_ids_dataset_ids", null)), "", List.of()), Pageable.unpaged());
List<? extends Record> expected = List.of(
new ContinuousConcept("\\phs000007\\pht000022\\phv00004260\\FM219\\", "phv00004260", "FM219", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA / DAY", true, 0F, 1F, "FHS", null),
new ContinuousConcept("\\phs000007\\pht000021\\phv00003844\\FL200\\", "phv00003844", "FL200", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA / DAY", true, 0F, 3F, "FHS", null),
new ContinuousConcept("\\phs000007\\pht000022\\phv00004260\\FM219\\", "phv00004260", "FM219", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA / DAY", true, 0F, 1F, "FHS", null),
new ContinuousConcept("\\phs000007\\pht000033\\phv00008849\\D080\\", "phv00008849", "D080", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA/DAY", true, 0F, 5F, "FHS", null)
);

Expand All @@ -91,8 +91,8 @@ void shouldFilterConceptsByFacet() {
void shouldFilterBySearch() {
List<Concept> actual = subject.getConcepts(new Filter(List.of(), "COLA", List.of()), Pageable.unpaged());
List<? extends Record> expected = List.of(
new ContinuousConcept("\\phs000007\\pht000022\\phv00004260\\FM219\\", "phv00004260", "FM219", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA / DAY", true, 0F, 1F, "FHS", null),
new ContinuousConcept("\\phs000007\\pht000021\\phv00003844\\FL200\\", "phv00003844", "FL200", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA / DAY", true, 0F, 3F, "FHS", null),
new ContinuousConcept("\\phs000007\\pht000022\\phv00004260\\FM219\\", "phv00004260", "FM219", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA / DAY", true, 0F, 1F, "FHS", null),
new ContinuousConcept("\\phs000007\\pht000033\\phv00008849\\D080\\", "phv00008849", "D080", "phs000007", "# 12 OZ CUPS OF CAFFEINATED COLA/DAY", true, 0F, 5F, "FHS", null)
);

Expand Down
Loading

0 comments on commit 7b239a5

Please sign in to comment.