Skip to content

Commit cdd4caf

Browse files
author
Luke Sikina
committed
[ALS-IDK] Stigmatizing variables
1 parent a2ad148 commit cdd4caf

13 files changed

+124
-86
lines changed

src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java

+35-10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import edu.harvard.dbmi.avillach.dictionary.filter.QueryParamPair;
66
import edu.harvard.dbmi.avillach.dictionary.util.MapExtractor;
77
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.beans.factory.annotation.Value;
89
import org.springframework.data.domain.Pageable;
910
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
1011
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@@ -17,35 +18,52 @@
1718
@Repository
1819
public class ConceptRepository {
1920

20-
private final NamedParameterJdbcTemplate template;
21+
private static final String ALLOW_FILTERING_Q = """
22+
WITH allow_filtering AS (
23+
SELECT
24+
concept_node.concept_node_id AS concept_node_id,
25+
(string_agg(concept_node_meta.value, ' ') NOT LIKE '%yes%') AS allowFiltering
26+
FROM
27+
concept_node
28+
JOIN concept_node_meta ON
29+
concept_node.concept_node_id = concept_node_meta.concept_node_id
30+
AND concept_node_meta.KEY IN (:disallowed_meta_keys)
31+
GROUP BY
32+
concept_node.concept_node_id
33+
)
34+
""";
2135

36+
private final NamedParameterJdbcTemplate template;
2237
private final ConceptRowMapper mapper;
23-
2438
private final ConceptFilterQueryGenerator filterGen;
2539
private final ConceptMetaExtractor conceptMetaExtractor;
2640
private final ConceptResultSetExtractor conceptResultSetExtractor;
41+
private final List<String> disallowedMetaFields;
2742

2843

2944
@Autowired
3045
public ConceptRepository(
3146
NamedParameterJdbcTemplate template, ConceptRowMapper mapper, ConceptFilterQueryGenerator filterGen,
32-
ConceptMetaExtractor conceptMetaExtractor, ConceptResultSetExtractor conceptResultSetExtractor
47+
ConceptMetaExtractor conceptMetaExtractor, ConceptResultSetExtractor conceptResultSetExtractor,
48+
@Value("${filtering.unfilterable_concepts}") List<String> disallowedMetaFields
3349
) {
3450
this.template = template;
3551
this.mapper = mapper;
3652
this.filterGen = filterGen;
3753
this.conceptMetaExtractor = conceptMetaExtractor;
3854
this.conceptResultSetExtractor = conceptResultSetExtractor;
55+
this.disallowedMetaFields = disallowedMetaFields;
3956
}
4057

4158

4259
public List<Concept> getConcepts(Filter filter, Pageable pageable) {
43-
String sql = """
60+
String sql = ALLOW_FILTERING_Q + """
4461
SELECT
4562
concept_node.*,
4663
ds.REF as dataset,
4764
continuous_min.VALUE as min, continuous_max.VALUE as max,
4865
categorical_values.VALUE as values,
66+
allow_filtering.allowFiltering AS allowFiltering,
4967
meta_description.VALUE AS description
5068
FROM
5169
concept_node
@@ -54,12 +72,13 @@ public List<Concept> getConcepts(Filter filter, Pageable pageable) {
5472
LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min'
5573
LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max'
5674
LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values'
75+
LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id
5776
WHERE concept_node.concept_node_id IN (
5877
5978
""";
6079
QueryParamPair filterQ = filterGen.generateFilterQuery(filter, pageable);
6180
sql = sql + filterQ.query() + "\n)";
62-
MapSqlParameterSource params = filterQ.params();
81+
MapSqlParameterSource params = filterQ.params().addValue("disallowed_meta_keys", disallowedMetaFields);
6382

6483
return template.query(sql, params, mapper);
6584
}
@@ -72,12 +91,13 @@ public long countConcepts(Filter filter) {
7291
}
7392

7493
public Optional<Concept> getConcept(String dataset, String conceptPath) {
75-
String sql = """
94+
String sql = ALLOW_FILTERING_Q + """
7695
SELECT
7796
concept_node.*,
7897
ds.REF as dataset,
7998
continuous_min.VALUE as min, continuous_max.VALUE as max,
8099
categorical_values.VALUE as values,
100+
allow_filtering.allowFiltering AS allowFiltering,
81101
meta_description.VALUE AS description
82102
FROM
83103
concept_node
@@ -86,13 +106,15 @@ public Optional<Concept> getConcept(String dataset, String conceptPath) {
86106
LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min'
87107
LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max'
88108
LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values'
109+
LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id
89110
WHERE
90111
concept_node.concept_path = :conceptPath
91112
AND ds.REF = :dataset
92113
""";
93114
MapSqlParameterSource params = new MapSqlParameterSource()
94115
.addValue("conceptPath", conceptPath)
95-
.addValue("dataset", dataset);
116+
.addValue("dataset", dataset)
117+
.addValue("disallowed_meta_keys", disallowedMetaFields);
96118
return template.query(sql, params, mapper).stream().findFirst();
97119
}
98120

@@ -137,8 +159,8 @@ public Map<Concept, Map<String, String>> getConceptMetaForConcepts(List<Concept>
137159
}
138160

139161
public Optional<Concept> getConceptTree(String dataset, String conceptPath, int depth) {
140-
String sql = """
141-
WITH core_query AS (
162+
String sql = ALLOW_FILTERING_Q + """
163+
, core_query AS (
142164
WITH RECURSIVE nodes AS (
143165
SELECT
144166
concept_node_id, parent_id, 0 AS depth
@@ -196,6 +218,7 @@ WITH RECURSIVE nodes AS (
196218
continuous_min.VALUE AS min, continuous_max.VALUE AS max,
197219
categorical_values.VALUE AS values,
198220
meta_description.VALUE AS description,
221+
allow_filtering.allowFiltering AS allowFiltering,
199222
core_query.depth AS depth
200223
FROM
201224
concept_node
@@ -205,11 +228,13 @@ WITH RECURSIVE nodes AS (
205228
LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min'
206229
LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max'
207230
LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values'
231+
LEFT JOIN allow_filtering ON concept_node.concept_node_id = allow_filtering.concept_node_id
208232
""";
209233
MapSqlParameterSource params = new MapSqlParameterSource()
210234
.addValue("path", conceptPath)
211235
.addValue("dataset", dataset)
212-
.addValue("depth", depth);
236+
.addValue("depth", depth)
237+
.addValue("disallowed_meta_keys", disallowedMetaFields);
213238

214239
if (depth < 0) {
215240
return Optional.empty();

src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptResultSetUtil.java

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public CategoricalConcept mapCategorical(ResultSet rs) throws SQLException {
2020
rs.getString("concept_path"), rs.getString("name"),
2121
rs.getString("display"), rs.getString("dataset"), rs.getString("description"),
2222
rs.getString("values") == null ? List.of() : parseValues(rs.getString("values")),
23+
rs.getBoolean("allowFiltering"),
2324
null,
2425
null
2526
);
@@ -29,6 +30,7 @@ public ContinuousConcept mapContinuous(ResultSet rs) throws SQLException {
2930
return new ContinuousConcept(
3031
rs.getString("concept_path"), rs.getString("name"),
3132
rs.getString("display"), rs.getString("dataset"), rs.getString("description"),
33+
rs.getBoolean("allowFiltering"),
3234
parseMin(rs.getString("values")), parseMax(rs.getString("values")),
3335
null
3436
);

src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/CategoricalConcept.java

+8-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public record CategoricalConcept(
1111
String conceptPath, String name, String display, String dataset, String description,
1212

13-
List<String> values,
13+
List<String> values, boolean allowFiltering,
1414

1515
@Nullable
1616
List<Concept> children,
@@ -28,21 +28,17 @@ public record CategoricalConcept(
2828

2929
public CategoricalConcept(
3030
String conceptPath, String name, String display, String dataset, String description, List<String> values,
31-
@Nullable List<Concept> children, @Nullable Map<String, String> meta
31+
boolean allowFiltering, @Nullable List<Concept> children, @Nullable Map<String, String> meta
3232
) {
33-
this(conceptPath, name, display, dataset, description, values, children, meta, null, null);
33+
this(conceptPath, name, display, dataset, description, values, allowFiltering, children, meta, null, null);
3434
}
3535

3636
public CategoricalConcept(CategoricalConcept core, Map<String, String> meta) {
37-
this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.values, core.children, meta);
38-
}
39-
40-
public CategoricalConcept(CategoricalConcept core, List<Concept> children) {
41-
this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.values, children, core.meta);
37+
this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.values, core.allowFiltering, core.children, meta);
4238
}
4339

4440
public CategoricalConcept(String conceptPath, String dataset) {
45-
this(conceptPath, "", "", dataset, "", List.of(), List.of(), null);
41+
this(conceptPath, "", "", dataset, "", List.of(), false, List.of(), null);
4642
}
4743

4844

@@ -54,20 +50,20 @@ public ConceptType type() {
5450

5551
@Override
5652
public CategoricalConcept withChildren(List<Concept> children) {
57-
return new CategoricalConcept(this, children);
53+
return new CategoricalConcept(conceptPath, name, display, dataset, description, values, allowFiltering, children, meta);
5854
}
5955

6056
@Override
6157
public Concept withTable(Concept table) {
6258
return new CategoricalConcept(
63-
conceptPath, name, display, dataset, description, values, children, meta, table, study
59+
conceptPath, name, display, dataset, description, values, allowFiltering, children, meta, table, study
6460
);
6561
}
6662

6763
@Override
6864
public Concept withStudy(Concept study) {
6965
return new CategoricalConcept(
70-
conceptPath, name, display, dataset, description, values, children, meta, table, study
66+
conceptPath, name, display, dataset, description, values, allowFiltering, children, meta, table, study
7167
);
7268
}
7369

src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/Concept.java

+4
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public sealed interface Concept
5858
@Nullable
5959
List<Concept> children();
6060

61+
default boolean allowFiltering() {
62+
return false;
63+
}
64+
6165
Concept withChildren(List<Concept> children);
6266

6367
Concept withTable(Concept table);

src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ContinuousConcept.java

+10-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import java.util.Objects;
1010

1111
public record ContinuousConcept(
12-
String conceptPath, String name, String display, String dataset, String description,
12+
String conceptPath, String name, String display, String dataset, String description, boolean allowFiltering,
1313

1414
@Nullable Integer min, @Nullable Integer max,
1515
Map<String, String> meta,
@@ -24,29 +24,25 @@ public record ContinuousConcept(
2424
) implements Concept {
2525

2626
public ContinuousConcept(
27-
String conceptPath, String name, String display, String dataset, String description,
27+
String conceptPath, String name, String display, String dataset, String description, boolean allowFiltering,
2828
@Nullable Integer min, @Nullable Integer max, Map<String, String> meta, @Nullable List<Concept> children
2929
) {
30-
this(conceptPath, name, display, dataset, description, min, max, meta, children, null, null);
30+
this(conceptPath, name, display, dataset, description, allowFiltering, min, max, meta, children, null, null);
3131
}
3232

3333
public ContinuousConcept(ContinuousConcept core, Map<String, String> meta) {
34-
this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.min, core.max, meta, core.children);
35-
}
36-
37-
public ContinuousConcept(ContinuousConcept core, List<Concept> children) {
38-
this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.min, core.max, core.meta, children);
34+
this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.allowFiltering, core.min, core.max, meta, core.children);
3935
}
4036

4137
public ContinuousConcept(String conceptPath, String dataset) {
42-
this(conceptPath, "", "", dataset, "", null, null, null, List.of());
38+
this(conceptPath, "", "", dataset, "", true, null, null, null, List.of());
4339
}
4440

4541
public ContinuousConcept(
46-
String conceptPath, String name, String display, String dataset, String description,
42+
String conceptPath, String name, String display, String dataset, String description, boolean allowFiltering,
4743
@Nullable Integer min, @Nullable Integer max, Map<String, String> meta
4844
) {
49-
this(conceptPath, name, display, dataset, description, min, max, meta, null);
45+
this(conceptPath, name, display, dataset, description, allowFiltering, min, max, meta, null);
5046
}
5147

5248
@JsonProperty("type")
@@ -57,20 +53,20 @@ public ConceptType type() {
5753

5854
@Override
5955
public ContinuousConcept withChildren(List<Concept> children) {
60-
return new ContinuousConcept(this, children);
56+
return new ContinuousConcept(conceptPath, name, display, dataset, description, allowFiltering, min, max, meta, children);
6157
}
6258

6359
@Override
6460
public Concept withTable(Concept table) {
6561
return new ContinuousConcept(
66-
conceptPath, name, display, dataset, description, min, max, meta, children, table, study
62+
conceptPath, name, display, dataset, description, allowFiltering, min, max, meta, children, table, study
6763
);
6864
}
6965

7066
@Override
7167
public Concept withStudy(Concept study) {
7268
return new ContinuousConcept(
73-
conceptPath, name, display, dataset, description, min, max, meta, children, table, study
69+
conceptPath, name, display, dataset, description, allowFiltering, min, max, meta, children, table, study
7470
);
7571
}
7672

src/main/resources/application-bdc.properties

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ spring.datasource.url=jdbc-secretsmanager:postgresql://${DATASOURCE_URL}/picsure
55
spring.datasource.username=${DATASOURCE_USERNAME}
66
server.port=80
77

8-
dashboard.enable.extra_details=true
8+
dashboard.enable.extra_details=true
9+
10+
filtering.unfilterable_concepts=stigmatizing

src/main/resources/application.properties

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ dashboard.columns={abbreviation:'Abbreviation',name:'Name',clinvars:'Clinical Va
99
dashboard.column-order=abbreviation,name,clinvars
1010
dashboard.nonmeta-columns=abbreviation,name
1111
dashboard.enable.extra_details=true
12-
dashboard.enable.bdc_hack=true
12+
dashboard.enable.bdc_hack=true
13+
14+
filtering.unfilterable_concepts=stigmatizing

0 commit comments

Comments
 (0)