Skip to content

Commit 593f5d3

Browse files
TDeSainLuke Sikina
authored and
Luke Sikina
committed
[ALS-7694] Add DashboardDrawer feature with controller, service, and repository (#55)
* Add Dashboard Drawer feature - Introduce DashboardDrawerController for drawer-related endpoints - Implement DashboardDrawerService to handle drawer logic - Create DashboardDrawerRepository for database interactions - Define DashboardDrawer and DashboardDrawerList records - Add DashboardDrawerRowMapper for result set mapping - Update DashboardRepository SQL to include dataset_id - Modify application properties to include dashboard layout configuration * Update application-bdc.properties with dashboard and filtering configs - Added `dashboard.enable.bdc_hack` property - Added `dashboard.layout.type` property - Moved `filtering.unfilterable_concepts` property - Ensured newline at end of file * Add 'program_name' column to Dashboard repository - Updated SQL query to include 'program_name' field. - Joined 'dataset_meta' table for 'program_name' metadata. - Added 'program_name' to the column list in DashboardService. * Refactor service and controller to handle Optional returns - Refactor `DashboardDrawerService` methods to return `Optional` - Update controller methods to handle `Optional` results with appropriate HTTP responses - Modify repository methods to return `Optional` for single item queries - Ensure consistency and error handling improvements across the service and controller layers
1 parent 2e1d6d3 commit 593f5d3

11 files changed

+222
-7
lines changed

src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardRepository.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public List<Map<String, String>> getHackyBDCRows() {
5252
String sql =
5353
"""
5454
SELECT
55+
dataset.dataset_id as dataset_id,
5556
dataset.abbreviation AS abbreviation,
5657
dataset.full_name AS name,
5758
CASE
@@ -70,10 +71,11 @@ public List<Map<String, String>> getHackyBDCRows() {
7071
END
7172
AS samples,
7273
CASE
73-
WHEN (consent.consent_code IS NOT NULL AND consent.consent_code != '') THEN concat(study_accession_meta.value, '.', consent.consent_code)
74+
WHEN (consent.consent_code IS NOT NULL AND consent.consent_code != '') THEN concat(study_accession_meta.value, '.', consent.consent_code)
7475
ELSE study_accession_meta.value
7576
END
7677
AS accession,
78+
program_name.value as program_name,
7779
study_focus_meta.value AS study_focus,
7880
additional_info_meta.value AS additional_info_link
7981
FROM
@@ -82,6 +84,7 @@ public List<Map<String, String>> getHackyBDCRows() {
8284
LEFT JOIN dataset_meta AS study_focus_meta ON study_focus_meta.dataset_id = dataset.dataset_id AND study_focus_meta.KEY = 'study_focus'
8385
LEFT JOIN dataset_meta AS study_accession_meta ON study_accession_meta.dataset_id = dataset.dataset_id AND study_accession_meta.KEY = 'study_accession'
8486
LEFT JOIN dataset_meta AS additional_info_meta ON additional_info_meta.dataset_id = dataset.dataset_id AND additional_info_meta.KEY = 'study_link'
87+
LEFT JOIN dataset_meta AS program_name ON program_name.dataset_id = dataset.dataset_id AND program_name.KEY = 'program_name'
8588
WHERE dataset.dataset_id NOT IN (select dataset_id from dataset_meta where KEY = 'show_dashboad' and VALUE = 'false')
8689
ORDER BY name ASC, abbreviation ASC
8790
""";

src/main/java/edu/harvard/dbmi/avillach/dictionary/dashboard/DashboardService.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ public Dashboard getDashboard() {
3434

3535
private static final List<DashboardColumn> hackyBDCColumns = List.of(
3636
new DashboardColumn("abbreviation", "Abbreviation"), new DashboardColumn("name", "Name"),
37-
new DashboardColumn("study_focus", "Study Focus"), new DashboardColumn("clinvars", "Clinical Variables"),
38-
new DashboardColumn("participants", "Participants"), new DashboardColumn("samples", "Samples Sequenced"),
39-
new DashboardColumn("accession", "Accession"), new DashboardColumn("additional_info_link", "Study Link")
37+
new DashboardColumn("study_focus", "Study Focus"), new DashboardColumn("program_name", "Program"),
38+
new DashboardColumn("participants", "Participants"), new DashboardColumn("clinvars", "Clinical Variables"),
39+
new DashboardColumn("samples", "Samples Sequenced"), new DashboardColumn("accession", "Accession"),
40+
new DashboardColumn("additional_info_link", "Study Link")
4041
);
4142
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package edu.harvard.dbmi.avillach.dictionary.dashboarddrawer;
2+
3+
import java.util.List;
4+
5+
public record DashboardDrawer(
6+
int datasetId, String studyFullname, String studyAbbreviation, List<String> consentGroups, String studySummary, List<String> studyFocus,
7+
String studyDesign, String sponsor
8+
) {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package edu.harvard.dbmi.avillach.dictionary.dashboarddrawer;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.stereotype.Controller;
6+
import org.springframework.web.bind.annotation.*;
7+
8+
@Controller
9+
@RequestMapping("/dashboard-drawer")
10+
public class DashboardDrawerController {
11+
12+
@Autowired
13+
private DashboardDrawerService dashboardDrawerService;
14+
15+
@GetMapping
16+
public ResponseEntity<DashboardDrawerList> findAll() {
17+
return dashboardDrawerService.findAll().map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
18+
}
19+
20+
@GetMapping("/{id}")
21+
public ResponseEntity<DashboardDrawer> findByDatasetId(@PathVariable Integer id) {
22+
return dashboardDrawerService.findByDatasetId(id).map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package edu.harvard.dbmi.avillach.dictionary.dashboarddrawer;
2+
3+
import java.util.List;
4+
5+
public record DashboardDrawerList(List<DashboardDrawer> dashboardDrawerList) {
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package edu.harvard.dbmi.avillach.dictionary.dashboarddrawer;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
7+
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
8+
import org.springframework.stereotype.Repository;
9+
10+
import java.util.*;
11+
12+
@Repository
13+
public class DashboardDrawerRepository {
14+
15+
private final NamedParameterJdbcTemplate template;
16+
17+
private static final Logger log = LoggerFactory.getLogger(DashboardDrawerRepository.class);
18+
19+
@Autowired
20+
public DashboardDrawerRepository(NamedParameterJdbcTemplate template) {
21+
this.template = template;
22+
}
23+
24+
public List<DashboardDrawer> getDashboardDrawerRows() {
25+
String materializedViewSql = """
26+
select * from dictionary_db.dict.dataset_meta_materialized_view dmmv;
27+
""";
28+
29+
String fallbackSql = """
30+
SELECT d.dataset_id,
31+
MAX(d.full_name) study_fullname,
32+
MAX(d.abbreviation) study_abbreviation,
33+
ARRAY_AGG(DISTINCT c.description) consent_groups,
34+
MAX(d.description) study_summary,
35+
ARRAY_AGG(DISTINCT dm.value) FILTER (where dm.key IN ('study_focus')) study_focus,
36+
MAX(DISTINCT dm.value) FILTER (where dm.key IN ('study_design')) study_design,
37+
MAX(DISTINCT dm.value) FILTER (where dm.key IN ('sponsor')) sponsor
38+
FROM dataset d
39+
JOIN dataset_meta dm ON d.dataset_id = dm.dataset_id
40+
JOIN consent c ON d.dataset_id = c.dataset_id
41+
GROUP BY d.dataset_id
42+
""";
43+
44+
try {
45+
return template.query(materializedViewSql, new DashboardDrawerRowMapper());
46+
} catch (Exception e) {
47+
log.debug("Materialized view not available, using fallback query. Error: {}", e.getMessage());
48+
return template.query(fallbackSql, new DashboardDrawerRowMapper());
49+
}
50+
}
51+
52+
public Optional<DashboardDrawer> getDashboardDrawerRows(Integer datasetId) {
53+
String materializedViewSql = """
54+
select * from dictionary_db.dict.dataset_meta_materialized_view dmmv where dmmv.dataset_id = :datasetId;
55+
""";
56+
57+
String fallbackSql = """
58+
SELECT d.dataset_id dataset_id,
59+
MAX(d.full_name) study_fullname,
60+
MAX(d.abbreviation) study_abbreviation,
61+
ARRAY_AGG(DISTINCT c.description) consent_groups,
62+
MAX(d.description) study_summary,
63+
ARRAY_AGG(DISTINCT dm.value) FILTER (where dm.key IN ('study_focus')) study_focus,
64+
MAX(DISTINCT dm.value) FILTER (where dm.key IN ('study_design')) study_design,
65+
MAX(DISTINCT dm.value) FILTER (where dm.key IN ('sponsor')) sponsor
66+
FROM dataset d
67+
JOIN dataset_meta dm ON d.dataset_id = dm.dataset_id
68+
JOIN consent c ON d.dataset_id = c.dataset_id
69+
where d.dataset_id = :datasetId
70+
GROUP BY d.dataset_id
71+
""";
72+
MapSqlParameterSource params = new MapSqlParameterSource();
73+
params.addValue("datasetId", datasetId);
74+
75+
try {
76+
return template.query(materializedViewSql, params, new DashboardDrawerRowMapper()).stream().findFirst();
77+
} catch (Exception e) {
78+
log.debug("Materialized view not available, using fallback query. Error: {}", e.getMessage());
79+
return template.query(fallbackSql, params, new DashboardDrawerRowMapper()).stream().findFirst();
80+
}
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package edu.harvard.dbmi.avillach.dictionary.dashboarddrawer;
2+
3+
import org.springframework.jdbc.core.RowMapper;
4+
5+
import java.sql.ResultSet;
6+
import java.sql.SQLException;
7+
import java.sql.Array; // For handling SQL Array
8+
import java.util.Arrays;
9+
import java.util.List;
10+
11+
public class DashboardDrawerRowMapper implements RowMapper<DashboardDrawer> {
12+
13+
@Override
14+
public DashboardDrawer mapRow(ResultSet rs, int rowNum) throws SQLException {
15+
return new DashboardDrawer(
16+
rs.getInt("dataset_id"), rs.getString("study_fullname"), rs.getString("study_abbreviation"),
17+
convertSqlArrayToList(rs.getArray("consent_groups")), rs.getString("study_summary"),
18+
convertSqlArrayToList(rs.getArray("study_focus")), rs.getString("study_design"), rs.getString("sponsor")
19+
);
20+
}
21+
22+
private List<String> convertSqlArrayToList(Array sqlArray) throws SQLException {
23+
if (sqlArray == null) {
24+
return List.of();
25+
} else {
26+
Object[] arrayContents = (Object[]) sqlArray.getArray();
27+
// Check if the array contains a single empty value
28+
if (arrayContents.length == 1 && "".equals(arrayContents[0])) {
29+
return List.of();
30+
} else {
31+
return Arrays.asList((String[]) sqlArray.getArray());
32+
}
33+
}
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package edu.harvard.dbmi.avillach.dictionary.dashboarddrawer;
2+
3+
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.stereotype.Service;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.Optional;
10+
11+
@Service
12+
public class DashboardDrawerService {
13+
14+
private final DashboardDrawerRepository repository;
15+
private final String dashboardLayout;
16+
17+
@Autowired
18+
public DashboardDrawerService(DashboardDrawerRepository repository, @Value("${dashboard.layout.type}") String dashboardLayout) {
19+
this.repository = repository;
20+
this.dashboardLayout = dashboardLayout;
21+
}
22+
23+
/**
24+
* Retrieves the Dashboard Drawer for all datasets.
25+
*
26+
* @return All Dashboard Instances and their metadata.
27+
*/
28+
public Optional<DashboardDrawerList> findAll() {
29+
if (dashboardLayout.equalsIgnoreCase("bdc")) {
30+
List<DashboardDrawer> records = repository.getDashboardDrawerRows();
31+
return Optional.of(new DashboardDrawerList(records));
32+
}
33+
34+
return Optional.of(new DashboardDrawerList(new ArrayList<>()));
35+
}
36+
37+
/**
38+
* Retrieves the Dashboard Drawer for a specific dataset.
39+
*
40+
*
41+
* @param datasetId the ID of the dataset to fetch.
42+
* @return a single Dashboard instance with drawer-specific metadata.
43+
*/
44+
public Optional<DashboardDrawer> findByDatasetId(Integer datasetId) {
45+
if ("bdc".equalsIgnoreCase(dashboardLayout)) {
46+
return repository.getDashboardDrawerRows(datasetId);
47+
}
48+
return Optional.empty();
49+
}
50+
}

src/main/resources/application-bdc.properties

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ spring.datasource.username=${DATASOURCE_USERNAME}
66
server.port=80
77

88
dashboard.enable.extra_details=true
9+
dashboard.enable.bdc_hack=true
10+
dashboard.layout.type=bdc
11+
12+
filtering.unfilterable_concepts=stigmatized
913

10-
filtering.unfilterable_concepts=stigmatized

src/main/resources/application.properties

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST}:5432/${POSTGRES_DB}?cur
33
spring.datasource.username=${POSTGRES_USER}
44
spring.datasource.password=${POSTGRES_PASSWORD}
55
spring.datasource.driver-class-name=org.postgresql.Driver
6+
67
server.port=80
78

89
dashboard.columns={abbreviation:'Abbreviation',name:'Name',clinvars:'Clinical Variables'}
910
dashboard.column-order=abbreviation,name,clinvars
1011
dashboard.nonmeta-columns=abbreviation,name
1112
dashboard.enable.extra_details=true
12-
dashboard.enable.bdc_hack=true
13+
dashboard.enable.bdc_hack=false
14+
dashboard.layout.type=default
1315

1416
filtering.unfilterable_concepts=stigmatized

src/test/resources/application.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ spring.datasource.driver-class-name=org.postgresql.Driver
77
dashboard.columns={abbreviation:'Abbreviation',melast:'This one goes last',name:'Name',clinvars:'Clinical Variables',participants:'Participants'}
88
dashboard.column-order=abbreviation,name,clinvars
99
dashboard.nonmeta-columns=abbreviation,name
10-
1110
dashboard.enable.extra_details=true
1211
dashboard.enable.bdc_hack=false
12+
dashboard.layout.type=default
1313

1414
filtering.unfilterable_concepts=stigmatized

0 commit comments

Comments
 (0)