diff --git a/pom.xml b/pom.xml
index f44b0e08a0..3263c4ffe8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
1.12.0
2.1.3
0.4.0
- 3.2.0
+ 3.7.0-SNAPSHOT
8.5.87
1.5
@@ -1122,7 +1122,7 @@
org.ohdsi
SkeletonCohortCharacterization
- 1.2.1
+ 2.0.0-SNAPSHOT
org.ohdsi
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java
index d32f478a8d..3218cab36d 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java
@@ -18,6 +18,7 @@
import org.ohdsi.webapi.cohortcharacterization.dto.CcPrevalenceStat;
import org.ohdsi.webapi.cohortcharacterization.dto.CcResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcShortDTO;
+import org.ohdsi.webapi.cohortcharacterization.dto.CcTemporalResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcVersionFullDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.CohortCharacterizationDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.ExportExecutionResultRequest;
@@ -257,7 +258,7 @@ public CohortCharacterizationDTO update(@PathParam("id") final Long id, final Co
final CohortCharacterizationEntity entity = conversionService.convert(dto, CohortCharacterizationEntity.class);
entity.setId(id);
final CohortCharacterizationEntity updatedEntity = service.updateCc(entity);
- return conversionService.convert(updatedEntity, CohortCharacterizationDTO.class);
+ return convertCcToDto(updatedEntity);
}
/**
@@ -445,6 +446,14 @@ public List getGenerationsResults(
return service.findResultAsList(generationId, thresholdLevel);
}
+ @GET
+ @Path("/generation/{generationId}/temporalresult")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public List getGenerationTemporalResults(@PathParam("generationId") final Long generationId) {
+ return service.findTemporalResultAsList(generationId);
+ }
+
@POST
@Path("/generation/{generationId}/result")
@Produces(MediaType.APPLICATION_JSON)
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcService.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcService.java
index 3f2dd701bd..fb851a85a1 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcService.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcService.java
@@ -6,6 +6,7 @@
import org.ohdsi.webapi.cohortcharacterization.dto.CcPrevalenceStat;
import org.ohdsi.webapi.cohortcharacterization.dto.CcResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcShortDTO;
+import org.ohdsi.webapi.cohortcharacterization.dto.CcTemporalResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcVersionFullDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.CohortCharacterizationDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.ExecutionResultRequest;
@@ -15,6 +16,8 @@
import org.ohdsi.webapi.cohortdefinition.event.CohortDefinitionChangedEvent;
import org.ohdsi.webapi.feanalysis.event.FeAnalysisChangedEvent;
import org.ohdsi.webapi.job.JobExecutionResource;
+import org.ohdsi.webapi.shiro.annotations.CcGenerationId;
+import org.ohdsi.webapi.shiro.annotations.DataSourceAccess;
import org.ohdsi.webapi.tag.domain.HasTags;
import org.ohdsi.webapi.tag.dto.TagNameListRequestDTO;
import org.ohdsi.webapi.versioning.domain.CharacterizationVersion;
@@ -64,6 +67,9 @@ public interface CcService extends HasTags {
List findGenerationsByCcIdAndSource(Long id, String sourceKey);
+ @DataSourceAccess
+ List findTemporalResultAsList(@CcGenerationId Long generationId);
+
GenerationResults findResult(Long generationId, ExecutionResultRequest params);
List findResultAsList(Long generationId, float thresholdLevel);
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcServiceImpl.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcServiceImpl.java
index a10b3a8ff9..c6b7aa7e4c 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcServiceImpl.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/CcServiceImpl.java
@@ -17,16 +17,20 @@
import org.ohdsi.webapi.Constants;
import org.ohdsi.webapi.JobInvalidator;
import org.ohdsi.webapi.cohortcharacterization.converter.SerializedCcToCcConverter;
+import org.ohdsi.webapi.cohortcharacterization.domain.CcFeAnalysisEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcGenerationEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcParamEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcStrataConceptSetEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcStrataEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CohortCharacterizationEntity;
+import org.ohdsi.webapi.cohortcharacterization.dto.AbstractTemporalResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcDistributionStat;
import org.ohdsi.webapi.cohortcharacterization.dto.CcExportDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.CcPrevalenceStat;
import org.ohdsi.webapi.cohortcharacterization.dto.CcResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcShortDTO;
+import org.ohdsi.webapi.cohortcharacterization.dto.CcTemporalAnnualResult;
+import org.ohdsi.webapi.cohortcharacterization.dto.CcTemporalResult;
import org.ohdsi.webapi.cohortcharacterization.dto.CcVersionFullDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.CohortCharacterizationDTO;
import org.ohdsi.webapi.cohortcharacterization.dto.ExecutionResultRequest;
@@ -34,9 +38,15 @@
import org.ohdsi.webapi.cohortcharacterization.dto.GenerationResults;
import org.ohdsi.webapi.cohortcharacterization.report.AnalysisItem;
import org.ohdsi.webapi.cohortcharacterization.report.AnalysisResultItem;
+import org.ohdsi.webapi.cohortcharacterization.report.ComparativeItem;
+import org.ohdsi.webapi.cohortcharacterization.report.ExportItem;
+import org.ohdsi.webapi.cohortcharacterization.report.PrevalenceItem;
import org.ohdsi.webapi.cohortcharacterization.report.Report;
+import org.ohdsi.webapi.cohortcharacterization.report.TemporalAnnualItem;
+import org.ohdsi.webapi.cohortcharacterization.report.TemporalItem;
import org.ohdsi.webapi.cohortcharacterization.repository.AnalysisGenerationInfoEntityRepository;
import org.ohdsi.webapi.cohortcharacterization.repository.CcConceptSetRepository;
+import org.ohdsi.webapi.cohortcharacterization.repository.CcFeAnalysisRepository;
import org.ohdsi.webapi.cohortcharacterization.repository.CcGenerationEntityRepository;
import org.ohdsi.webapi.cohortcharacterization.repository.CcParamRepository;
import org.ohdsi.webapi.cohortcharacterization.repository.CcRepository;
@@ -99,6 +109,7 @@
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
+import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -122,10 +133,14 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
+import static java.util.stream.Collectors.groupingBy;
import static org.ohdsi.analysis.cohortcharacterization.design.CcResultType.DISTRIBUTION;
import static org.ohdsi.analysis.cohortcharacterization.design.CcResultType.PREVALENCE;
import static org.ohdsi.webapi.Constants.GENERATE_COHORT_CHARACTERIZATION;
@@ -149,6 +164,8 @@ public class CcServiceImpl extends AbstractDaoService implements CcService, Gene
private static final String[] PARAMETERS_COUNT = {"cohort_characterization_generation_id", "vocabulary_schema"};
private static final String[] PREVALENCE_STATS_PARAMS = {"cdm_database_schema", "cdm_results_schema", "cc_generation_id", "analysis_id", "cohort_id", "covariate_id"};
private final String QUERY_RESULTS = ResourceHelper.GetResourceAsString("/resources/cohortcharacterizations/sql/queryResults.sql");
+ private final String QUERY_TEMPORAL_RESULTS = ResourceHelper.GetResourceAsString("/resources/cohortcharacterizations/sql/queryTemporalResults.sql");
+ private final String QUERY_TEMPORAL_ANNUAL_RESULTS = ResourceHelper.GetResourceAsString("/resources/cohortcharacterizations/sql/queryTemporalAnnualResults.sql");
private final String QUERY_COUNT = ResourceHelper.GetResourceAsString("/resources/cohortcharacterizations/sql/queryCountWithoutThreshold.sql");
private final String DELETE_RESULTS = ResourceHelper.GetResourceAsString("/resources/cohortcharacterizations/sql/deleteResults.sql");
private final String DELETE_EXECUTION = ResourceHelper.GetResourceAsString("/resources/cohortcharacterizations/sql/deleteExecution.sql");
@@ -209,6 +226,7 @@ public class CcServiceImpl extends AbstractDaoService implements CcService, Gene
private final JobInvalidator jobInvalidator;
private final GenericConversionService genericConversionService;
private final VocabularyService vocabularyService;
+ private final CcFeAnalysisRepository ccFeAnalysisRepository;
private VersionService versionService;
private PermissionService permissionService;
@@ -238,7 +256,7 @@ public CcServiceImpl(
final ApplicationEventPublisher eventPublisher,
final JobInvalidator jobInvalidator,
final VocabularyService vocabularyService,
- final VersionService versionService,
+ CcFeAnalysisRepository ccFeAnalysisRepository, final VersionService versionService,
final PermissionService permissionService,
@Qualifier("conversionService") final GenericConversionService genericConversionService,
Environment env) {
@@ -260,6 +278,7 @@ public CcServiceImpl(
this.eventPublisher = eventPublisher;
this.jobInvalidator = jobInvalidator;
this.vocabularyService = vocabularyService;
+ this.ccFeAnalysisRepository = ccFeAnalysisRepository;
this.permissionService = permissionService;
this.genericConversionService = genericConversionService;
this.versionService = versionService;
@@ -275,7 +294,7 @@ public CohortCharacterizationEntity createCc(final CohortCharacterizationEntity
}
private CohortCharacterizationEntity saveCc(final CohortCharacterizationEntity entity) {
- CohortCharacterizationEntity savedEntity = repository.saveAndFlush(entity);
+ CohortCharacterizationEntity savedEntity = repository.save(entity);
for(CcStrataEntity strata: entity.getStratas()){
strata.setCohortCharacterization(savedEntity);
@@ -287,6 +306,11 @@ private CohortCharacterizationEntity saveCc(final CohortCharacterizationEntity e
paramRepository.save(param);
}
+ for(CcFeAnalysisEntity analysis : entity.getCcFeatureAnalyses()) {
+ analysis.setCohortCharacterization(savedEntity);
+ ccFeAnalysisRepository.save(analysis);
+ }
+
entityManager.flush();
entityManager.refresh(savedEntity);
@@ -422,8 +446,10 @@ private void updateCohorts(final CohortCharacterizationEntity entity, final Coho
}
private void updateAnalyses(final CohortCharacterizationEntity entity, final CohortCharacterizationEntity foundEntity) {
- foundEntity.getFeatureAnalyses().clear();
- foundEntity.getFeatureAnalyses().addAll(entity.getFeatureAnalyses());
+ ccFeAnalysisRepository.delete(foundEntity.getCcFeatureAnalyses());
+ foundEntity.getCcFeatureAnalyses().clear();
+ foundEntity.getCcFeatureAnalyses().addAll(entity.getCcFeatureAnalyses());
+ ccFeAnalysisRepository.save(foundEntity.getCcFeatureAnalyses());
}
private List getLinksToDelete(final CohortCharacterizationEntity foundEntity,
@@ -478,9 +504,9 @@ public CohortCharacterizationEntity importCc(final CohortCharacterizationEntity
updateConceptSet(entity, persistedCohortCharacterization);
importCohorts(entity, persistedCohortCharacterization);
- List savedAnalysesIds = importAnalyses(entity, persistedCohortCharacterization);
final CohortCharacterizationEntity savedEntity = saveCc(persistedCohortCharacterization);
+ List savedAnalysesIds = importAnalyses(entity, savedEntity);
eventPublisher.publishEvent(new CcImportEvent(savedAnalysesIds));
@@ -644,17 +670,29 @@ public List findAllIncompleteGenerations() {
}
protected List findResults(final Long generationId, ExecutionResultRequest params) {
+ return executeFindResults(generationId, params, QUERY_RESULTS, getGenerationResults());
+ }
+
+ private List findTemporalResults(final Long generationId, ExecutionResultRequest params) {
+ return executeFindResults(generationId, params, QUERY_TEMPORAL_RESULTS, getGenerationTemporalResult());
+ }
+
+ private List findTemporalAnnualResults(final Long generationId, ExecutionResultRequest params) {
+ return executeFindResults(generationId, params, QUERY_TEMPORAL_ANNUAL_RESULTS, getGenerationTemporalAnnualResult());
+ }
+
+ private List executeFindResults(final Long generationId, ExecutionResultRequest params, String query, RowMapper rowMapper) {
final CcGenerationEntity generationEntity = ccGenerationRepository.findById(generationId)
.orElseThrow(() -> new IllegalArgumentException(String.format(GENERATION_NOT_FOUND_ERROR, generationId)));
final Source source = generationEntity.getSource();
String analysis = params.getAnalysisIds().stream().map(String::valueOf).collect(Collectors.joining(","));
String cohorts = params.getCohortIds().stream().map(String::valueOf).collect(Collectors.joining(","));
- String generationResults = sourceAwareSqlRender.renderSql(source.getSourceId(), QUERY_RESULTS, PARAMETERS_RESULTS_FILTERED,
+ String generationResults = sourceAwareSqlRender.renderSql(source.getSourceId(), query, PARAMETERS_RESULTS_FILTERED,
new String[]{String.valueOf(generationId), String.valueOf(params.getThresholdValuePct()),
analysis, cohorts, SourceUtils.getVocabularyQualifier(source)});
final String tempSchema = SourceUtils.getTempQualifier(source);
String translatedSql = SqlTranslate.translateSql(generationResults, source.getSourceDialect(), SessionUtils.sessionId(), tempSchema);
- return getGenerationResults(source, translatedSql);
+ return this.getSourceJdbcTemplate(source).query(translatedSql, rowMapper);
}
@Override
@@ -720,7 +758,34 @@ public List findResultAsList(@CcGenerationId final Long generationId,
.map(fa -> fa.getDomain().toString()).distinct().collect(Collectors.toList()));
return findResults(generationId, params);
}
-
+
+ @Override
+ @DataSourceAccess
+ public List findTemporalResultAsList(@CcGenerationId final Long generationId) {
+ ExecutionResultRequest params = getExecutionResultRequest(generationId);
+ return findTemporalResults(generationId, params);
+ }
+
+ @DataSourceAccess
+ public List findTemporalAnnualResultAsList(@CcGenerationId final Long generationId) {
+ ExecutionResultRequest params = getExecutionResultRequest(generationId);
+ return findTemporalAnnualResults(generationId, params);
+ }
+
+ private ExecutionResultRequest getExecutionResultRequest(Long generationId) {
+ ExecutionResultRequest params = new ExecutionResultRequest();
+ CcGenerationEntity generationEntity = ccGenerationRepository.findById(generationId)
+ .orElseThrow(() -> new IllegalArgumentException(String.format(GENERATION_NOT_FOUND_ERROR, generationId)));
+ CohortCharacterizationEntity characterization = generationEntity.getCohortCharacterization();
+ params.setCohortIds(characterization.getCohortDefinitions().stream()
+ .map(CohortDefinition::getId).collect(Collectors.toList()));
+ params.setAnalysisIds(characterization.getFeatureAnalyses().stream()
+ .map(this::mapFeatureAnalysisId).collect(Collectors.toList()));
+ params.setDomainIds(generationEntity.getCohortCharacterization().getFeatureAnalyses().stream()
+ .map(fa -> fa.getDomain().toString()).distinct().collect(Collectors.toList()));
+ return params;
+ }
+
@Override
@DataSourceAccess
public GenerationResults findResult(@CcGenerationId final Long generationId, ExecutionResultRequest params) {
@@ -758,6 +823,7 @@ public GenerationResults findResult(@CcGenerationId final Long generationId, Exe
.noneMatch(fe -> mapFeatureAnalysisId(fe).equals(s) && params.getDomainIds().contains(fe.getDomain().toString())));
List ccResults = findResults(generationId, params);
+ List ccTemporalResults = findTemporalResults(generationId, params);
// create initial structure and fill with results
Map analysisMap = new HashMap<>();
@@ -789,7 +855,57 @@ public GenerationResults findResult(@CcGenerationId final Long generationId, Exe
.filter(def -> params.getCohortIds().contains(def.getId()))
.collect(Collectors.toSet());
- List reports = prepareReportData(analysisMap, cohortDefs, featureAnalyses);
+ //Temporal
+ Map prespecAnalysisIdMap = FeatureExtraction.getNameToPrespecAnalysis().values()
+ .stream().collect(Collectors.toMap(a -> a.analysisId, a -> a));
+ List temporalResult = findTemporalResultAsList(generationId);
+ List mappedResult = temporalResult.stream().map(tr -> mapTemporalResult(featureAnalyses, prespecAnalysisIdMap, tr, CcTemporalResult::new,
+ (source, target) -> {
+ target.setStartDay(source.getStartDay());
+ target.setEndDay(source.getEndDay());
+ target.setTimeId(source.getTimeId());
+ }))
+ .collect(Collectors.toList());
+ Map>>> temporalByCohort = groupByResult(mappedResult);
+
+ List temporalAnnualResult = findTemporalAnnualResultAsList(generationId);
+ List mappedAnnualResult = temporalAnnualResult.stream().map(tr -> mapTemporalResult(featureAnalyses, prespecAnalysisIdMap, tr, CcTemporalAnnualResult::new,
+ (source, target) -> {
+ target.setYear(source.getYear());
+ }))
+ .collect(Collectors.toList());
+ Map>>> annualByCohort = groupByResult(mappedAnnualResult);
+
+ List reports = prepareReportData(analysisMap, cohortDefs, featureAnalyses, params);
+ reports.forEach(r -> {
+ r.items.stream()
+ .filter(i -> Objects.equals(i.getFaType(), StandardFeatureAnalysisType.PRESET.toString()))
+ .filter(o -> !r.isComparative)
+ .map(this::toPrevalenceItem)
+ .forEach(item -> {
+ setTemporal(temporalByCohort, item, cov -> {
+ List temporalItems = cov.stream().map(temp -> {
+ TemporalItem ti = new TemporalItem();
+ ti.setAvg(temp.getAvg());
+ ti.setCount(temp.getCount());
+ ti.setStartDay(temp.getStartDay());
+ ti.setEndDay(temp.getEndDay());
+ return ti;
+ }).collect(Collectors.toList());
+ item.setTemporal(temporalItems);
+ });
+ setTemporal(annualByCohort, item, cov -> {
+ List temporalAnnualItems = cov.stream().map(temp -> {
+ TemporalAnnualItem tai = new TemporalAnnualItem();
+ tai.setYear(temp.getYear());
+ tai.setAvg(temp.getAvg());
+ tai.setCount(temp.getCount());
+ return tai;
+ }).collect(Collectors.toList());
+ item.setTemporalAnnual(temporalAnnualItems);
+ });
+ });
+ });
GenerationResults res = new GenerationResults();
res.setReports(reports);
@@ -797,6 +913,49 @@ public GenerationResults findResult(@CcGenerationId final Long generationId, Exe
return res;
}
+ private PrevalenceItem> toPrevalenceItem(ExportItem> exportItem){
+ return PrevalenceItem.class.cast(exportItem);
+ }
+
+ private static void setTemporal(Map>>> temporalByCohort, PrevalenceItem item, Consumer> setter) {
+ Optional.ofNullable(temporalByCohort.get(item.getCohortId()))
+ .flatMap(cr -> Optional.ofNullable(cr.get(item.getAnalysisId()))
+ .flatMap(ar -> Optional.ofNullable(ar.get(item.getCovariateId()))))
+ .ifPresent(setter);
+ }
+
+ private static Map>>> groupByResult(List mappedAnnualResult) {
+ return mappedAnnualResult.stream()
+ .collect(groupingBy(T::getCohortId, groupingBy(T::getAnalysisId, groupingBy(T::getCovariateId))));
+ }
+
+ private static T mapTemporalResult(
+ Set featureAnalyses,
+ Map prespecAnalysisIdMap,
+ T source,
+ Supplier constructor,
+ BiConsumer initializer
+ ) {
+ T result = constructor.get();
+ String analysisName = prespecAnalysisIdMap.get(source.getAnalysisId()).analysisName;
+ Integer analysisId = featureAnalyses.stream().filter(fa -> Objects.equals(fa.getRawDesign(), analysisName))
+ .findFirst()
+ .map(FeAnalysisEntity::getId)
+ .orElseThrow(() -> new IllegalArgumentException(String.format("Preset analysis [%s} is not mapped to feature", analysisName)));
+ result.setAnalysisId(analysisId);
+ result.setCovariateId(source.getCovariateId());
+ result.setAvg(source.getAvg());
+ result.setCount(source.getCount());
+ result.setAnalysisName(source.getAnalysisName());
+ result.setCovariateName(source.getCovariateName());
+ result.setStrataId(source.getStrataId());
+ result.setCohortId(source.getCohortId());
+ result.setStrataName(source.getStrataName());
+ result.setConceptId(source.getConceptId());
+ initializer.accept(source, result);
+ return result;
+ }
+
private Integer mapFeatureAnalysisId(FeAnalysisEntity feAnalysis) {
if (feAnalysis.isPreset()) {
@@ -818,7 +977,7 @@ private String mapFeatureName(FeAnalysisEntity entity) {
}
private List prepareReportData(Map analysisMap, Set cohortDefs,
- Set featureAnalyses) {
+ Set featureAnalyses, ExecutionResultRequest params) {
// Create map to get cohort name by its id
final Map definitionMap = cohortDefs.stream()
.collect(Collectors.toMap(CohortDefinition::getId, Function.identity()));
@@ -854,7 +1013,7 @@ private List prepareReportData(Map analysisMap, S
reports.add(simpleReport);
// comparative mode
- if (definitionMap.size() == 2) {
+ if (definitionMap.size() == 2 && !params.getExcludeComparativeResults()) {
Iterator iter = definitionMap.values().iterator();
CohortDefinition firstCohortDef = iter.next();
CohortDefinition secondCohortDef = iter.next();
@@ -1074,8 +1233,8 @@ public List listByTags(TagNameListRequestDTO requestDTO) {
return listByTags(entities, names, CcShortDTO.class);
}
- private List getGenerationResults(final Source source, final String translatedSql) {
- return this.getSourceJdbcTemplate(source).query(translatedSql, (rs, rowNum) -> {
+ private RowMapper getGenerationResults() {
+ return (rs, rowNum) -> {
final String type = rs.getString("type");
if (StringUtils.equals(type, DISTRIBUTION.toString())) {
final CcDistributionStat distributionStat = new CcDistributionStat();
@@ -1088,7 +1247,45 @@ private List getGenerationResults(final Source source, final String tr
return prevalenceStat;
}
return null;
- });
+ };
+ }
+
+ private RowMapper getGenerationTemporalResult() {
+ return (rs, rowNum) -> {
+ CcTemporalResult result = new CcTemporalResult();
+ result.setAnalysisName(rs.getString("analysis_name"));
+ result.setAvg(rs.getDouble("avg_value"));
+ result.setAnalysisId(rs.getInt("analysis_id"));
+ result.setCohortId(rs.getInt("cohort_definition_id"));
+ result.setConceptId(rs.getInt("concept_id"));
+ result.setCount(rs.getLong("count_value"));
+ result.setCovariateId(rs.getLong("covariate_id"));
+ result.setCovariateName(rs.getString("covariate_name"));
+ result.setStrataId(rs.getInt("strata_id"));
+ result.setEndDay(rs.getInt("end_day"));
+ result.setStartDay(rs.getInt("start_day"));
+ result.setStrataName(rs.getString("strata_name"));
+ result.setTimeId(rs.getInt("time_id"));
+ return result;
+ };
+ }
+
+ private RowMapper getGenerationTemporalAnnualResult() {
+ return (rs, rowNum) -> {
+ CcTemporalAnnualResult result = new CcTemporalAnnualResult();
+ result.setAnalysisName(rs.getString("analysis_name"));
+ result.setAvg(rs.getDouble("avg_value"));
+ result.setAnalysisId(rs.getInt("analysis_id"));
+ result.setCohortId(rs.getInt("cohort_definition_id"));
+ result.setConceptId(rs.getInt("concept_id"));
+ result.setCount(rs.getLong("count_value"));
+ result.setCovariateId(rs.getLong("covariate_id"));
+ result.setCovariateName(rs.getString("covariate_name"));
+ result.setStrataId(rs.getInt("strata_id"));
+ result.setStrataName(rs.getString("strata_name"));
+ result.setYear(rs.getInt("event_year"));
+ return result;
+ };
}
private void gatherForPrevalence(final CcPrevalenceStat stat, final ResultSet rs) throws SQLException {
@@ -1165,7 +1362,16 @@ private List importAnalyses(final CohortCharacterizationEntity entity,
}
}
- persistedEntity.setFeatureAnalyses(analysesSet);
+ persistedEntity.getCcFeatureAnalyses().clear();
+ Set featureAnalyses = analysesSet.stream().map(a -> {
+ CcFeAnalysisEntity feAnalysisEntity = new CcFeAnalysisEntity();
+ feAnalysisEntity.setFeatureAnalysis(a);
+ feAnalysisEntity.setCohortCharacterization(persistedEntity);
+ return feAnalysisEntity;
+ }).collect(Collectors.toSet());
+ ccFeAnalysisRepository.save(featureAnalyses);
+
+ persistedEntity.getCcFeatureAnalyses().addAll(featureAnalyses);
return savedAnalysesIds;
}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/GenerateCohortCharacterizationTasklet.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/GenerateCohortCharacterizationTasklet.java
index c7039e58f8..0a91861219 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/GenerateCohortCharacterizationTasklet.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/GenerateCohortCharacterizationTasklet.java
@@ -22,6 +22,7 @@
import org.ohdsi.sql.SqlSplit;
import org.ohdsi.sql.SqlTranslate;
import org.ohdsi.webapi.cohortcharacterization.converter.SerializedCcToCcConverter;
+import org.ohdsi.webapi.cohortcharacterization.domain.CcFeAnalysisEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CohortCharacterizationEntity;
import org.ohdsi.webapi.cohortcharacterization.repository.AnalysisGenerationInfoEntityRepository;
import org.ohdsi.webapi.common.generation.AnalysisTasklet;
@@ -37,6 +38,7 @@
import java.sql.SQLException;
import java.util.Map;
+import java.util.Optional;
import static org.ohdsi.webapi.Constants.Params.*;
@@ -75,9 +77,13 @@ protected String[] prepareQueries(ChunkContext chunkContext, CancelableJdbcTempl
final String cohortTable = jobParams.get(TARGET_TABLE).toString();
final String sessionId = jobParams.get(SESSION_ID).toString();
final String tempSchema = SourceUtils.getTempQualifier(source);
+ boolean includeAnnual = cohortCharacterization.getCcFeatureAnalyses().stream()
+ .anyMatch(fe -> Optional.ofNullable(fe.getIncludeAnnual()).orElse(false));
+ boolean includeTemporal = cohortCharacterization.getCcFeatureAnalyses().stream()
+ .anyMatch(fe -> Optional.ofNullable(fe.getIncludeTemporal()).orElse(false));
CCQueryBuilder ccQueryBuilder = new CCQueryBuilder(cohortCharacterization, cohortTable, sessionId,
SourceUtils.getCdmQualifier(source), SourceUtils.getResultsQualifier(source),
- SourceUtils.getVocabularyQualifier(source), tempSchema, jobId);
+ SourceUtils.getVocabularyQualifier(source), tempSchema, jobId, includeAnnual, includeTemporal);
String sql = ccQueryBuilder.build();
/*
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/BaseCcDTOToCcEntityConverter.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/BaseCcDTOToCcEntityConverter.java
index 509a0c2c53..294478593d 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/BaseCcDTOToCcEntityConverter.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/BaseCcDTOToCcEntityConverter.java
@@ -5,6 +5,7 @@
import org.ohdsi.analysis.CohortMetadata;
import org.ohdsi.analysis.Utils;
import org.ohdsi.analysis.cohortcharacterization.design.CcResultType;
+import org.ohdsi.webapi.cohortcharacterization.domain.CcFeAnalysisEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcStrataConceptSetEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcParamEntity;
import org.ohdsi.webapi.cohortcharacterization.domain.CcStrataEntity;
@@ -51,7 +52,16 @@ public CohortCharacterizationEntity convert(T source) {
fa.setStatType(CcResultType.PREVALENCE);
}
});
- cohortCharacterization.setFeatureAnalyses(converterUtils.convertSet(source.getFeatureAnalyses(), FeAnalysisEntity.class));
+ cohortCharacterization.setFeatureAnalyses(
+ source.getFeatureAnalyses().stream().map(fa -> {
+ CcFeAnalysisEntity feAnalysisEntity = new CcFeAnalysisEntity();
+ feAnalysisEntity.setFeatureAnalysis(conversionService.convert(fa, FeAnalysisEntity.class));
+ feAnalysisEntity.setCohortCharacterization(cohortCharacterization);
+ feAnalysisEntity.setIncludeAnnual(fa.getIncludeAnnual());
+ feAnalysisEntity.setIncludeTemporal(fa.getIncludeTemporal());
+ return feAnalysisEntity;
+ }).collect(Collectors.toSet())
+ );
cohortCharacterization.setParameters(converterUtils.convertSet(source.getParameters(), CcParamEntity.class));
cohortCharacterization.setStratas(converterUtils.convertSet(source.getStratas(), CcStrataEntity.class));
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/CcToCcDTOConverter.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/CcToCcDTOConverter.java
index c9aad19aaa..c8b8d34df0 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/CcToCcDTOConverter.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/converter/CcToCcDTOConverter.java
@@ -15,7 +15,7 @@ public CohortCharacterizationDTO convert(final CohortCharacterizationEntity sour
final CohortCharacterizationDTO cohortCharacterizationDTO = super.convert(source);
cohortCharacterizationDTO.setCohorts(converterUtils.convertSet(source.getCohortDefinitions(), CohortMetadataImplDTO.class));
- cohortCharacterizationDTO.setFeatureAnalyses(converterUtils.convertSet(source.getFeatureAnalyses(), FeAnalysisShortDTO.class));
+ cohortCharacterizationDTO.setFeatureAnalyses(converterUtils.convertSet(source.getCcFeatureAnalyses(), FeAnalysisShortDTO.class));
return cohortCharacterizationDTO;
}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CcFeAnalysisEntity.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CcFeAnalysisEntity.java
new file mode 100644
index 0000000000..1110d72ca0
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CcFeAnalysisEntity.java
@@ -0,0 +1,115 @@
+package org.ohdsi.webapi.cohortcharacterization.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+import org.hibernate.annotations.Parameter;
+import org.ohdsi.analysis.cohortcharacterization.design.FeatureAnalysis;
+import org.ohdsi.analysis.cohortcharacterization.design.FeatureAnalysisDomain;
+import org.ohdsi.analysis.cohortcharacterization.design.FeatureAnalysisType;
+import org.ohdsi.webapi.feanalysis.domain.FeAnalysisEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+@Entity
+@Table(name = "cc_analysis")
+public class CcFeAnalysisEntity implements FeatureAnalysis {
+
+ @Id
+ @GenericGenerator(
+ name = "cc_analysis_generator",
+ strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
+ parameters = {
+ @Parameter(name = "sequence_name", value = "cc_analysis_seq"),
+ @Parameter(name = "increment_size", value = "1")
+ }
+ )
+ @GeneratedValue(generator = "cc_analysis_generator")
+ private Long id;
+ @ManyToOne(optional = false)
+ @JoinColumn(name = "cohort_characterization_id")
+ private CohortCharacterizationEntity cohortCharacterization;
+ @ManyToOne(optional = false)
+ @JoinColumn(name = "fe_analysis_id")
+ private FeAnalysisEntity featureAnalysis;
+ @Column(name = "include_annual")
+ private Boolean includeAnnual;
+ @Column(name = "include_temporal")
+ private Boolean includeTemporal;
+
+ public CohortCharacterizationEntity getCohortCharacterization() {
+ return cohortCharacterization;
+ }
+
+ public void setCohortCharacterization(CohortCharacterizationEntity cohortCharacterization) {
+ this.cohortCharacterization = cohortCharacterization;
+ }
+
+ public FeAnalysisEntity getFeatureAnalysis() {
+ return featureAnalysis;
+ }
+
+ public void setFeatureAnalysis(FeAnalysisEntity featureAnalysis) {
+ this.featureAnalysis = featureAnalysis;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Boolean getIncludeAnnual() {
+ return includeAnnual;
+ }
+
+ public void setIncludeAnnual(Boolean includeAnnual) {
+ this.includeAnnual = includeAnnual;
+ }
+
+ public Boolean getIncludeTemporal() {
+ return includeTemporal;
+ }
+
+ public void setIncludeTemporal(Boolean includeTemporal) {
+ this.includeTemporal = includeTemporal;
+ }
+
+ private T mapFeatureAnalysis(Function getter) {
+ return Optional.ofNullable(featureAnalysis).map(getter).orElse(null);
+ }
+
+ @Override
+ public FeatureAnalysisType getType() {
+ return mapFeatureAnalysis(FeatureAnalysis::getType);
+ }
+
+ @Override
+ public String getName() {
+ return mapFeatureAnalysis(FeatureAnalysis::getName);
+ }
+
+ @Override
+ public FeatureAnalysisDomain getDomain() {
+ return mapFeatureAnalysis(FeatureAnalysis::getDomain);
+ }
+
+ @Override
+ public String getDescr() {
+ return mapFeatureAnalysis(FeatureAnalysis::getDescr);
+ }
+
+ @Override
+ public Object getDesign() {
+ return mapFeatureAnalysis(FeatureAnalysis::getDesign);
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CohortCharacterizationEntity.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CohortCharacterizationEntity.java
index c9aef23268..99a427d889 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CohortCharacterizationEntity.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/domain/CohortCharacterizationEntity.java
@@ -1,6 +1,7 @@
package org.ohdsi.webapi.cohortcharacterization.domain;
import java.util.*;
+import java.util.stream.Collectors;
import javax.persistence.CascadeType;
import javax.persistence.Column;
@@ -52,11 +53,9 @@ public class CohortCharacterizationEntity extends CommonEntityExt implemen
inverseJoinColumns = @JoinColumn(name = "cohort_id", referencedColumnName = "id"))
private Set cohortDefinitions = new HashSet<>();
- @ManyToMany(targetEntity = FeAnalysisEntity.class, fetch = FetchType.LAZY)
- @JoinTable(name = "cc_analysis",
- joinColumns = @JoinColumn(name = "cohort_characterization_id", referencedColumnName = "id"),
- inverseJoinColumns = @JoinColumn(name = "fe_analysis_id", referencedColumnName = "id"))
- private Set featureAnalyses = new HashSet<>();
+ @OneToMany(orphanRemoval = true)
+ @JoinColumn(name = "cohort_characterization_id", insertable = false, updatable = false, nullable = false)
+ private Set featureAnalyses = new HashSet<>();
@OneToMany(mappedBy = "cohortCharacterization", fetch = FetchType.LAZY, targetEntity = CcParamEntity.class)
private Set parameters = new HashSet<>();
@@ -89,6 +88,12 @@ public Set getCohorts() {
@Override
public Set getFeatureAnalyses() {
+ return featureAnalyses != null ?
+ featureAnalyses.stream().map(CcFeAnalysisEntity::getFeatureAnalysis).collect(Collectors.toSet()) :
+ Collections.emptySet();
+ }
+
+ public Set getCcFeatureAnalyses() {
return featureAnalyses;
}
@@ -97,6 +102,10 @@ public Set getParameters() {
return parameters;
}
+ public void setFeatureAnalyses(Set featureAnalyses) {
+ this.featureAnalyses = featureAnalyses;
+ }
+
@Override
public Long getId() {
return id;
@@ -126,10 +135,6 @@ public void setParameters(final Set parameters) {
this.parameters = parameters;
}
- public void setFeatureAnalyses(final Set featureAnalyses) {
- this.featureAnalyses = featureAnalyses;
- }
-
public Set getCohortDefinitions() {
return cohortDefinitions;
}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/AbstractTemporalResult.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/AbstractTemporalResult.java
new file mode 100644
index 0000000000..383ee8514a
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/AbstractTemporalResult.java
@@ -0,0 +1,94 @@
+package org.ohdsi.webapi.cohortcharacterization.dto;
+
+public abstract class AbstractTemporalResult {
+ protected Integer analysisId;
+ protected String analysisName;
+ protected Long covariateId;
+ protected String covariateName;
+ protected Integer strataId;
+ protected String strataName;
+ protected Integer conceptId;
+ protected Integer cohortId;
+ protected Long count;
+ protected Double avg;
+
+ public Integer getAnalysisId() {
+ return analysisId;
+ }
+
+ public void setAnalysisId(Integer analysisId) {
+ this.analysisId = analysisId;
+ }
+
+ public String getAnalysisName() {
+ return analysisName;
+ }
+
+ public void setAnalysisName(String analysisName) {
+ this.analysisName = analysisName;
+ }
+
+ public Long getCovariateId() {
+ return covariateId;
+ }
+
+ public void setCovariateId(Long covariateId) {
+ this.covariateId = covariateId;
+ }
+
+ public String getCovariateName() {
+ return covariateName;
+ }
+
+ public void setCovariateName(String covariateName) {
+ this.covariateName = covariateName;
+ }
+
+ public Integer getStrataId() {
+ return strataId;
+ }
+
+ public void setStrataId(Integer strataId) {
+ this.strataId = strataId;
+ }
+
+ public String getStrataName() {
+ return strataName;
+ }
+
+ public void setStrataName(String strataName) {
+ this.strataName = strataName;
+ }
+
+ public Integer getConceptId() {
+ return conceptId;
+ }
+
+ public void setConceptId(Integer conceptId) {
+ this.conceptId = conceptId;
+ }
+
+ public Long getCount() {
+ return count;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+
+ public Double getAvg() {
+ return avg;
+ }
+
+ public void setAvg(Double avg) {
+ this.avg = avg;
+ }
+
+ public Integer getCohortId() {
+ return cohortId;
+ }
+
+ public void setCohortId(Integer cohortId) {
+ this.cohortId = cohortId;
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/CcTemporalAnnualResult.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/CcTemporalAnnualResult.java
new file mode 100644
index 0000000000..c40c5078ef
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/CcTemporalAnnualResult.java
@@ -0,0 +1,13 @@
+package org.ohdsi.webapi.cohortcharacterization.dto;
+
+public class CcTemporalAnnualResult extends AbstractTemporalResult{
+ private Integer year;
+
+ public Integer getYear() {
+ return year;
+ }
+
+ public void setYear(Integer year) {
+ this.year = year;
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/CcTemporalResult.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/CcTemporalResult.java
new file mode 100644
index 0000000000..ae5bf55754
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/CcTemporalResult.java
@@ -0,0 +1,31 @@
+package org.ohdsi.webapi.cohortcharacterization.dto;
+
+public class CcTemporalResult extends AbstractTemporalResult {
+ private Integer timeId;
+ private Integer startDay;
+ private Integer endDay;
+
+ public Integer getTimeId() {
+ return timeId;
+ }
+
+ public void setTimeId(Integer timeId) {
+ this.timeId = timeId;
+ }
+
+ public Integer getStartDay() {
+ return startDay;
+ }
+
+ public void setStartDay(Integer startDay) {
+ this.startDay = startDay;
+ }
+
+ public Integer getEndDay() {
+ return endDay;
+ }
+
+ public void setEndDay(Integer endDay) {
+ this.endDay = endDay;
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/ExecutionResultRequest.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/ExecutionResultRequest.java
index 9fbea1dd89..22b4c16665 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/ExecutionResultRequest.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/dto/ExecutionResultRequest.java
@@ -27,6 +27,8 @@ public class ExecutionResultRequest {
@JsonProperty("showEmptyResults")
private Boolean isShowEmptyResults = false;
+ @JsonProperty("excludeComparativeResults")
+ private boolean excludeComparativeResults;
public List getCohortIds() {
if(cohortIds == null) {
return Collections.emptyList();
@@ -87,4 +89,12 @@ public Boolean getShowEmptyResults() {
public void setShowEmptyResults(Boolean showEmptyResults) {
isShowEmptyResults = showEmptyResults;
}
+
+ public boolean getExcludeComparativeResults() {
+ return excludeComparativeResults;
+ }
+
+ public void setExcludeComparativeResults(boolean excludeComparativeResults) {
+ this.excludeComparativeResults = excludeComparativeResults;
+ }
}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/PrevalenceItem.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/PrevalenceItem.java
index bbe50ff98f..ce08d90ad7 100644
--- a/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/PrevalenceItem.java
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/PrevalenceItem.java
@@ -12,6 +12,8 @@ public class PrevalenceItem extends ExportItem {
protected final Long count;
protected final Double pct;
protected final Double avg;
+ private List temporal;
+ private List temporalAnnual;
public PrevalenceItem(CcPrevalenceStat prevalenceStat, String cohortName) {
super(prevalenceStat);
@@ -98,6 +100,22 @@ public String getCohortName() {
return cohortName;
}
+ public List getTemporal() {
+ return temporal;
+ }
+
+ public void setTemporal(List temporal) {
+ this.temporal = temporal;
+ }
+
+ public List getTemporalAnnual() {
+ return temporalAnnual;
+ }
+
+ public void setTemporalAnnual(List temporalAnnual) {
+ this.temporalAnnual = temporalAnnual;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/TemporalAnnualItem.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/TemporalAnnualItem.java
new file mode 100644
index 0000000000..901c66a106
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/TemporalAnnualItem.java
@@ -0,0 +1,31 @@
+package org.ohdsi.webapi.cohortcharacterization.report;
+
+public class TemporalAnnualItem {
+ private Long count;
+ private Double avg;
+ private Integer year;
+
+ public Long getCount() {
+ return count;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+
+ public Double getAvg() {
+ return avg;
+ }
+
+ public void setAvg(Double avg) {
+ this.avg = avg;
+ }
+
+ public Integer getYear() {
+ return year;
+ }
+
+ public void setYear(Integer year) {
+ this.year = year;
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/TemporalItem.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/TemporalItem.java
new file mode 100644
index 0000000000..2286d2dc3c
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/report/TemporalItem.java
@@ -0,0 +1,40 @@
+package org.ohdsi.webapi.cohortcharacterization.report;
+
+public class TemporalItem {
+ private Long count;
+ private Double avg;
+ private Integer startDay;
+ private Integer endDay;
+
+ public Long getCount() {
+ return count;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+
+ public Double getAvg() {
+ return avg;
+ }
+
+ public void setAvg(Double avg) {
+ this.avg = avg;
+ }
+
+ public Integer getStartDay() {
+ return startDay;
+ }
+
+ public void setStartDay(Integer startDay) {
+ this.startDay = startDay;
+ }
+
+ public Integer getEndDay() {
+ return endDay;
+ }
+
+ public void setEndDay(Integer endDay) {
+ this.endDay = endDay;
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/cohortcharacterization/repository/CcFeAnalysisRepository.java b/src/main/java/org/ohdsi/webapi/cohortcharacterization/repository/CcFeAnalysisRepository.java
new file mode 100644
index 0000000000..b85fbe20a2
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/cohortcharacterization/repository/CcFeAnalysisRepository.java
@@ -0,0 +1,7 @@
+package org.ohdsi.webapi.cohortcharacterization.repository;
+
+import org.ohdsi.webapi.cohortcharacterization.domain.CcFeAnalysisEntity;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CcFeAnalysisRepository extends JpaRepository {
+}
diff --git a/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisDTOToFeAnalysisConverter.java b/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisDTOToFeAnalysisConverter.java
index 694636c5b0..6063655470 100644
--- a/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisDTOToFeAnalysisConverter.java
+++ b/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisDTOToFeAnalysisConverter.java
@@ -18,6 +18,8 @@ public T convert(D source) {
result.setName(StringUtils.trim(source.getName()));
result.setType(source.getType());
result.setStatType(source.getStatType());
+ result.setSupportsAnnual(source.getSupportsAnnual());
+ result.setSupportsTemporal(source.getSupportsTemporal());
return result;
}
diff --git a/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisEntityToFeAnalysisDTOConverter.java b/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisEntityToFeAnalysisDTOConverter.java
index daae37aa09..65e9144031 100644
--- a/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisEntityToFeAnalysisDTOConverter.java
+++ b/src/main/java/org/ohdsi/webapi/feanalysis/converter/BaseFeAnalysisEntityToFeAnalysisDTOConverter.java
@@ -15,5 +15,7 @@ public void doConvert(FeAnalysisEntity> source, T target) {
target.setDomain(source.getDomain());
target.setDescription(source.getDescr());
target.setStatType(source.getStatType());
+ target.setSupportsAnnual(source.getSupportsAnnual());
+ target.setSupportsTemporal(source.getSupportsTemporal());
}
}
diff --git a/src/main/java/org/ohdsi/webapi/feanalysis/converter/CcFeAnalysisEntityToFeAnalysisShortDTOConverter.java b/src/main/java/org/ohdsi/webapi/feanalysis/converter/CcFeAnalysisEntityToFeAnalysisShortDTOConverter.java
new file mode 100644
index 0000000000..c43a985aeb
--- /dev/null
+++ b/src/main/java/org/ohdsi/webapi/feanalysis/converter/CcFeAnalysisEntityToFeAnalysisShortDTOConverter.java
@@ -0,0 +1,23 @@
+package org.ohdsi.webapi.feanalysis.converter;
+
+import org.ohdsi.webapi.cohortcharacterization.domain.CcFeAnalysisEntity;
+import org.ohdsi.webapi.converter.BaseConversionServiceAwareConverter;
+import org.ohdsi.webapi.feanalysis.dto.FeAnalysisShortDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.convert.support.GenericConversionService;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CcFeAnalysisEntityToFeAnalysisShortDTOConverter extends BaseConversionServiceAwareConverter {
+
+ @Autowired
+ private GenericConversionService conversionService;
+
+ @Override
+ public FeAnalysisShortDTO convert(CcFeAnalysisEntity source) {
+ FeAnalysisShortDTO dto = conversionService.convert(source.getFeatureAnalysis(), FeAnalysisShortDTO.class);
+ dto.setIncludeAnnual(source.getIncludeAnnual());
+ dto.setIncludeTemporal(source.getIncludeTemporal());
+ return dto;
+ }
+}
diff --git a/src/main/java/org/ohdsi/webapi/feanalysis/converter/FeAnalysisEntityToFeAnalysisDTOConverter.java b/src/main/java/org/ohdsi/webapi/feanalysis/converter/FeAnalysisEntityToFeAnalysisDTOConverter.java
index f39f12cd2f..5343f8863d 100644
--- a/src/main/java/org/ohdsi/webapi/feanalysis/converter/FeAnalysisEntityToFeAnalysisDTOConverter.java
+++ b/src/main/java/org/ohdsi/webapi/feanalysis/converter/FeAnalysisEntityToFeAnalysisDTOConverter.java
@@ -2,14 +2,17 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.collections4.CollectionUtils;
import org.ohdsi.analysis.cohortcharacterization.design.FeatureAnalysisAggregate;
import org.ohdsi.webapi.feanalysis.domain.*;
import org.ohdsi.webapi.feanalysis.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Collectors;
import static org.ohdsi.analysis.cohortcharacterization.design.StandardFeatureAnalysisType.CRITERIA_SET;
@@ -24,6 +27,8 @@ public class FeAnalysisEntityToFeAnalysisDTOConverter extends BaseFeAnalysisEnti
public FeAnalysisDTO convert(final FeAnalysisEntity source) {
final FeAnalysisDTO dto = super.convert(source);
dto.setDesign(convertDesignToJson(source));
+ dto.setSupportsAnnual(source.getSupportsAnnual());
+ dto.setSupportsTemporal(source.getSupportsTemporal());
if (CRITERIA_SET.equals(source.getType())){
FeAnalysisWithConceptSetDTO dtoWithConceptSet = (FeAnalysisWithConceptSetDTO) dto;
FeAnalysisWithCriteriaEntity> sourceWithCriteria = (FeAnalysisWithCriteriaEntity) source;
@@ -34,26 +39,33 @@ public FeAnalysisDTO convert(final FeAnalysisEntity source) {
@Override
protected FeAnalysisDTO createResultObject(FeAnalysisEntity feAnalysisEntity) {
- switch (feAnalysisEntity.getType()){
- case CRITERIA_SET:
- return new FeAnalysisWithConceptSetDTO();
- default:
- return new FeAnalysisDTO();
- }
+ return Optional.ofNullable(feAnalysisEntity.getType()).map(type -> {
+ switch (type) {
+ case CRITERIA_SET:
+ return new FeAnalysisWithConceptSetDTO();
+ default:
+ return new FeAnalysisDTO();
+ }
+ }).orElseGet(() -> new FeAnalysisDTO());
}
private Object convertDesignToJson(final FeAnalysisEntity source) {
- switch (source.getType()) {
- case CRITERIA_SET:
- FeAnalysisWithCriteriaEntity> sourceWithCriteria = (FeAnalysisWithCriteriaEntity>) source;
- return sourceWithCriteria.getDesign()
- .stream()
- .map(this::convertCriteria)
- .map(c -> (JsonNode)objectMapper.valueToTree(c))
- .collect(Collectors.toList());
- default:
- return source.getDesign();
- }
+ return Optional.ofNullable(source.getType()).map(type -> {
+ switch (type) {
+ case CRITERIA_SET:
+ FeAnalysisWithCriteriaEntity> sourceWithCriteria = (FeAnalysisWithCriteriaEntity>) source;
+ if (CollectionUtils.isEmpty(sourceWithCriteria.getDesign())) {
+ return Collections.emptyList();
+ }
+ return sourceWithCriteria.getDesign()
+ .stream()
+ .map(this::convertCriteria)
+ .map(c -> (JsonNode) objectMapper.valueToTree(c))
+ .collect(Collectors.toList());
+ default:
+ return source.getDesign();
+ }
+ }).orElseGet(() -> source.getDesign());
}
private BaseFeAnalysisCriteriaDTO convertCriteria(FeAnalysisCriteriaEntity criteriaEntity){
diff --git a/src/main/java/org/ohdsi/webapi/feanalysis/domain/FeAnalysisEntity.java b/src/main/java/org/ohdsi/webapi/feanalysis/domain/FeAnalysisEntity.java
index 868303e1ef..b644abaf6a 100644
--- a/src/main/java/org/ohdsi/webapi/feanalysis/domain/FeAnalysisEntity.java
+++ b/src/main/java/org/ohdsi/webapi/feanalysis/domain/FeAnalysisEntity.java
@@ -92,6 +92,12 @@ public FeAnalysisEntity(final FeAnalysisEntity entityForCopy) {
@Enumerated(value = EnumType.STRING)
private CcResultType statType;
+ @Column(name = "supports_annual", updatable = false, insertable = false)
+ private Boolean supportsAnnual;
+
+ @Column(name = "supports_temporal", updatable = false, insertable = false)
+ private Boolean supportsTemporal;
+
@Override
public Integer getId() {
return id;
@@ -207,5 +213,21 @@ public void setStatType(final CcResultType statType) {
this.statType = statType;
}
+
+ public Boolean getSupportsAnnual() {
+ return supportsAnnual;
+ }
+
+ public void setSupportsAnnual(Boolean supportsAnnual) {
+ this.supportsAnnual = supportsAnnual;
+ }
+
+ public Boolean getSupportsTemporal() {
+ return supportsTemporal;
+ }
+
+ public void setSupportsTemporal(Boolean supportsTemporal) {
+ this.supportsTemporal = supportsTemporal;
+ }
}
diff --git a/src/main/java/org/ohdsi/webapi/feanalysis/dto/FeAnalysisShortDTO.java b/src/main/java/org/ohdsi/webapi/feanalysis/dto/FeAnalysisShortDTO.java
index fc0629a091..02bedcf4ac 100644
--- a/src/main/java/org/ohdsi/webapi/feanalysis/dto/FeAnalysisShortDTO.java
+++ b/src/main/java/org/ohdsi/webapi/feanalysis/dto/FeAnalysisShortDTO.java
@@ -10,6 +10,8 @@ public class FeAnalysisShortDTO extends CommonEntityDTO {
@JsonProperty("description")
protected String description;
+ protected Boolean supportsAnnual;
+ protected Boolean supportsTemporal;
@JsonProperty("id")
private Integer id;
@JsonProperty("name")
@@ -20,6 +22,10 @@ public class FeAnalysisShortDTO extends CommonEntityDTO {
private StandardFeatureAnalysisDomain domain;
@JsonProperty("statType")
private CcResultType statType;
+ @JsonProperty("includeAnnual")
+ private Boolean includeAnnual;
+ @JsonProperty("includeTemporal")
+ private Boolean includeTemporal;
public Integer getId() {
@@ -76,4 +82,36 @@ public void setStatType(CcResultType statType) {
this.statType = statType;
}
+
+ public Boolean getSupportsAnnual() {
+ return supportsAnnual;
+ }
+
+ public void setSupportsAnnual(Boolean supportsAnnual) {
+ this.supportsAnnual = supportsAnnual;
+ }
+
+ public Boolean getSupportsTemporal() {
+ return supportsTemporal;
+ }
+
+ public void setSupportsTemporal(Boolean supportsTemporal) {
+ this.supportsTemporal = supportsTemporal;
+ }
+
+ public Boolean getIncludeAnnual() {
+ return includeAnnual;
+ }
+
+ public void setIncludeAnnual(Boolean includeAnnual) {
+ this.includeAnnual = includeAnnual;
+ }
+
+ public Boolean getIncludeTemporal() {
+ return includeTemporal;
+ }
+
+ public void setIncludeTemporal(Boolean includeTemporal) {
+ this.includeTemporal = includeTemporal;
+ }
}
diff --git a/src/main/resources/db/migration/postgresql/V2.14.0.20240126132805__add_temporal_result_permission.sql b/src/main/resources/db/migration/postgresql/V2.14.0.20240126132805__add_temporal_result_permission.sql
new file mode 100644
index 0000000000..2832f61d77
--- /dev/null
+++ b/src/main/resources/db/migration/postgresql/V2.14.0.20240126132805__add_temporal_result_permission.sql
@@ -0,0 +1,10 @@
+INSERT INTO ${ohdsiSchema}.sec_permission(id, value, description) VALUES
+ (nextval('${ohdsiSchema}.sec_permission_id_seq'), 'cohort-characterization:generation:*:temporalresult:get', 'Get cohort characterization generation temporal results');
+
+INSERT INTO ${ohdsiSchema}.sec_role_permission(role_id, permission_id)
+SELECT sr.id, sp.id
+FROM ${ohdsiSchema}.sec_permission SP, ${ohdsiSchema}.sec_role sr
+WHERE sp."value" IN (
+ 'cohort-characterization:generation:*:temporalresult:get'
+ )
+ AND sr.name IN ('Atlas users');
\ No newline at end of file
diff --git a/src/main/resources/db/migration/postgresql/V2.14.0.20240214154300__add_feature_temporal_options.sql b/src/main/resources/db/migration/postgresql/V2.14.0.20240214154300__add_feature_temporal_options.sql
new file mode 100644
index 0000000000..b160f81d2e
--- /dev/null
+++ b/src/main/resources/db/migration/postgresql/V2.14.0.20240214154300__add_feature_temporal_options.sql
@@ -0,0 +1,83 @@
+ALTER TABLE ${ohdsiSchema}.fe_analysis
+ ADD COLUMN IF NOT EXISTS supports_annual BOOLEAN default false,
+ ADD COLUMN IF NOT EXISTS supports_temporal BOOLEAN default false;
+
+UPDATE ${ohdsiSchema}.fe_analysis
+ SET supports_annual = TRUE
+WHERE design in (
+ 'ConditionOccurrenceAnyTimePrior',
+ 'ConditionOccurrenceLongTerm',
+ 'ConditionOccurrenceMediumTerm',
+ 'ConditionOccurrenceShortTerm',
+ 'ConditionOccurrencePrimaryInpatientAnyTimePrior',
+ 'ConditionOccurrencePrimaryInpatientLongTerm',
+ 'ConditionOccurrencePrimaryInpatientMediumTerm',
+ 'ConditionOccurrencePrimaryInpatientShortTerm',
+ 'ConditionEraAnyTimePrior',
+ 'ConditionEraLongTerm',
+ 'ConditionEraMediumTerm',
+ 'ConditionEraShortTerm',
+ 'ConditionEraOverlapping',
+ 'ConditionEraStartLongTerm',
+ 'ConditionEraStartMediumTerm',
+ 'ConditionEraStartShortTerm',
+ 'DrugExposureAnyTimePrior',
+ 'DrugExposureLongTerm',
+ 'DrugExposureMediumTerm',
+ 'DrugExposureShortTerm',
+ 'DrugEraAnyTimePrior',
+ 'DrugEraLongTerm',
+ 'DrugEraMediumTerm',
+ 'DrugEraShortTerm',
+ 'DrugEraOverlapping',
+ 'DrugEraStartLongTerm',
+ 'DrugEraStartMediumTerm',
+ 'DrugEraStartShortTerm',
+ 'ProcedureOccurrenceAnyTimePrior',
+ 'ProcedureOccurrenceLongTerm',
+ 'ProcedureOccurrenceMediumTerm',
+ 'ProcedureOccurrenceShortTerm',
+ 'DeviceExposureAnyTimePrior',
+ 'DeviceExposureLongTerm',
+ 'DeviceExposureMediumTerm',
+ 'DeviceExposureShortTerm',
+ 'MeasurementAnyTimePrior',
+ 'MeasurementLongTerm',
+ 'MeasurementMediumTerm',
+ 'MeasurementShortTerm'
+ 'ObservationAnyTimePrior',
+ 'ObservationLongTerm',
+ 'ObservationMediumTerm',
+ 'ObservationShortTerm'
+);
+
+UPDATE ${ohdsiSchema}.fe_analysis
+ SET supports_temporal = TRUE
+WHERE
+ design in (
+ 'DemographicsGender',
+ 'DemographicsAge',
+ 'DemographicsAgeGroup',
+ 'DemographicsRace',
+ 'DemographicsEthnicity',
+ 'DemographicsIndexYear',
+ 'DemographicsIndexMonth',
+ 'DemographicsPriorObservationTime',
+ 'DemographicsPostObservationTime',
+ 'DemographicsTimeInCohort',
+ 'DemographicsIndexYearMonth'
+);
+
+CREATE SEQUENCE IF NOT EXISTS ${ohdsiSchema}.cc_analysis_seq;
+
+ALTER TABLE ${ohdsiSchema}.cc_analysis
+ ADD COLUMN IF NOT EXISTS id BIGINT NOT NULL DEFAULT nextval('cc_analysis_seq'),
+ ADD COLUMN IF NOT EXISTS include_annual BOOLEAN DEFAULT false,
+ ADD COLUMN IF NOT EXISTS include_temporal BOOLEAN DEFAULT false;
+
+SELECT setval('${ohdsiSchema}.cc_analysis_seq', COALESCE(MAX(id) + 1, 1), false) FROM ${ohdsiSchema}.cc_analysis;
+
+ALTER TABLE ${ohdsiSchema}.cc_analysis
+ DROP CONSTRAINT cc_analysis_pkey;
+ALTER TABLE ${ohdsiSchema}.cc_analysis
+ ADD CONSTRAINT cc_analysis_pkey PRIMARY KEY (id);
diff --git a/src/main/resources/ddl/results/cohort_characterizations.sql b/src/main/resources/ddl/results/cohort_characterizations.sql
index 077aa1f6eb..d5fcd4bb0d 100644
--- a/src/main/resources/ddl/results/cohort_characterizations.sql
+++ b/src/main/resources/ddl/results/cohort_characterizations.sql
@@ -26,4 +26,42 @@ CREATE TABLE @results_schema.cc_results
aggregate_id INTEGER,
aggregate_name VARCHAR(1000),
missing_means_zero INTEGER
+);
+
+IF OBJECT_ID('@results_schema.cc_temporal_results') IS NULL
+CREATE TABLE @results_schema.cc_temporal_results(
+ type varchar(255),
+ fa_type varchar(255),
+ cc_generation_id bigint,
+ analysis_id integer,
+ analysis_name varchar(1000),
+ covariate_id bigint,
+ covariate_name varchar(1000),
+ strata_id bigint,
+ strata_name varchar(1000),
+ concept_id integer,
+ count_value bigint,
+ avg_value double precision,
+ cohort_definition_id bigint,
+ time_id integer,
+ start_day integer,
+ end_day integer
+);
+
+IF OBJECT_ID('@results_schema.cc_temporal_annual_results') IS NULL
+ CREATE TABLE @results_schema.cc_temporal_annual_results(
+ type varchar(255),
+ fa_type varchar(255),
+ cc_generation_id bigint,
+ analysis_id integer,
+ analysis_name varchar(1000),
+ covariate_id bigint,
+ covariate_name varchar(1000),
+ strata_id bigint,
+ strata_name varchar(1000),
+ concept_id integer,
+ count_value bigint,
+ avg_value double precision,
+ cohort_definition_id bigint,
+ event_year integer
);
\ No newline at end of file
diff --git a/src/main/resources/i18n/messages_en.json b/src/main/resources/i18n/messages_en.json
index be9e537c5c..601f5f8252 100644
--- a/src/main/resources/i18n/messages_en.json
+++ b/src/main/resources/i18n/messages_en.json
@@ -1477,6 +1477,7 @@
"stddev": "Std Dev",
"strata": "Strata",
"suggestedNegativeControl": "Suggested Negative Control",
+ "supportsAnnual": "Supports Annual",
"tableQualifiers": "Table Qualifiers",
"target": "Target",
"targetCohortName": "Target Cohort Name",
diff --git a/src/main/resources/resources/cohortcharacterizations/sql/queryTemporalAnnualResults.sql b/src/main/resources/resources/cohortcharacterizations/sql/queryTemporalAnnualResults.sql
new file mode 100644
index 0000000000..10a57fa000
--- /dev/null
+++ b/src/main/resources/resources/cohortcharacterizations/sql/queryTemporalAnnualResults.sql
@@ -0,0 +1,21 @@
+select
+ r.type,
+ r.fa_type,
+ r.cc_generation_id,
+ r.analysis_id,
+ r.analysis_name,
+ r.covariate_id,
+ r.covariate_name,
+ c.concept_name,
+ r.concept_id,
+ r.count_value,
+ r.avg_value,
+ r.cohort_definition_id,
+ r.strata_id,
+ r.strata_name,
+ r.event_year
+from @results_database_schema.cc_temporal_annual_results r
+ JOIN @vocabulary_schema.concept c on c.concept_id = r.concept_id
+where r.cc_generation_id = @cohort_characterization_generation_id
+ and r.analysis_id in (@analysis_ids)
+ and r.cohort_definition_id in (@cohort_ids)
diff --git a/src/main/resources/resources/cohortcharacterizations/sql/queryTemporalResults.sql b/src/main/resources/resources/cohortcharacterizations/sql/queryTemporalResults.sql
new file mode 100644
index 0000000000..e3a7d8adb5
--- /dev/null
+++ b/src/main/resources/resources/cohortcharacterizations/sql/queryTemporalResults.sql
@@ -0,0 +1,23 @@
+select
+ r.type,
+ r.fa_type,
+ r.cc_generation_id,
+ r.analysis_id,
+ r.analysis_name,
+ r.covariate_id,
+ r.covariate_name,
+ c.concept_name,
+ r.concept_id,
+ r.count_value,
+ r.avg_value,
+ r.cohort_definition_id,
+ r.strata_id,
+ r.strata_name,
+ r.start_day,
+ r.end_day,
+ r.time_id
+from @results_database_schema.cc_temporal_results r
+ JOIN @vocabulary_schema.concept c on c.concept_id = r.concept_id
+where r.cc_generation_id = @cohort_characterization_generation_id
+ and r.analysis_id in (@analysis_ids)
+ and r.cohort_definition_id in (@cohort_ids)