Skip to content

Commit

Permalink
Replace all data in a research study
Browse files Browse the repository at this point in the history
  • Loading branch information
dmpotter44 committed Feb 6, 2024
1 parent 2872f1e commit 14bd619
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 295 deletions.
2 changes: 1 addition & 1 deletion spec/clinicaltrialsgov.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ describe('ClinicalTrialsGovService', () => {
});

describe('#updateResearchStudy', () => {
it('forwards to updateResearchStudyWithClinicalStudy', () => {
it('forwards to createResearchStudyFromClinicalStudy', () => {
const service = createMemoryCTGovService();
const testResearchStudy = createResearchStudy('test');
const testClinicalStudy = createClinicalStudy();
Expand Down
2 changes: 1 addition & 1 deletion spec/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('index', () => {
expect(ctms.ResearchStudy).toBeDefined();
expect(ctms.ClinicalTrialMatchingService).toBeDefined();
expect(ctms.ClinicalTrialsGovAPI).toBeDefined();
expect(ctms.updateResearchStudyWithClinicalStudy).toBeDefined();
expect(ctms.createResearchStudyFromClinicalStudy).toBeDefined();
expect(ctms.CodeMapper).toBeDefined();
expect(ctms.CodeSystemEnum).toBeDefined();
expect(ctms.ClientError).toBeDefined();
Expand Down
118 changes: 31 additions & 87 deletions spec/study-trial-converter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Address, FhirResource, Location, ResearchStudy, PlanDefinition } from 'fhir/r4';
import { getContainedResource, ResearchStudy as ResearchStudyObj } from '../src/research-study';
import { updateResearchStudyWithClinicalStudy } from '../src/study-fhir-converter';
import { createResearchStudyFromClinicalStudy } from '../src/study-fhir-converter';
import {
DateType,
DesignAllocation,
Expand Down Expand Up @@ -28,7 +28,7 @@ describe('filling out a partial trial', () => {
let updatedTrial: ResearchStudy;

beforeAll(async function () {
updatedTrial = updateResearchStudyWithClinicalStudy(study, sampleStudy as Study);
updatedTrial = createResearchStudyFromClinicalStudy(sampleStudy as Study, study);
});

it('fills in inclusion criteria', () => {
Expand Down Expand Up @@ -66,11 +66,11 @@ describe('filling out a partial trial', () => {
}
});

it('does not overwrite existing categories', () => {
it('overwrites existing categories', () => {
const researchStudy = new ResearchStudyObj('id');
researchStudy.category = [{ text: 'Study Type: Do Not Replace' }];

updateResearchStudyWithClinicalStudy(researchStudy, {
createResearchStudyFromClinicalStudy({
protocolSection: {
designModule: {
studyType: StudyType.INTERVENTIONAL,
Expand All @@ -86,7 +86,7 @@ describe('filling out a partial trial', () => {
}
}
}
});
}, researchStudy);

expect(researchStudy.category).toBeDefined();
if (researchStudy.category) {
Expand All @@ -95,7 +95,7 @@ describe('filling out a partial trial', () => {
expect(categories).toHaveSize(7);
expect(categories).toEqual(
jasmine.arrayContaining([
'Study Type: Do Not Replace',
'Study Type: Interventional',
'Intervention Model: Parallel',
'Primary Purpose: Treatment',
'Masking: None',
Expand All @@ -107,25 +107,6 @@ describe('filling out a partial trial', () => {
}
});

it('will retain old categories if not part of standard study design', () => {
const researchStudy = new ResearchStudyObj('id');
// Empty category but there is an object there for the sake of this test.
researchStudy.category = [{}];

updateResearchStudyWithClinicalStudy(researchStudy, {
protocolSection: {
designModule: {
studyType: StudyType.INTERVENTIONAL
}
}
});

expect(researchStudy.category).toBeDefined();
if (researchStudy.category) {
expect(researchStudy.category).toHaveSize(2);
}
});

it('fills in arms', () => {
expect(updatedTrial.arm).toBeDefined();
if (updatedTrial.arm) {
Expand Down Expand Up @@ -191,8 +172,7 @@ describe('filling out a partial trial', () => {
});

it('fills in interventions even without arms', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
armsInterventionsModule: {
interventions: [
Expand Down Expand Up @@ -231,8 +211,7 @@ describe('filling out a partial trial', () => {
});

it('fills in interventions with description and subtitle', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
armsInterventionsModule: {
interventions: [
Expand Down Expand Up @@ -273,8 +252,7 @@ describe('filling out a partial trial', () => {
});

it('falls back on the intervention model description if no intervention model is given', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
designModule: {
designInfo: {
Expand All @@ -287,8 +265,7 @@ describe('filling out a partial trial', () => {
});

it('fills in period', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
statusModule: {
startDateStruct: {
Expand All @@ -314,8 +291,7 @@ describe('filling out a partial trial', () => {
});

it('fills in start of period even without end', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
statusModule: {
startDateStruct: {
Expand All @@ -336,8 +312,7 @@ describe('filling out a partial trial', () => {
});

it('fills in end of period even without start', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
statusModule: {
completionDateStruct: {
Expand All @@ -358,8 +333,7 @@ describe('filling out a partial trial', () => {
});

it('does not fill in period if not a real date', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
statusModule: {
startDateStruct: {
Expand All @@ -382,8 +356,7 @@ describe('filling out a partial trial', () => {
});

it('fills out the status', () => {
const actual = updateResearchStudyWithClinicalStudy(
{ resourceType: 'ResearchStudy', status: 'active' },
const actual = createResearchStudyFromClinicalStudy(
{
protocolSection: {
statusModule: {
Expand All @@ -395,9 +368,8 @@ describe('filling out a partial trial', () => {
expect(actual.status).toEqual('completed');
});

it('leaves status alone if unavailable', () => {
const actual = updateResearchStudyWithClinicalStudy(
{ resourceType: 'ResearchStudy', status: 'active' },
it('defaults to active if status is unavailable', () => {
const actual = createResearchStudyFromClinicalStudy(
{
// Lie about types
protocolSection: {
Expand All @@ -412,8 +384,7 @@ describe('filling out a partial trial', () => {
});

it('fills out conditions', () => {
const actual = updateResearchStudyWithClinicalStudy(
{ resourceType: 'ResearchStudy', status: 'active' },
const actual = createResearchStudyFromClinicalStudy(
{
protocolSection: {
conditionsModule: {
Expand All @@ -431,8 +402,7 @@ describe('filling out a partial trial', () => {
});

it('fills in contact', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
contactsLocationsModule: {
centralContacts: [
Expand Down Expand Up @@ -476,8 +446,7 @@ describe('filling out a partial trial', () => {
});

it('fills in contacts even with missing information', () => {
const researchStudy = new ResearchStudyObj('id');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
contactsLocationsModule: {
centralContacts: [
Expand Down Expand Up @@ -512,10 +481,10 @@ describe('filling out a partial trial', () => {
}
});

it('does not overwrite site data', () => {
it('overwrites site data', () => {
const researchStudy = new ResearchStudyObj('id');
const location = researchStudy.addSite('Example');
const result = updateResearchStudyWithClinicalStudy(researchStudy, {
researchStudy.addSite('Example');
const result = createResearchStudyFromClinicalStudy({
protocolSection: {
contactsLocationsModule: {
locations: [
Expand All @@ -527,38 +496,14 @@ describe('filling out a partial trial', () => {
}
});
expect(result.site).toBeDefined();
const sites = result.site;
if (sites) {
expect(sites.length).toEqual(1);
if (sites[0]) {
expect(sites[0].reference).toEqual('#' + location.id);
if (location.id) {
const actualLocation = getContainedResource(result, location.id);
expect(actualLocation).not.toBeNull();
if (actualLocation) {
expect(actualLocation.resourceType).toEqual('Location');
expect((actualLocation as Location).name).toEqual('Example');
}
} else {
fail('location.id not defined');
}
} else {
fail('sites[0] undefined');
}
}
// FIXME: Verify that the sites are from thte test data
});

it('does not alter a filled out trial', () => {
it('replaces the data in a filled out study', () => {
// Clone the trial in the dumbest but also most sensible way
const exampleStudy: ResearchStudy = JSON.parse(JSON.stringify(trialFilled));
updateResearchStudyWithClinicalStudy(exampleStudy, sampleStudy as Study);
// Currently active gets overwritten intentioanlly, so set the example
// back to its original value even if it changed
// (Note that the "as" *should* verify that the underlying JSON value is
// in fact valid at compile time. I think.)
exampleStudy.status = trialFilled.status as ResearchStudy['status'];
// Nothing should have changed
expect(exampleStudy).toEqual(trialFilled as ResearchStudy);
createResearchStudyFromClinicalStudy(sampleStudy as Study, exampleStudy);
// TODO: Figure out the data that should have changed
});

function expectTelecom(location: Location, type: 'phone' | 'email', expectedValue: string | null) {
Expand Down Expand Up @@ -622,8 +567,7 @@ describe('filling out a partial trial', () => {
}

it('fills out sites as expected', () => {
const result = updateResearchStudyWithClinicalStudy(
{ resourceType: 'ResearchStudy', status: 'active' },
const result = createResearchStudyFromClinicalStudy(
{
protocolSection: {
contactsLocationsModule: {
Expand Down Expand Up @@ -702,7 +646,7 @@ describe('filling out a partial trial', () => {
});

function expectEmptyResearchStudy(researchStudy: ResearchStudy): void {
// Technically this is just checking fields updateResearchStudyWithClinicalStudy may change
// Technically this is just checking fields createResearchStudyFromClinicalStudy may change
expect(researchStudy.contained).withContext('contained').not.toBeDefined();
expect(researchStudy.enrollment).withContext('enrollment').not.toBeDefined();
expect(researchStudy.description).withContext('description').not.toBeDefined();
Expand All @@ -716,11 +660,11 @@ describe('filling out a partial trial', () => {
it("handles JSON with missing data (doesn't crash)", () => {
let researchStudy: ResearchStudy;
// According to the schema, literally everything is optional, so an empty object should "work"
researchStudy = updateResearchStudyWithClinicalStudy(new ResearchStudyObj('id'), {});
researchStudy = createResearchStudyFromClinicalStudy({});
// Expect nothing to have changed
expectEmptyResearchStudy(researchStudy);
// Some partial JSON
researchStudy = updateResearchStudyWithClinicalStudy(new ResearchStudyObj('id'), {
researchStudy = createResearchStudyFromClinicalStudy({
protocolSection: {
eligibilityModule: {
genderBased: false,
Expand All @@ -731,7 +675,7 @@ describe('filling out a partial trial', () => {
});
expectEmptyResearchStudy(researchStudy);
// Some bad JSON that shouldn't cause issues
researchStudy = updateResearchStudyWithClinicalStudy(new ResearchStudyObj('id'), {
researchStudy = createResearchStudyFromClinicalStudy({
protocolSection: {
contactsLocationsModule: {
locations: [undefined as unknown as StudyLocation]
Expand Down
4 changes: 2 additions & 2 deletions src/clinicaltrialsgov.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { debuglog } from 'util';
import { ResearchStudy } from 'fhir/r4';
import { SearchBundleEntry as SearchSetEntry } from './searchset';
import { ClinicalTrialsGovAPI, Study } from './clinicaltrialsgov-api';
import { updateResearchStudyWithClinicalStudy } from './study-fhir-converter';
import { createResearchStudyFromClinicalStudy } from './study-fhir-converter';
import * as sqlite from 'sqlite';
import * as sqlite3 from 'sqlite3';

Expand Down Expand Up @@ -690,7 +690,7 @@ export class ClinicalTrialsGovService {
* @param clinicalStudy the clinical study to update it with
*/
updateResearchStudy(researchStudy: ResearchStudy, clinicalStudy: Study): void {
updateResearchStudyWithClinicalStudy(researchStudy, clinicalStudy);
createResearchStudyFromClinicalStudy(clinicalStudy, researchStudy);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { CodeMapper, CodeSystemEnum } from './codeMapper';
export * from './mcodeextractor';
export { Study } from './ctg-api';
export { ClinicalTrialsGovAPI } from './clinicaltrialsgov-api';
export { updateResearchStudyWithClinicalStudy } from './study-fhir-converter';
export { createResearchStudyFromClinicalStudy } from './study-fhir-converter';

// The export { v } from "mod" forms do not appear to work for types yet, so
// they have to be imported and then exported...
Expand Down
Loading

0 comments on commit 14bd619

Please sign in to comment.