Skip to content

Commit bb99c25

Browse files
authored
feat: added different levels of variable summary visibility #4467 (#4469)
* feat: added global summary statistics policy #4467 * feat: added simple flags for dealing with summary in the variable template * feat: OPEN_STATS renamed to OPEN_SUMMARY
1 parent f6fc0ac commit bb99c25

File tree

21 files changed

+192
-37
lines changed

21 files changed

+192
-37
lines changed

mica-core/src/main/java/org/obiba/mica/dataset/service/CollectedDatasetService.java

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.google.common.collect.Sets;
1616
import com.google.common.eventbus.EventBus;
1717
import com.google.common.eventbus.Subscribe;
18+
import org.apache.shiro.SecurityUtils;
1819
import org.obiba.magma.NoSuchValueTableException;
1920
import org.obiba.magma.NoSuchVariableException;
2021
import org.obiba.magma.ValueTable;
@@ -40,6 +41,8 @@
4041
import org.obiba.mica.dataset.event.DatasetUpdatedEvent;
4142
import org.obiba.mica.file.FileUtils;
4243
import org.obiba.mica.file.service.FileSystemService;
44+
import org.obiba.mica.micaConfig.domain.MicaConfig;
45+
import org.obiba.mica.micaConfig.domain.SummaryStatisticsAccessPolicy;
4346
import org.obiba.mica.micaConfig.service.MicaConfigService;
4447
import org.obiba.mica.micaConfig.service.OpalService;
4548
import org.obiba.mica.network.service.NetworkService;
@@ -518,6 +521,11 @@ protected EventBus getEventBus() {
518521
return eventBus;
519522
}
520523

524+
@Override
525+
protected MicaConfig getMicaConfig() {
526+
return micaConfigService.getConfig();
527+
}
528+
521529
//
522530
// Private methods
523531
//

mica-core/src/main/java/org/obiba/mica/dataset/service/DatasetService.java

+42
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import com.google.common.base.Strings;
1414
import com.google.common.eventbus.EventBus;
15+
import org.apache.shiro.SecurityUtils;
1516
import org.obiba.magma.MagmaRuntimeException;
1617
import org.obiba.magma.NoSuchValueTableException;
1718
import org.obiba.magma.NoSuchVariableException;
@@ -26,10 +27,13 @@
2627
import org.obiba.mica.dataset.NoSuchDatasetException;
2728
import org.obiba.mica.dataset.domain.Dataset;
2829
import org.obiba.mica.dataset.domain.DatasetVariable;
30+
import org.obiba.mica.micaConfig.domain.MicaConfig;
31+
import org.obiba.mica.micaConfig.domain.SummaryStatisticsAccessPolicy;
2932
import org.obiba.mica.micaConfig.service.OpalService;
3033
import org.obiba.mica.network.service.NetworkService;
3134
import org.obiba.mica.spi.tables.StudyTableSource;
3235
import org.obiba.mica.study.service.StudyService;
36+
import org.obiba.mica.web.model.Mica;
3337
import org.slf4j.Logger;
3438
import org.slf4j.LoggerFactory;
3539

@@ -77,6 +81,44 @@ public abstract DatasetVariable getDatasetVariable(T dataset, String name)
7781

7882
protected abstract EventBus getEventBus();
7983

