Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
89a3974
Initial sampling implementation
blootsvoets Nov 28, 2019
3eaf3d6
Add name to cohort sample
blootsvoets Dec 5, 2019
dbe6328
Revert server port to 8080
blootsvoets Dec 19, 2019
c0cfc65
Fix queryParam -> pathParam
blootsvoets Jan 8, 2020
ea742ff
Initial update to fix SQL commands for sample generation
blootsvoets Jan 8, 2020
ca86aad
Attempt to move cohort_sample table to ohdsi
blootsvoets Jan 9, 2020
d70cc25
Separate cohort_sample from cohort_sample_element
blootsvoets Jan 9, 2020
c3adeba
Create index in separate query
blootsvoets Jan 9, 2020
7933edf
Misc fixes
blootsvoets Jan 15, 2020
d82594e
Fix gender sampling and DTOs
blootsvoets Jan 16, 2020
1e7ef5a
Documentation and exception string updates
blootsvoets Jan 20, 2020
d65efea
Provide record counts with each sample element
blootsvoets Jan 20, 2020
dafbb21
Make sample record counts easier to turn off
blootsvoets Jan 20, 2020
83fd26c
Make record counts optional
blootsvoets Jan 21, 2020
76ef810
Delete samples on cohort redefinition
blootsvoets Jan 21, 2020
ccb005b
Run sample deletion in tasklet
blootsvoets Jan 21, 2020
f83d408
Don't run removal if no cohort samples are present
blootsvoets Jan 21, 2020
0b65c77
Fixes with authorization and SQL server
blootsvoets Jan 23, 2020
85c793b
Check cohort generation status before generating sample
blootsvoets Jan 23, 2020
e192d45
Fix Oracle and PostgreSQL migration syntax
blootsvoets Jan 27, 2020
60da4e0
add endpoint to check that samples exist for a cohort
Feb 10, 2020
a26f765
calcuate age in subquery to allow usage in sample expression
Feb 10, 2020
e381ec1
delete samples of a source upon triggering cohort generation of that …
Feb 26, 2020
dde9418
fix sql for gender sample criteria
Feb 28, 2020
10fd14d
add has-samples endpoint by sourceKey and cohortDefinitionId
Mar 4, 2020
b4df22f
add isValid attribute to cohortsample summary
Mar 4, 2020
6a25647
Merge pull request #2 from thehyve/has-samples-by-source
Mar 4, 2020
7366005
Merge branch 'master-ohdsi' into cohort-sampling
Oct 14, 2020
8454e34
Merge branch 'master' into cohort-sampling
Oct 14, 2020
a25245b
use tab-indentation
Oct 14, 2020
1019d91
Make person ID a String for JS compatibility
Oct 28, 2020
681619b
fix corrupt bigint person_id
Oct 30, 2020
329b717
Rename rank to rank_value.
chrisknoll Nov 4, 2020
0dbb9f8
Added refresh function.
chrisknoll Nov 6, 2020
5f17870
Made refreshCohortSample POST.
chrisknoll Nov 9, 2020
bdea2c1
Added security records for refresh to migration scripts.
chrisknoll Nov 9, 2020
b1e15fc
Merge pull request #4 from chrisknoll/cohort-sampling-cknoll1-update1
blootsvoets Nov 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
WebAPIConfig/
*application.properties
.idea/
.metadata/
Expand Down
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@
<logging.level.org.ohdsi>info</logging.level.org.ohdsi>
<logging.level.org.springframework.orm>info</logging.level.org.springframework.orm>
<logging.level.org.springframework.jdbc>info</logging.level.org.springframework.jdbc>
<logging.level.org.springframework.web>info</logging.level.org.springframework.web>
<logging.level.org.hibernate>info</logging.level.org.hibernate>
<logging.level.org.apache.shiro>warn</logging.level.org.apache.shiro>

<spring.batch.taskExecutor.corePoolSize>10</spring.batch.taskExecutor.corePoolSize>
Expand Down Expand Up @@ -949,6 +951,11 @@
<version>2.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<profiles>
<profile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package org.ohdsi.webapi.cohortsample;

import org.ohdsi.webapi.cohortdefinition.CleanupCohortTasklet;
import org.ohdsi.webapi.job.JobExecutionResource;
import org.ohdsi.webapi.job.JobTemplate;
import org.ohdsi.webapi.source.Source;
import org.ohdsi.webapi.source.SourceDaimon;
import org.ohdsi.webapi.source.SourceRepository;
import org.ohdsi.webapi.util.PreparedStatementRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.List;
import java.util.Map;

