/*
* Copyright 2015 Observational Health Data Sciences and Informatics [OHDSI.org].
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ohdsi.webapi.service;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.commons.lang3.StringUtils;
import org.ohdsi.sql.SqlTranslate;
import org.ohdsi.webapi.feasibility.InclusionRule;
import org.ohdsi.webapi.feasibility.FeasibilityStudy;
import org.ohdsi.webapi.feasibility.PerformFeasibilityTasklet;
import org.ohdsi.webapi.feasibility.StudyGenerationInfo;
import org.ohdsi.webapi.feasibility.FeasibilityReport;
import org.ohdsi.webapi.cohortdefinition.CohortDefinition;
import org.ohdsi.webapi.cohortdefinition.CohortDefinitionRepository;
import org.ohdsi.webapi.TerminateJobStepExceptionHandler;
import org.ohdsi.webapi.cohortdefinition.CohortDefinitionDetails;
import org.ohdsi.webapi.cohortdefinition.CohortExpression;
import org.ohdsi.webapi.cohortdefinition.CohortGenerationInfo;
import org.ohdsi.webapi.cohortdefinition.CriteriaGroup;
import org.ohdsi.webapi.cohortdefinition.ExpressionType;
import org.ohdsi.webapi.feasibility.FeasibilityStudyRepository;
import org.ohdsi.webapi.cohortdefinition.GenerateCohortTasklet;
import org.ohdsi.webapi.GenerationStatus;
import org.ohdsi.webapi.job.JobExecutionResource;
import org.ohdsi.webapi.job.JobTemplate;
import org.ohdsi.webapi.shiro.management.Security;
import org.ohdsi.webapi.source.Source;
import org.ohdsi.webapi.source.SourceDaimon;
import org.ohdsi.webapi.util.SessionUtils;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/**
*
* @author Chris Knoll <cknoll@ohdsi.org>
*/
@Path("/feasibility/")
@Component
public class FeasibilityService extends AbstractDaoService {
@Autowired
private CohortDefinitionRepository cohortDefinitionRepository;
@Autowired
private FeasibilityStudyRepository feasibilityStudyRepository;
@Autowired
private CohortDefinitionService definitionService;
@Autowired
private JobBuilderFactory jobBuilders;
@Autowired
private StepBuilderFactory stepBuilders;
@Autowired
private JobTemplate jobTemplate;
@Autowired
private Security security;
@Context
ServletContext context;
private StudyGenerationInfo findStudyGenerationInfoBySourceId(Collection<StudyGenerationInfo> infoList, Integer sourceId) {
for (StudyGenerationInfo info : infoList) {
if (info.getId().getSourceId() == sourceId) {
return info;
}
}
return null;
}
private CohortGenerationInfo findCohortGenerationInfoBySourceId(Collection<CohortGenerationInfo> infoList, Integer sourceId) {
for (CohortGenerationInfo info : infoList) {
if (info.getId().getSourceId() == sourceId) {
return info;
}
}
return null;
}
public static class FeasibilityStudyListItem {
public Integer id;
public String name;
public String description;
public String createdBy;
public Integer indexCohortId;
public Integer matchingCohortId;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd, HH:mm")
public Date createdDate;
public String modifiedBy;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd, HH:mm")
public Date modifiedDate;
}
public static class FeasibilityStudyDTO extends FeasibilityStudyListItem {
public String indexRule;
public String indexDescription;
public List<InclusionRule> inclusionRules;
}
public static class StudyInfoDTO {
public StudyGenerationInfo generationInfo;
public FeasibilityReport.Summary summary;
}
private final RowMapper<FeasibilityReport.Summary> summaryMapper = new RowMapper<FeasibilityReport.Summary>() {
@Override
public FeasibilityReport.Summary mapRow(ResultSet rs, int rowNum) throws SQLException {
FeasibilityReport.Summary summary = new FeasibilityReport.Summary();
summary.totalPersons = rs.getLong("person_count");
summary.matchingPersons = rs.getLong("match_count");
double matchRatio = (summary.totalPersons > 0) ? ((double) summary.matchingPersons / (double) summary.totalPersons) : 0.0;
summary.percentMatched = new BigDecimal(matchRatio * 100.0).setScale(2, RoundingMode.HALF_UP).toPlainString() + "%";
return summary;
}
};
private FeasibilityReport.Summary getSimulationSummary(int id, Source source) {
String resultsTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.Results);
String summaryQuery = String.format("select person_count, match_count from %s.feas_study_index_stats where study_id = %d", resultsTableQualifier, id);
String translatedSql = SqlTranslate.translateSql(summaryQuery, "sql server", source.getSourceDialect(), SessionUtils.sessionId(), resultsTableQualifier);
List<FeasibilityReport.Summary> summaryList = this.getSourceJdbcTemplate(source).query(translatedSql, summaryMapper);
if (summaryList.size() > 0)
return summaryList.get(0);
return null;
}
private final RowMapper<FeasibilityReport.InclusionRuleStatistic> inclusionRuleStatisticMapper = new RowMapper<FeasibilityReport.InclusionRuleStatistic>() {
@Override
public FeasibilityReport.InclusionRuleStatistic mapRow(ResultSet rs, int rowNum) throws SQLException {
FeasibilityReport.InclusionRuleStatistic statistic = new FeasibilityReport.InclusionRuleStatistic();
statistic.id = rs.getInt("rule_sequence");
statistic.name = rs.getString("name");
statistic.countSatisfying = rs.getLong("person_count");
long personTotal = rs.getLong("person_total");
long gainCount = rs.getLong("gain_count");
double excludeRatio = personTotal > 0 ? (double) gainCount / (double) personTotal : 0.0;
String percentExcluded = new BigDecimal(excludeRatio * 100.0).setScale(2, RoundingMode.HALF_UP).toPlainString();
statistic.percentExcluded = percentExcluded + "%";
long satisfyCount = rs.getLong("person_count");
double satisfyRatio = personTotal > 0 ? (double) satisfyCount / (double) personTotal : 0.0;
String percentSatisfying = new BigDecimal(satisfyRatio * 100.0).setScale(2, RoundingMode.HALF_UP).toPlainString();
statistic.percentSatisfying = percentSatisfying + "%";
return statistic;
}
};
private String getMatchingCriteriaExpression(FeasibilityStudy p) {
if (p.getInclusionRules().size() == 0) {
throw new RuntimeException("Study must have at least 1 inclusion rule");
}
try {
// all resultRule repository objects are initalized; create 'all criteria' cohort definition from index rule + inclusion rules
ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
CohortExpression indexRuleExpression = mapper.readValue(p.getIndexRule().getDetails().getExpression(), CohortExpression.class);
if (indexRuleExpression.additionalCriteria == null) {
CriteriaGroup additionalCriteria = new CriteriaGroup();
additionalCriteria.type = "ALL";
indexRuleExpression.additionalCriteria = additionalCriteria;
} else {
if (!"ALL".equalsIgnoreCase(indexRuleExpression.additionalCriteria.type)) {
// move this CriteriaGroup inside a new parent CriteriaGroup where the parent CriteriaGroup.type == "ALL"
CriteriaGroup parentGroup = new CriteriaGroup();
parentGroup.type = "ALL";
parentGroup.groups = new CriteriaGroup[1];
parentGroup.groups[0] = indexRuleExpression.additionalCriteria;
indexRuleExpression.additionalCriteria = parentGroup;
}
}
// place each inclusion rule (which is a CriteriaGroup) in the indexRuleExpression.additionalCriteria.group array to create the 'allCriteriaExpression'
ArrayList<CriteriaGroup> additionalCriteriaGroups = new ArrayList<>();
if (indexRuleExpression.additionalCriteria.groups != null) {
additionalCriteriaGroups.addAll(Arrays.asList(indexRuleExpression.additionalCriteria.groups));
}
for (InclusionRule inclusionRule : p.getInclusionRules()) {
String inclusionRuleJSON = inclusionRule.getExpression();
CriteriaGroup inclusionRuleGroup = mapper.readValue(inclusionRuleJSON, CriteriaGroup.class);
additionalCriteriaGroups.add(inclusionRuleGroup);
}
// overwrite indexRule additional criteria groups with the new list of groups with inclusion rules
indexRuleExpression.additionalCriteria.groups = additionalCriteriaGroups.toArray(new CriteriaGroup[0]);
String allCriteriaExpression = mapper.writeValueAsString(indexRuleExpression); // index rule expression now contains all inclusion criteria as additional criteria
return allCriteriaExpression;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private List<FeasibilityReport.InclusionRuleStatistic> getSimulationInclusionRuleStatistics(int id, Source source) {
String resultsTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.Results);
String statisticsQuery = String.format("select rule_sequence, name, person_count, gain_count, person_total from %s.feas_study_inclusion_stats where study_id = %d ORDER BY rule_sequence", resultsTableQualifier, id);
String translatedSql = SqlTranslate.translateSql(statisticsQuery, "sql server", source.getSourceDialect(), SessionUtils.sessionId(), resultsTableQualifier);
return this.getSourceJdbcTemplate(source).query(translatedSql, inclusionRuleStatisticMapper);
}
private int countSetBits(long n) {
int count = 0;
while (n > 0) {
n &= (n - 1);
count++;
}
return count;
}
private String formatBitMask(Long n, int size) {
return StringUtils.reverse(StringUtils.leftPad(Long.toBinaryString(n), size, "0"));
}
private final RowMapper<Long[]> simulationResultItemMapper = new RowMapper<Long[]>() {
@Override
public Long[] mapRow(ResultSet rs, int rowNum) throws SQLException {
Long[] resultItem = new Long[2];
resultItem[0] = rs.getLong("inclusion_rule_mask");
resultItem[1] = rs.getLong("person_count");
return resultItem;
}
};
private String getInclusionRuleTreemapData(int id, int inclusionRuleCount, Source source) {
String resultsTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.Results);
String smulationResultsQuery = String.format("select inclusion_rule_mask, person_count from %s.feas_study_result where study_id = %d",
resultsTableQualifier, id);
String translatedSql = SqlTranslate.translateSql(smulationResultsQuery, "sql server", source.getSourceDialect(), SessionUtils.sessionId(), resultsTableQualifier);
// [0] is the inclusion rule bitmask, [1] is the count of the match
List<Long[]> items = this.getSourceJdbcTemplate(source).query(translatedSql, simulationResultItemMapper);
Map<Integer, List<Long[]>> groups = new HashMap<>();
for (Long[] item : items) {
int bitsSet = countSetBits(item[0]);
if (!groups.containsKey(bitsSet)) {
groups.put(bitsSet, new ArrayList<Long[]>());
}
groups.get(bitsSet).add(item);
}
StringBuilder treemapData = new StringBuilder("{\"name\" : \"Everyone\", \"children\" : [");
List<Integer> groupKeys = new ArrayList<Integer>(groups.keySet());
Collections.sort(groupKeys);
Collections.reverse(groupKeys);
int groupCount = 0;
// create a nested treemap data where more matches (more bits set in string) appear higher in the hierarchy)
for (Integer groupKey : groupKeys) {
if (groupCount > 0) {
treemapData.append(",");
}
treemapData.append(String.format("{\"name\" : \"Group %d\", \"children\" : [", groupKey));
int groupItemCount = 0;
for (Long[] groupItem : groups.get(groupKey)) {
if (groupItemCount > 0) {
treemapData.append(",");
}
//sb_treemap.Append("{\"name\": \"" + cohort_identifer + "\", \"size\": " + cohorts[cohort_identifer].ToString() + "}");
treemapData.append(String.format("{\"name\": \"%s\", \"size\": %d}", formatBitMask(groupItem[0], inclusionRuleCount), groupItem[1]));
groupItemCount++;
}
groupCount++;
}
treemapData.append(StringUtils.repeat("]}", groupCount + 1));
return treemapData.toString();
}
public FeasibilityStudyDTO feasibilityStudyToDTO(FeasibilityStudy study) {
FeasibilityStudyDTO pDTO = new FeasibilityStudyDTO();
pDTO.id = study.getId();
pDTO.name = study.getName();
pDTO.description = study.getDescription();
pDTO.indexCohortId = study.getIndexRule().getId();
pDTO.matchingCohortId = study.getResultRule() != null ? study.getResultRule().getId() : null;
pDTO.createdBy = study.getCreatedBy();
pDTO.createdDate = study.getCreatedDate();
pDTO.modifiedBy = study.getModifiedBy();
pDTO.modifiedDate = study.getModifiedDate();
pDTO.indexRule = study.getIndexRule().getDetails().getExpression();
pDTO.indexDescription = study.getIndexRule().getDescription();
pDTO.inclusionRules = study.getInclusionRules();
return pDTO;
}
/**
* Returns all cohort definitions in the cohort schema
*
* @return List of cohort_definition
*/
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public List<FeasibilityService.FeasibilityStudyListItem> getFeasibilityStudyList() {
ArrayList<FeasibilityService.FeasibilityStudyListItem> result = new ArrayList<>();
Iterable<FeasibilityStudy> studies = this.feasibilityStudyRepository.findAll();
for (FeasibilityStudy p : studies) {
FeasibilityService.FeasibilityStudyListItem item = new FeasibilityService.FeasibilityStudyListItem();
item.id = p.getId();
item.name = p.getName();
item.description = p.getDescription();
item.indexCohortId = p.getIndexRule().getId();
item.matchingCohortId = p.getResultRule() != null ? p.getResultRule().getId() : null;
item.createdBy = p.getCreatedBy();
item.createdDate = p.getCreatedDate();
item.modifiedBy = p.getModifiedBy();
item.modifiedDate = p.getModifiedDate();
result.add(item);
}
return result;
}
/**
* Creates the feasibility study
*
* @param study The study to create.
* @return The new FeasibilityStudy
*/
@PUT
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
public FeasibilityService.FeasibilityStudyDTO createStudy(FeasibilityService.FeasibilityStudyDTO study) {
Date currentTime = Calendar.getInstance().getTime();
//create definition in 2 saves, first to get the generated ID for the new cohort definition (the index rule)
// then to associate the new definition with the index rule of the study
FeasibilityStudy newStudy = new FeasibilityStudy();
newStudy.setName(study.name)
.setDescription(study.description)
.setCreatedBy(security.getSubject())
.setCreatedDate(currentTime)
.setInclusionRules(new ArrayList<InclusionRule>(study.inclusionRules));
// create index cohort
CohortDefinition indexRule = new CohortDefinition()
.setName("Index Population for Study: " + newStudy.getName())
.setDescription(study.indexDescription)
.setCreatedBy(security.getSubject())
.setCreatedDate(currentTime)
.setExpressionType(ExpressionType.SIMPLE_EXPRESSION);
CohortDefinitionDetails indexDetails = new CohortDefinitionDetails();
indexDetails.setCohortDefinition(indexRule)
.setExpression(study.indexRule);
indexRule.setDetails(indexDetails);
newStudy.setIndexRule(indexRule);
// build matching cohort from inclusion rules if inclusion rules exist
if (newStudy.getInclusionRules().size() > 0) {
CohortDefinition resultDef = new CohortDefinition()
.setName("Matching Population for Study: " + newStudy.getName())
.setDescription(newStudy.getDescription())
.setCreatedBy(security.getSubject())
.setCreatedDate(currentTime)
.setExpressionType(ExpressionType.SIMPLE_EXPRESSION);
CohortDefinitionDetails resultDetails = new CohortDefinitionDetails();
resultDetails.setCohortDefinition(resultDef)
.setExpression(getMatchingCriteriaExpression(newStudy));
resultDef.setDetails(resultDetails);
newStudy.setResultRule(resultDef);
}
FeasibilityStudy createdStudy = this.feasibilityStudyRepository.save(newStudy);
return feasibilityStudyToDTO(createdStudy);
}
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Transactional(readOnly = true)
public FeasibilityService.FeasibilityStudyDTO getStudy(@PathParam("id") final int id) {
FeasibilityStudy s = this.feasibilityStudyRepository.findOneWithDetail(id);
return feasibilityStudyToDTO(s);
}
@PUT
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public FeasibilityService.FeasibilityStudyDTO saveStudy(@PathParam("id") final int id, FeasibilityStudyDTO study) {
Date currentTime = Calendar.getInstance().getTime();
FeasibilityStudy updatedStudy = this.feasibilityStudyRepository.findOne(id);
updatedStudy.setName(study.name)
.setDescription(study.description)
.setModifiedBy(security.getSubject())
.setModifiedDate(currentTime)
.setInclusionRules(study.inclusionRules);
updatedStudy.getIndexRule()
.setModifiedBy(security.getSubject())
.setModifiedDate(currentTime)
.setName("Index Population for Study: " + updatedStudy.getName())
.setDescription(study.indexDescription)
.getDetails().setExpression(study.indexRule);
CohortDefinition resultRule = updatedStudy.getResultRule();
if (updatedStudy.getInclusionRules().size() > 0) {
if (resultRule == null) {
resultRule = new CohortDefinition();
resultRule.setName("Matching Population for Study: " + updatedStudy.getName())
.setCreatedBy(security.getSubject())
.setCreatedDate(currentTime)
.setExpressionType(ExpressionType.SIMPLE_EXPRESSION);
CohortDefinitionDetails resultDetails = new CohortDefinitionDetails();
resultDetails.setCohortDefinition(resultRule);
resultRule.setDetails(resultDetails);
updatedStudy.setResultRule(resultRule);
}
resultRule.setModifiedBy(security.getSubject())
.setModifiedDate(currentTime)
.setName("Matching Population for Study: " + updatedStudy.getName())
.setDescription(updatedStudy.getDescription())
.setModifiedBy(security.getSubject())
.setModifiedDate(currentTime)
.getDetails().setExpression(getMatchingCriteriaExpression(updatedStudy));
} else {
updatedStudy.setResultRule(null);
if (resultRule != null) {
cohortDefinitionRepository.delete(resultRule);
}
}
this.feasibilityStudyRepository.save(updatedStudy);
return getStudy(id);
}
@GET
@Path("/{study_id}/generate/{sourceKey}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public JobExecutionResource performStudy(@PathParam("study_id") final int study_id, @PathParam("sourceKey") final String sourceKey) {
Date startTime = Calendar.getInstance().getTime();
Source source = this.getSourceRepository().findBySourceKey(sourceKey);
String resultsTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.Results);
String cdmTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.CDM);
DefaultTransactionDefinition requresNewTx = new DefaultTransactionDefinition();
requresNewTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus initStatus = this.getTransactionTemplate().getTransactionManager().getTransaction(requresNewTx);
FeasibilityStudy study = this.feasibilityStudyRepository.findOne(study_id);
CohortDefinition indexRule = this.cohortDefinitionRepository.findOne(study.getIndexRule().getId());
CohortGenerationInfo indexInfo = findCohortGenerationInfoBySourceId(indexRule.getGenerationInfoList(), source.getSourceId());
if (indexInfo == null) {
indexInfo = new CohortGenerationInfo(indexRule, source.getSourceId());
indexRule.getGenerationInfoList().add(indexInfo);
}
indexInfo.setStatus(GenerationStatus.PENDING)
.setStartTime(startTime)
.setExecutionDuration(null);
this.cohortDefinitionRepository.save(indexRule);
if (study.getResultRule() != null)
{
CohortDefinition resultRule = this.cohortDefinitionRepository.findOne(study.getResultRule().getId());
CohortGenerationInfo resultInfo = findCohortGenerationInfoBySourceId(resultRule.getGenerationInfoList(), source.getSourceId());
if (resultInfo == null) {
resultInfo = new CohortGenerationInfo(resultRule, source.getSourceId());
resultRule.getGenerationInfoList().add(resultInfo);
}
resultInfo.setStatus(GenerationStatus.PENDING)
.setStartTime(startTime)
.setExecutionDuration(null);
this.cohortDefinitionRepository.save(resultRule);
}
StudyGenerationInfo studyInfo = findStudyGenerationInfoBySourceId(study.getStudyGenerationInfoList(), source.getSourceId());
if (studyInfo == null) {
studyInfo = new StudyGenerationInfo(study, source);
study.getStudyGenerationInfoList().add(studyInfo);
}
studyInfo.setStatus(GenerationStatus.PENDING)
.setStartTime(startTime)
.setExecutionDuration(null);
this.feasibilityStudyRepository.save(study);
this.getTransactionTemplate().getTransactionManager().commit(initStatus);
JobParametersBuilder builder = new JobParametersBuilder();
builder.addString("jobName", "performing feasibility study on " + indexRule.getName() + " : " + source.getSourceName() + " (" + source.getSourceKey() + ")");
builder.addString("cdm_database_schema", cdmTableQualifier);
builder.addString("results_database_schema", resultsTableQualifier);
builder.addString("target_database_schema", resultsTableQualifier);
builder.addString("target_dialect", source.getSourceDialect());
builder.addString("target_table", "cohort");
builder.addString("cohort_definition_id", ("" + indexRule.getId()));
builder.addString("study_id", ("" + study_id));
builder.addString("source_id", ("" + source.getSourceId()));
builder.addString("generate_stats", Boolean.TRUE.toString());
final JobParameters jobParameters = builder.toJobParameters();
final JdbcTemplate sourceJdbcTemplate = getSourceJdbcTemplate(source);
GenerateCohortTasklet indexRuleTasklet = new GenerateCohortTasklet(sourceJdbcTemplate, getTransactionTemplate(), cohortDefinitionRepository);
Step generateCohortStep = stepBuilders.get("performStudy.generateIndexCohort")
.tasklet(indexRuleTasklet)
.exceptionHandler(new TerminateJobStepExceptionHandler())
.build();
PerformFeasibilityTasklet simulateTasket = new PerformFeasibilityTasklet(sourceJdbcTemplate, getTransactionTemplate(), feasibilityStudyRepository, cohortDefinitionRepository);
Step performStudyStep = stepBuilders.get("performStudy.performStudy")
.tasklet(simulateTasket)
.build();
Job performStudyJob = jobBuilders.get("performStudy")
.start(generateCohortStep)
.next(performStudyStep)
.build();
JobExecutionResource jobExec = this.jobTemplate.launch(performStudyJob, jobParameters);
return jobExec;
}
@GET
@Path("/{id}/info")
@Produces(MediaType.APPLICATION_JSON)
@Transactional(readOnly = true)
public List<StudyInfoDTO> getSimulationInfo(@PathParam("id") final int id) {
FeasibilityStudy study = this.feasibilityStudyRepository.findOne(id);
List<StudyInfoDTO> result = new ArrayList<>();
for (StudyGenerationInfo generationInfo : study.getStudyGenerationInfoList()) {
StudyInfoDTO info = new StudyInfoDTO();
info.generationInfo = generationInfo;
info.summary = getSimulationSummary(id, generationInfo.getSource());
result.add(info);
}
return result;
}
@GET
@Path("/{id}/report/{sourceKey}")
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public FeasibilityReport getSimulationReport(@PathParam("id") final int id, @PathParam("sourceKey") final String sourceKey) {
Source source = this.getSourceRepository().findBySourceKey(sourceKey);
FeasibilityReport.Summary summary = getSimulationSummary(id, source);
List<FeasibilityReport.InclusionRuleStatistic> inclusionRuleStats = getSimulationInclusionRuleStatistics(id, source);
String treemapData = getInclusionRuleTreemapData(id, inclusionRuleStats.size(), source);
FeasibilityReport report = new FeasibilityReport();
report.summary = summary;
report.inclusionRuleStats = inclusionRuleStats;
report.treemapData = treemapData;
return report;
}
/**
* Copies the specified cohort definition
*
* @param id - the Cohort Definition ID to copy
* @return the copied cohort definition as a CohortDefinitionDTO
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{id}/copy")
@javax.transaction.Transactional
public FeasibilityStudyDTO copy(@PathParam("id") final int id) {
FeasibilityStudyDTO sourceStudy = getStudy(id);
sourceStudy.id = null; // clear the ID
sourceStudy.name = "COPY OF: " + sourceStudy.name;
FeasibilityStudyDTO copyStudy = createStudy(sourceStudy);
return copyStudy;
}
/**
* Deletes the specified cohort definition
*
* @param id - the Cohort Definition ID to copy
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("/{id}")
public void delete(@PathParam("id") final int id) {
feasibilityStudyRepository.delete(id);
}
/**
* Deletes the specified cohort definition
*
* @param id - the Cohort Definition ID to copy
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("/{id}/info/{sourceKey}")
@Transactional
public void deleteInfo(@PathParam("id") final int id, @PathParam("sourceKey") final String sourceKey) {
FeasibilityStudy study = feasibilityStudyRepository.findOne(id);
StudyGenerationInfo itemToRemove = null;
for (StudyGenerationInfo info : study.getStudyGenerationInfoList())
{
if (info.getSource().getSourceKey().equals(sourceKey))
itemToRemove = info;
}
if (itemToRemove != null)
study.getStudyGenerationInfoList().remove(itemToRemove);
feasibilityStudyRepository.save(study);
}
}