84+
protected abstract MicaConfig getMicaConfig();
85+
86+
/**
87+
* Apply summary statistics visibility policy.
88+
*
89+
* @param summary
90+
* @return
91+
*/
92+
public Mica.DatasetVariableAggregationDto getFilteredVariableSummary(Mica.DatasetVariableAggregationDto summary) {
93+
SummaryStatisticsAccessPolicy policy = getMicaConfig().getSummaryStatisticsAccessPolicy();
94+
if (summary == null || policy.equals(SummaryStatisticsAccessPolicy.OPEN_ALL)) return summary;
95+
if (!SecurityUtils.getSubject().isAuthenticated()) {
96+
if (policy.equals(SummaryStatisticsAccessPolicy.OPEN_SUMMARY)) return summary;
97+
// strip out detailed stats because user is not authenticated
98+
summary = summary.toBuilder().clearFrequencies().build();
99+
summary = summary.toBuilder().clearStatistics().build();
100+
}
101+
return summary;
102+
}
103+
104+
/**
105+
* Apply summary statistics visibility policy.
106+
*
107+
* @param summary
108+
* @return
109+
*/
110+
public Mica.DatasetVariableAggregationsDto getFilteredVariableSummary(Mica.DatasetVariableAggregationsDto summary) {
111+
SummaryStatisticsAccessPolicy policy = getMicaConfig().getSummaryStatisticsAccessPolicy();
112+
if (summary == null || policy.equals(SummaryStatisticsAccessPolicy.OPEN_ALL)) return summary;
113+
if (!SecurityUtils.getSubject().isAuthenticated()) {
114+
if (policy.equals(SummaryStatisticsAccessPolicy.OPEN_SUMMARY)) return summary;
115+
// strip out detailed stats because user is not authenticated
116+
summary = summary.toBuilder().clearFrequencies().build();
117+
summary = summary.toBuilder().clearStatistics().build();
118+
}
119+
return summary;
120+
}
121+
80122
/**
81123
* Find all dataset identifiers.
82124
*

mica-core/src/main/java/org/obiba/mica/dataset/service/HarmonizedDatasetService.java

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.google.common.collect.Lists;
1616
import com.google.common.collect.Sets;
1717
import com.google.common.eventbus.EventBus;
18+
import org.apache.shiro.SecurityUtils;
1819
import org.obiba.magma.*;
1920
import org.obiba.magma.support.Disposables;
2021
import org.obiba.mica.NoSuchEntityException;
@@ -35,6 +36,8 @@
3536
import org.obiba.mica.dataset.event.DatasetUpdatedEvent;
3637
import org.obiba.mica.file.FileUtils;
3738
import org.obiba.mica.file.service.FileSystemService;
39+
import org.obiba.mica.micaConfig.domain.MicaConfig;
40+
import org.obiba.mica.micaConfig.domain.SummaryStatisticsAccessPolicy;
3841
import org.obiba.mica.micaConfig.service.MicaConfigService;
3942
import org.obiba.mica.micaConfig.service.OpalService;
4043
import org.obiba.mica.network.service.NetworkService;
@@ -438,6 +441,11 @@ protected EventBus getEventBus() {
438441
return eventBus;
439442
}
440443

444+
@Override
445+
protected MicaConfig getMicaConfig() {
446+
return micaConfigService.getConfig();
447+
}
448+
441449
//
442450
// Private methods
443451
//

mica-core/src/main/java/org/obiba/mica/micaConfig/domain/MicaConfig.java

+17
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ public class MicaConfig extends AbstractAuditableDocument implements Serializabl
131131

132132
private boolean variableSummaryRequiresAuthentication = false;
133133

134+
private SummaryStatisticsAccessPolicy summaryStatisticsAccessPolicy;
135+
134136
private boolean usePublicUrlForSharedLink = true;
135137

136138
private String style;
@@ -543,6 +545,21 @@ public boolean isVariableSummaryRequiresAuthentication() {
543545

544546
public void setVariableSummaryRequiresAuthentication(boolean variableSummaryRequiresAuthentication) {
545547
this.variableSummaryRequiresAuthentication = variableSummaryRequiresAuthentication;
548+
if (summaryStatisticsAccessPolicy == null) {
549+
summaryStatisticsAccessPolicy = variableSummaryRequiresAuthentication ? SummaryStatisticsAccessPolicy.RESTRICTED_ALL : SummaryStatisticsAccessPolicy.OPEN_ALL;
550+
}
551+
}
552+
553+
public SummaryStatisticsAccessPolicy getSummaryStatisticsAccessPolicy() {
554+
if (summaryStatisticsAccessPolicy == null) {
555+
summaryStatisticsAccessPolicy = variableSummaryRequiresAuthentication ? SummaryStatisticsAccessPolicy.RESTRICTED_ALL : SummaryStatisticsAccessPolicy.OPEN_ALL;
556+
}
557+
return summaryStatisticsAccessPolicy;
558+
}
559+
560+
public void setSummaryStatisticsAccessPolicy(SummaryStatisticsAccessPolicy summaryStatisticsAccessPolicy) {
561+
this.summaryStatisticsAccessPolicy = summaryStatisticsAccessPolicy;
562+
this.variableSummaryRequiresAuthentication = !SummaryStatisticsAccessPolicy.OPEN_ALL.equals(summaryStatisticsAccessPolicy);
546563
}
547564

548565
public boolean hasStyle() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2022 OBiBa. All rights reserved.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the GNU Public License v3.0.
6+
*
7+
* You should have received a copy of the GNU General Public License
8+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
9+
*/
10+
11+
package org.obiba.mica.micaConfig.domain;
12+
13+
/**
14+
* Specify the access policy to the variable stats.
15+
*/
16+
public enum SummaryStatisticsAccessPolicy {
17+
18+
OPEN_ALL, // open basic counts, detailed stats and contingency
19+
20+
OPEN_SUMMARY, // open basic counts and detailed stats, restricted contingency
21+
22+
OPEN_BASICS, // open basic counts, restricted detailed stats and contingency
23+
24+
RESTRICTED_ALL, // restricted basic counts, detailed stats and contingency
25+
26+
}