import static org.ohdsi.webapi.Constants.Params.COHORT_DEFINITION_ID;
import static org.ohdsi.webapi.Constants.Params.JOB_NAME;
import static org.ohdsi.webapi.Constants.Params.SOURCE_ID;

public class CleanupCohortSamplesTasklet implements Tasklet {
private static final Logger log = LoggerFactory.getLogger(CleanupCohortTasklet.class);

private final TransactionTemplate transactionTemplate;
private final SourceRepository sourceRepository;
private final CohortSamplingService samplingService;
private final CohortSampleRepository sampleRepository;

public CleanupCohortSamplesTasklet(
final TransactionTemplate transactionTemplate,
final SourceRepository sourceRepository,
CohortSamplingService samplingService,
CohortSampleRepository sampleRepository
) {
this.transactionTemplate = transactionTemplate;
this.sourceRepository = sourceRepository;
this.samplingService = samplingService;
this.sampleRepository = sampleRepository;
}

private Integer doTask(ChunkContext chunkContext) {
Map<String, Object> jobParams = chunkContext.getStepContext().getJobParameters();
int cohortDefinitionId = Integer.parseInt(jobParams.get(COHORT_DEFINITION_ID).toString());

if (jobParams.containsKey(SOURCE_ID)) {
int sourceId = Integer.parseInt(jobParams.get(SOURCE_ID).toString());
Source source = this.sourceRepository.findOne(sourceId);
if (source != null) {
return mapSource(source, cohortDefinitionId);
} else {
return 0;
}
} else {
return this.sourceRepository.findAll().stream()
.filter(source-> source.getDaimons()
.stream()
.anyMatch(daimon -> daimon.getDaimonType() == SourceDaimon.DaimonType.Results))
.mapToInt(source -> mapSource(source, cohortDefinitionId))
.sum();
}
}

private int mapSource(Source source, int cohortDefinitionId) {
try {
String resultSchema = source.getTableQualifier(SourceDaimon.DaimonType.Results);
return transactionTemplate.execute(transactionStatus -> {
List<CohortSample> samples = sampleRepository.findByCohortDefinitionIdAndSourceId(cohortDefinitionId, source.getId());
if (samples.isEmpty()) {
return 0;
}

sampleRepository.delete(samples);

int[] cohortSampleIds = samples.stream()
.mapToInt(CohortSample::getId)
.toArray();

PreparedStatementRenderer renderer = new PreparedStatementRenderer(
source,
"/resources/cohortsample/sql/deleteSampleElementsById.sql",
"results_schema",
resultSchema,
"cohortSampleId",
cohortSampleIds);

samplingService.getSourceJdbcTemplate(source)
.update(renderer.getSql(), renderer.getOrderedParams());
return cohortSampleIds.length;
});
} catch (Exception e) {
log.error("Error deleting samples for cohort: {}, cause: {}", cohortDefinitionId, e.getMessage());
return 0;
}
}

@Override
public RepeatStatus execute(final StepContribution contribution, final ChunkContext chunkContext) throws Exception {
this.transactionTemplate.execute(status -> doTask(chunkContext));

return RepeatStatus.FINISHED;
}

public JobExecutionResource launch(JobBuilderFactory jobBuilders, StepBuilderFactory stepBuilders, JobTemplate jobTemplate, int cohortDefinitionId) {
JobParametersBuilder builder = new JobParametersBuilder();
builder.addString(JOB_NAME, String.format("Cleanup cohort samples of cohort definition %d.", cohortDefinitionId));
builder.addString(COHORT_DEFINITION_ID, String.valueOf(cohortDefinitionId));

log.info("Beginning cohort cleanup for cohort definition id: {}", cohortDefinitionId);
return launch(jobBuilders, stepBuilders, jobTemplate, builder.toJobParameters());
}

public JobExecutionResource launch(JobBuilderFactory jobBuilders, StepBuilderFactory stepBuilders, JobTemplate jobTemplate, int cohortDefinitionId, int sourceId) {
JobParametersBuilder builder = new JobParametersBuilder();
builder.addString(JOB_NAME, String.format("Cleanup cohort samples of cohort definition %d.", cohortDefinitionId));
builder.addString(COHORT_DEFINITION_ID, String.valueOf(cohortDefinitionId));
builder.addString(SOURCE_ID, String.valueOf(sourceId));

log.info("Beginning cohort cleanup for cohort definition id {} and source ID {}", cohortDefinitionId, sourceId);
return launch(jobBuilders, stepBuilders, jobTemplate, builder.toJobParameters());
}

private JobExecutionResource launch(JobBuilderFactory jobBuilders, StepBuilderFactory stepBuilders, JobTemplate jobTemplate, JobParameters jobParameters) {
Step cleanupStep = stepBuilders.get("cohortSample.cleanupSamples")
.tasklet(this)
.build();

SimpleJobBuilder cleanupJobBuilder = jobBuilders.get("cleanupSamples")
.start(cleanupStep);

Job cleanupCohortJob = cleanupJobBuilder.build();

return jobTemplate.launch(cleanupCohortJob, jobParameters);
}
}
139 changes: 139 additions & 0 deletions src/main/java/org/ohdsi/webapi/cohortsample/CohortSample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package org.ohdsi.webapi.cohortsample;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
import org.ohdsi.webapi.model.CommonEntity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.List;

/**
* Cohort sample details.
*/
@Entity(name = "CohortSample")
@Table(name = "cohort_sample")
public class CohortSample extends CommonEntity<Integer> {
@Id
@GenericGenerator(
name = "cohort_sample_generator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "cohort_sample_sequence"),
@Parameter(name = "increment_size", value = "1")
}
)
@GeneratedValue(generator = "cohort_sample_generator")
private Integer id;

@Column
private String name;

@Column(name = "cohort_definition_id")
private int cohortDefinitionId;

@Column(name = "source_id")
private int sourceId;

@Column(name = "age_mode")
private String ageMode;

@Column(name = "age_min")
private Integer ageMin;

@Column(name = "age_max")
private Integer ageMax;

@Column(name = "gender_concept_ids")
private String genderConceptIds;

@Column
private int size;

@Transient
private List<SampleElement> elements;

public Integer getId() {
return this.id;
}

public void setId(Integer id) {
this.id = id;
}

public int getCohortDefinitionId() {
return cohortDefinitionId;
}

public void setCohortDefinitionId(int cohortDefinitionId) {
this.cohortDefinitionId = cohortDefinitionId;
}

public List<SampleElement> getElements() {
return elements;
}

public void setElements(List<SampleElement> elements) {
this.elements = elements;
}

public int getSize() {
return size;
}

public void setSize(int size) {
this.size = size;
}

public Integer getAgeMin() {
return ageMin;
}

public void setAgeMin(Integer ageMin) {
this.ageMin = ageMin;
}

public Integer getAgeMax() {
return ageMax;
}

public void setAgeMax(Integer ageMax) {
this.ageMax = ageMax;
}

public String getGenderConceptIds() {
return genderConceptIds;
}

public void setGenderConceptIds(String genderConceptIds) {
this.genderConceptIds = genderConceptIds;
}

public int getSourceId() {
return sourceId;
}

public void setSourceId(int sourceId) {
this.sourceId = sourceId;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public String getAgeMode() {
return ageMode;
}

public void setAgeMode(String ageMode) {
this.ageMode = ageMode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.ohdsi.webapi.cohortsample;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* Repository of samples. This does not fetch any sample elements.
*/
@Component
public interface CohortSampleRepository extends CrudRepository<CohortSample, Integer> {
@Query("SELECT c FROM CohortSample c LEFT JOIN FETCH c.createdBy WHERE c.id = :id")
CohortSample findById(@Param("id") int cohortSampleId);

@Query("SELECT c FROM CohortSample c LEFT JOIN FETCH c.createdBy WHERE c.cohortDefinitionId = :cohortDefinitionId AND c.sourceId = :sourceId")
List<CohortSample> findByCohortDefinitionIdAndSourceId(@Param("cohortDefinitionId") int cohortDefinitionId, @Param("sourceId") int sourceId);

@Query("SELECT count(c.id) FROM CohortSample c WHERE c.cohortDefinitionId = :cohortDefinitionId")
int countSamples(@Param("cohortDefinitionId") int cohortDefinitionId);

@Query("SELECT count(c.id) FROM CohortSample c WHERE c.cohortDefinitionId = :cohortDefinitionId AND c.sourceId = :sourceId")
int countSamples(@Param("cohortDefinitionId") int cohortDefinitionId, @Param("sourceId") int sourceId);
}
Loading