mica-core/src/main/java/org/obiba/mica/web/model/MicaConfigDtos.java

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ Mica.MicaConfigDto asDto(@NotNull MicaConfig config, String language) {
149149
builder.setIsCollectedDatasetEnabled(config.isStudyDatasetEnabled());
150150
builder.setIsHarmonizedDatasetEnabled(config.isHarmonizationDatasetEnabled());
151151
builder.setVariableSummaryRequiresAuthentication(config.isVariableSummaryRequiresAuthentication());
152+
builder.setSummaryStatisticsAccessPolicy(config.getSummaryStatisticsAccessPolicy().name());
152153
builder.setIsImportStudiesFeatureEnabled(config.isImportStudiesFeatureEnabled());
153154

154155
if(config.hasStyle()) builder.setStyle(config.getStyle());
@@ -249,6 +250,7 @@ MicaConfig fromDto(@NotNull Mica.MicaConfigDtoOrBuilder dto) {
249250
config.setHarmonizationDatasetEnabled(dto.getIsHarmonizedDatasetEnabled());
250251
config.setImportStudiesFeatureEnabled(dto.getIsImportStudiesFeatureEnabled());
251252
if (dto.hasVariableSummaryRequiresAuthentication()) config.setVariableSummaryRequiresAuthentication(dto.getVariableSummaryRequiresAuthentication());
253+
config.setSummaryStatisticsAccessPolicy(SummaryStatisticsAccessPolicy.valueOf(dto.getSummaryStatisticsAccessPolicy()));
252254

253255
if (dto.hasSignupEnabled()) config.setSignupEnabled(dto.getSignupEnabled());
254256
config.setSignupWithPassword(dto.getSignupWithPassword());

mica-rest/src/main/java/org/obiba/mica/dataset/rest/PublishedDatasetResource.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ public DatasetVariable getVariable(@PathParam("id") String id, @PathParam("proje
7474
@Path("/collected/{project}/{table}/{variableName}/_summary")
7575
public Mica.DatasetVariableAggregationDto getVariableSummary(@PathParam("id") String id, @PathParam("project") String project, @PathParam("table") String table, @PathParam("variableName") String variableName) {
7676
checkVariableSummaryAccess();
77-
return collectedDatasetService.getVariableSummary(alternativeStudyDataset(id, project, table), variableName);
77+
Mica.DatasetVariableAggregationDto summary = collectedDatasetService.getVariableSummary(alternativeStudyDataset(id, project, table), variableName);
78+
return collectedDatasetService.getFilteredVariableSummary(summary);
7879
}
7980

8081
private Dataset getDataset(String id) {

mica-search/src/main/java/org/obiba/mica/dataset/search/rest/AbstractPublishedDatasetResource.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.obiba.mica.dataset.domain.Dataset;
2121
import org.obiba.mica.dataset.domain.DatasetVariable;
2222
import org.obiba.mica.dataset.domain.HarmonizationDataset;
23+
import org.obiba.mica.micaConfig.domain.SummaryStatisticsAccessPolicy;
2324
import org.obiba.mica.micaConfig.service.MicaConfigService;
2425
import org.obiba.mica.micaConfig.service.TaxonomiesService;
2526
import org.obiba.mica.security.service.SubjectAclService;
@@ -308,10 +309,18 @@ protected List<Taxonomy> getTaxonomies() {
308309
protected void checkContingencyAccess() {
309310
if (!micaConfigService.getConfig().isContingencyEnabled())
310311
throw new ForbiddenException();
312+
if (micaConfigService.getConfig().getSummaryStatisticsAccessPolicy().equals(SummaryStatisticsAccessPolicy.OPEN_ALL))
313+
return;
314+
// other require authentication
315+
if (!SecurityUtils.getSubject().isAuthenticated())
316+
throw new ForbiddenException();
311317
}
312318

313319
protected void checkVariableSummaryAccess() {
314-
if (!SecurityUtils.getSubject().isAuthenticated() && micaConfigService.getConfig().isVariableSummaryRequiresAuthentication())
320+
if (micaConfigService.getConfig().getSummaryStatisticsAccessPolicy().name().startsWith("OPEN_"))
321+
return;
322+
// other require authentication
323+
if (!SecurityUtils.getSubject().isAuthenticated())
315324
throw new ForbiddenException();
316325
}
317326

mica-search/src/main/java/org/obiba/mica/dataset/search/rest/collection/PublishedCollectedDatasetVariableResource.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ public Mica.DatasetVariableAggregationDto getVariableAggregations(@QueryParam("s
6767
StudyDataset dataset = getDataset(StudyDataset.class, datasetId);
6868
StudyTable studyTable = dataset.getSafeStudyTable();
6969
try {
70-
return dtos.asDto(studyTable, datasetService.getVariableSummary(dataset, variableName), withStudySummary).build();
70+
Mica.DatasetVariableAggregationDto summary = datasetService.getVariableSummary(dataset, variableName);
71+
return dtos.asDto(studyTable, datasetService.getFilteredVariableSummary(summary), withStudySummary).build();
7172
} catch (Exception e) {
7273
if (log.isDebugEnabled())
7374
log.warn("Unable to retrieve statistics: {}", e.getMessage(), e);

mica-search/src/main/java/org/obiba/mica/dataset/search/rest/harmonization/PublishedDataschemaDatasetVariableResource.java

+11-14
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@
1616
import com.google.common.collect.LinkedListMultimap;
1717
import com.google.common.collect.Lists;
1818
import com.google.common.collect.Multimap;
19-
import jakarta.ws.rs.BadRequestException;
20-
import jakarta.ws.rs.DefaultValue;
21-
import jakarta.ws.rs.GET;
22-
import jakarta.ws.rs.Path;
23-
import jakarta.ws.rs.Produces;
24-
import jakarta.ws.rs.QueryParam;
19+
import jakarta.ws.rs.*;
2520
import jakarta.ws.rs.core.Response;
2621
import org.apache.commons.math3.util.Pair;
2722
import org.obiba.mica.core.domain.BaseStudyTable;
@@ -36,13 +31,13 @@
3631
import org.slf4j.LoggerFactory;
3732
import org.springframework.context.annotation.Scope;
3833
import org.springframework.scheduling.annotation.Async;
39-
import org.springframework.scheduling.annotation.AsyncResult;
4034
import org.springframework.stereotype.Component;
4135

4236
import javax.inject.Inject;
4337
import java.io.ByteArrayOutputStream;
4438
import java.io.IOException;
4539
import java.util.List;
40+
import java.util.concurrent.CompletableFuture;
4641
import java.util.concurrent.Future;
4742
import java.util.stream.Collectors;
4843

@@ -91,7 +86,8 @@ public Mica.DatasetVariableAggregationsDto getVariableAggregations(@QueryParam("
9186
BaseStudyTable opalTable = studyTables.get(i);
9287
Future<Mica.DatasetVariableAggregationDto> futureResult = results.get(i);
9388
try {
94-
builder.add(dtos.asDto(opalTable, futureResult.get(), withStudySummary).build());
89+
Mica.DatasetVariableAggregationDto summary = futureResult.get();
90+
builder.add(dtos.asDto(opalTable, summary, withStudySummary).build());
9591
} catch (Exception e) {
9692
if (log.isDebugEnabled())
9793
log.warn("Unable to retrieve statistics: {}", e.getMessage(), e);
@@ -108,9 +104,9 @@ public Mica.DatasetVariableAggregationsDto getVariableAggregations(@QueryParam("
108104
if (allAggDto.hasStatistics()) aggDto.setStatistics(allAggDto.getStatistics());
109105
aggDto.addAllFrequencies(allAggDto.getFrequenciesList());
110106

111-
aggDto.addAllAggregations(aggsDto);
107+
aggDto.addAllAggregations(aggsDto.stream().map((summary) -> datasetService.getFilteredVariableSummary(summary)).toList());
112108

113-
return aggDto.build();
109+
return datasetService.getFilteredVariableSummary(aggDto.build());
114110
}
115111

116112
@GET
@@ -259,24 +255,25 @@ protected Future<Mica.DatasetVariableAggregationDto> getVariableFacet(Harmonizat
259255
BaseStudyTable table) {
260256
try {
261257
String studyId = table.getStudyId();
262-
return new AsyncResult<>(datasetService.getVariableSummary(dataset, variableName, studyId, table.getSource()));
258+
Mica.DatasetVariableAggregationDto summary = datasetService.getVariableSummary(dataset, variableName, studyId, table.getSource());
259+
return CompletableFuture.supplyAsync(() -> summary);
263260
} catch (Exception e) {
264261
if (log.isDebugEnabled())
265262
log.warn("Unable to retrieve statistics: {}", e.getMessage(), e);
266263
else
267264
log.warn("Unable to retrieve statistics: {}", e.getMessage());
268-
return new AsyncResult<>(null);
265+
return CompletableFuture.supplyAsync(() -> null);
269266
}
270267
}
271268

272269
@Async
273270
protected Future<Mica.DatasetVariableContingencyDto> getContingencyTable(HarmonizationDataset dataset, DatasetVariable var,
274271
DatasetVariable crossVar, BaseStudyTable studyTable) {
275272
try {
276-
return new AsyncResult<>(datasetService.getContingencyTable(dataset, studyTable, var, crossVar));
273+
return CompletableFuture.supplyAsync(() -> datasetService.getContingencyTable(dataset, studyTable, var, crossVar));
277274
} catch (Exception e) {
278275
log.warn("Unable to retrieve contingency statistics: " + e.getMessage(), e);
279-
return new AsyncResult<>(null);
276+
return CompletableFuture.supplyAsync(() -> null);
280277
}
281278
}
282279
}

mica-search/src/main/java/org/obiba/mica/dataset/search/rest/harmonization/PublishedHarmonizedDatasetVariableResource.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ public Mica.DatasetVariableAggregationDto getVariableAggregations(@QueryParam("s
8080
for (BaseStudyTable baseTable : getBaseStudyTables(dataset)) {
8181
if (baseTable.isFor(studyId, source)) {
8282
try {
83-
return dtos.asDto(baseTable,
84-
datasetService.getVariableSummary(dataset, variableName, studyId, source), withStudySummary).build();
83+
Mica.DatasetVariableAggregationDto summary = datasetService.getVariableSummary(dataset, variableName, studyId, source);
84+
return dtos.asDto(baseTable, datasetService.getFilteredVariableSummary(summary), withStudySummary).build();
8585
} catch (Exception e) {
8686
if (log.isDebugEnabled())
8787
log.warn("Unable to retrieve statistics: {}", e.getMessage(), e);

mica-web-model/src/main/protobuf/Mica.proto

+1
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ message MicaConfigDto {
452452
required bool isStudiesExportEnabled = 68;
453453
required bool isNetworksExportEnabled = 69;
454454
repeated string usableVariableTaxonomiesForConceptTagging = 70;
455+
required string summaryStatisticsAccessPolicy = 71;
455456
}
456457

457458
message PublicMicaConfigDto {

mica-webapp/src/main/java/org/obiba/mica/web/controller/VariableController.java

+20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import com.google.common.collect.Lists;
5+
import org.apache.shiro.SecurityUtils;
56
import org.obiba.magma.NoSuchVariableException;
67
import org.obiba.mica.core.domain.HarmonizationStudyTable;
78
import org.obiba.mica.core.domain.StudyTable;
@@ -10,6 +11,8 @@
1011
import org.obiba.mica.dataset.domain.HarmonizationDataset;
1112
import org.obiba.mica.dataset.service.CollectedDatasetService;
1213
import org.obiba.mica.dataset.service.HarmonizedDatasetService;
14+
import org.obiba.mica.micaConfig.domain.MicaConfig;
15+
import org.obiba.mica.micaConfig.domain.SummaryStatisticsAccessPolicy;
1316
import org.obiba.mica.micaConfig.service.TaxonomiesService;
1417
import org.obiba.mica.spi.search.Indexer;
1518
import org.obiba.mica.spi.search.Searcher;
@@ -141,6 +144,8 @@ public ModelAndView variable(@PathVariable String id) {
141144
params.put("query", "variable(" + query.toString() + ")");
142145

143146
params.put("showDatasetContingencyLink", showDatasetContingencyLink());
147+
params.put("showVariableSummary", showVariableSummary());
148+
params.put("showSigninForVariableSummary", showSigninForVariableSummary());
144149

145150
return new ModelAndView("variable", params);
146151
}
@@ -250,4 +255,19 @@ private HarmonizationDataset getHarmonizationDataset(String id) {
250255
return harmonizedDatasetService.findById(id);
251256
}
252257

258+
private boolean showVariableSummary() {
259+
MicaConfig config = micaConfigService.getConfig();
260+
if (config.getSummaryStatisticsAccessPolicy().name().startsWith("OPEN_"))
261+
return true;
262+
return SecurityUtils.getSubject().isAuthenticated();
263+
}
264+
265+
private boolean showSigninForVariableSummary() {
266+
MicaConfig config = micaConfigService.getConfig();
267+
if (config.getSummaryStatisticsAccessPolicy().equals(SummaryStatisticsAccessPolicy.OPEN_ALL))
268+
return false;
269+
if (config.getSummaryStatisticsAccessPolicy().equals(SummaryStatisticsAccessPolicy.OPEN_SUMMARY))
270+
return false;
271+
return !SecurityUtils.getSubject().isAuthenticated();
272+
}
253273
}

0 commit comments

Comments
 (0)