diff --git a/cypress/component/QueryForm.cy.tsx b/cypress/component/QueryForm.cy.tsx index 74a5f33c..17558928 100644 --- a/cypress/component/QueryForm.cy.tsx +++ b/cypress/component/QueryForm.cy.tsx @@ -37,7 +37,8 @@ const props = { sex: null, diagnosis: null, isControl: false, - minNumSessions: null, + minNumImagingSessions: null, + minNumPhenotypicSessions: null, setIsControl: () => {}, assessmentTool: null, imagingModality: null, @@ -60,7 +61,8 @@ describe('QueryForm', () => { sex={props.sex} diagnosis={props.diagnosis} isControl={props.isControl} - minNumSessions={props.minNumSessions} + minNumImagingSessions={props.minNumImagingSessions} + minNumPhenotypicSessions={props.minNumPhenotypicSessions} setIsControl={props.setIsControl} assessmentTool={props.assessmentTool} imagingModality={props.imagingModality} @@ -77,7 +79,10 @@ describe('QueryForm', () => { cy.get('[data-cy="Sex-categorical-field"]').should('be.visible'); cy.get('[data-cy="Diagnosis-categorical-field"]').should('be.visible'); cy.get('[data-cy="healthy-control-checkbox"]').should('be.visible'); - cy.get('[data-cy="Minimum number of sessions-continuous-field"]').should('be.visible'); + cy.get('[data-cy="Minimum number of imaging sessions-continuous-field"]').should('be.visible'); + cy.get('[data-cy="Minimum number of phenotypic sessions-continuous-field"]').should( + 'be.visible' + ); cy.get('[data-cy="Assessment tool-categorical-field"]').should('be.visible'); cy.get('[data-cy="Imaging modality-categorical-field"]').should('be.visible'); cy.get('[data-cy="submit-query-button"]').should('be.visible'); @@ -95,7 +100,8 @@ describe('QueryForm', () => { sex={props.sex} diagnosis={props.diagnosis} isControl={props.isControl} - minNumSessions={props.minNumSessions} + minNumImagingSessions={props.minNumImagingSessions} + minNumPhenotypicSessions={props.minNumPhenotypicSessions} setIsControl={props.setIsControl} assessmentTool={props.assessmentTool} imagingModality={props.imagingModality} @@ -125,7 +131,8 @@ describe('QueryForm', () => { sex={props.sex} diagnosis={props.diagnosis} isControl={props.isControl} - minNumSessions={props.minNumSessions} + minNumImagingSessions={props.minNumImagingSessions} + minNumPhenotypicSessions={props.minNumPhenotypicSessions} setIsControl={props.setIsControl} assessmentTool={props.assessmentTool} imagingModality={props.imagingModality} @@ -151,7 +158,8 @@ describe('QueryForm', () => { sex={props.sex} diagnosis={props.diagnosis} isControl={props.isControl} - minNumSessions={props.minNumSessions} + minNumImagingSessions={props.minNumImagingSessions} + minNumPhenotypicSessions={props.minNumPhenotypicSessions} setIsControl={props.setIsControl} assessmentTool={props.assessmentTool} imagingModality={props.imagingModality} diff --git a/cypress/e2e/APIRequests.cy.ts b/cypress/e2e/APIRequests.cy.ts index f6a3cab0..6ed84280 100644 --- a/cypress/e2e/APIRequests.cy.ts +++ b/cypress/e2e/APIRequests.cy.ts @@ -188,8 +188,15 @@ describe('Successful API query requests', () => { it('Intercepts the request sent to the API and asserts over the request url', () => { cy.get('[data-cy="Minimum age-continuous-field"]').type('10'); cy.get('[data-cy="Maximum age-continuous-field"]').type('30'); + cy.get('[data-cy="Minimum number of imaging sessions-continuous-field"]').type('2'); + cy.get('[data-cy="Minimum number of phenotypic sessions-continuous-field"]').type('3'); cy.get('[data-cy="submit-query-button"]').click(); - cy.wait('@call').its('request.url').should('contains', 'min_age=10&max_age=30'); + cy.wait('@call') + .its('request.url') + .should('contain', 'min_age=10') + .and('contain', 'max_age=30') + .and('contain', 'min_num_imaging_sessions=2') + .and('contain', 'min_num_phenotypic_sessions=3'); }); }); diff --git a/cypress/e2e/ResultsTSV.cy.ts b/cypress/e2e/ResultsTSV.cy.ts index e7da9ee2..9a2e951f 100644 --- a/cypress/e2e/ResultsTSV.cy.ts +++ b/cypress/e2e/ResultsTSV.cy.ts @@ -1,4 +1,4 @@ -import { mixedResponse } from '../fixtures/mocked-responses'; +import { mixedResponse, unprotectedResponse } from '../fixtures/mocked-responses'; describe('Results TSV', () => { it('Removes a newline character from a dataset name in the downloaded dataset-level results file', () => { @@ -37,10 +37,51 @@ describe('Results TSV', () => { const rows = fileContent.split('\n'); const datasetProtected = rows[1]; - const datasetNotProtected = rows[2]; + const datasetNotProtected = rows[3]; expect(datasetProtected.split('\t')[7]).to.equal('protected'); - expect(datasetNotProtected.split('\t')[7]).to.equal('/ds004116/sub-300100'); + expect(datasetNotProtected.split('\t')[8]).to.equal('/ds004116/sub-300101'); + }); + }); + it('Checks whether the rows in the participant.tsv file generated according to session_type', () => { + cy.intercept('query/?*', unprotectedResponse).as('call'); + cy.visit('/'); + cy.get('[data-cy="submit-query-button"]').click(); + cy.wait('@call'); + cy.get('[data-cy="select-all-checkbox"]').find('input').check(); + cy.get('[data-cy="dataset-level-download-results-button"]').click(); + cy.get('[data-cy="participant-level-download-results-button"]').click(); + cy.readFile('cypress/downloads/participant-level-results.tsv').then((fileContent) => { + const rows = fileContent.split('\n'); + + const phenotypicSession = rows[1]; + const imagingSession = rows[2]; + + expect(phenotypicSession.split('\t')[3]).to.equal( + 'http://neurobagel.org/vocab/PhenotypicSession' + ); + expect(phenotypicSession.split('\t')[4]).to.equal('10.4'); + expect(phenotypicSession.split('\t')[5]).to.equal( + 'http://purl.bioontology.org/ontology/SNOMEDCT/248152002' + ); + expect(phenotypicSession.split('\t')[6]).to.equal( + 'http://purl.bioontology.org/ontology/SNOMEDCT/370143000' + ); + expect(phenotypicSession.split('\t')[7]).to.equal( + 'https://www.cognitiveatlas.org/task/id/trm_4f2419c4a1646' + ); + expect(phenotypicSession.split('\t')[8]).to.equal(''); + expect(phenotypicSession.split('\t')[11]).to.equal(''); + + expect(imagingSession.split('\t')[3]).to.equal('http://neurobagel.org/vocab/ImagingSession'); + expect(imagingSession.split('\t')[4]).to.equal(''); + expect(imagingSession.split('\t')[5]).to.equal(''); + expect(imagingSession.split('\t')[6]).to.equal(''); + expect(imagingSession.split('\t')[7]).to.equal(''); + expect(imagingSession.split('\t')[8]).to.equal('/ds004116/sub-300101'); + expect(imagingSession.split('\t')[11]).to.equal( + 'http://purl.org/nidash/nidm#FlowWeighted, http://purl.org/nidash/nidm#T2Weighted' + ); }); }); }); diff --git a/cypress/fixtures/mocked-responses.ts b/cypress/fixtures/mocked-responses.ts index a9311703..9a49a8b9 100644 --- a/cypress/fixtures/mocked-responses.ts +++ b/cypress/fixtures/mocked-responses.ts @@ -29,24 +29,25 @@ const unprotectedDatasetSnippet = { { sub_id: 'sub-300100', session_id: 'ses-nb01', - num_sessions: '1', + num_matching_phenotypic_sessions: '1', + num_matching_imaging_sessions: '0', + session_type: 'http://neurobagel.org/vocab/PhenotypicSession', age: '10.4', sex: 'http://purl.bioontology.org/ontology/SNOMEDCT/248152002', - diagnosis: [null], + diagnosis: ['http://purl.bioontology.org/ontology/SNOMEDCT/370143000'], subject_group: null, - assessment: [null], - image_modal: [ - 'http://purl.org/nidash/nidm#FlowWeighted', - 'http://purl.org/nidash/nidm#T2Weighted', - ], - session_file_path: '/ds004116/sub-300100', + assessment: ['https://www.cognitiveatlas.org/task/id/trm_4f2419c4a1646'], + image_modal: [null], + session_file_path: null, }, { sub_id: 'sub-300101', session_id: 'ses-nb01', - num_sessions: '1', - age: '10.4', - sex: 'http://purl.bioontology.org/ontology/SNOMEDCT/248152002', + num_matching_phenotypic_sessions: '0', + num_matching_imaging_sessions: '1', + session_type: 'http://neurobagel.org/vocab/ImagingSession', + age: null, + sex: null, diagnosis: [null], subject_group: null, assessment: [null], @@ -80,6 +81,12 @@ export const protectedResponse2 = { nodes_response_status: 'success', }; +export const unprotectedResponse = { + errors: [], + responses: [unprotectedDatasetSnippet], + nodes_response_status: 'success', +}; + // Protected Response with a dataset name containing a newline // character and a mix of protected and unprotected results export const mixedResponse = { diff --git a/src/App.tsx b/src/App.tsx index 7cda46a8..2e96bdcd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,7 +36,8 @@ function App() { const [sex, setSex] = useState(null); const [diagnosis, setDiagnosis] = useState(null); const [isControl, setIsControl] = useState(false); - const [minNumSessions, setMinNumSessions] = useState(null); + const [minNumImagingSessions, setMinNumSessions] = useState(null); + const [minNumPhenotypicSessions, setMinNumPhenotypicSessions] = useState(null); const [assessmentTool, setAssessmentTool] = useState(null); const [imagingModality, setImagingModality] = useState(null); const [searchParams, setSearchParams] = useSearchParams(); @@ -190,9 +191,12 @@ function App() { case 'Maximum age': setMaxAge(value); break; - case 'Minimum number of sessions': + case 'Minimum number of imaging sessions': setMinNumSessions(value); break; + case 'Minimum number of phenotypic sessions': + setMinNumPhenotypicSessions(value); + break; default: break; } @@ -236,7 +240,14 @@ function App() { setQueryParam('sex', sex, queryParams); setQueryParam('diagnosis', isControl ? null : diagnosis, queryParams); queryParams.set('is_control', isControl ? 'true' : ''); - queryParams.set('min_num_sessions', minNumSessions ? minNumSessions.toString() : ''); + queryParams.set( + 'min_num_imaging_sessions', + minNumImagingSessions ? minNumImagingSessions.toString() : '' + ); + queryParams.set( + 'min_num_phenotypic_sessions', + minNumPhenotypicSessions ? minNumPhenotypicSessions.toString() : '' + ); setQueryParam('assessment', assessmentTool, queryParams); setQueryParam('image_modal', imagingModality, queryParams); @@ -320,7 +331,7 @@ function App() { )} -
+
Neurobagel Query - + Define and find cohorts at the subject level
- Documentation + Documentation diff --git a/src/components/QueryForm.tsx b/src/components/QueryForm.tsx index bfa880b1..8867c764 100644 --- a/src/components/QueryForm.tsx +++ b/src/components/QueryForm.tsx @@ -21,7 +21,8 @@ function QueryForm({ sex, diagnosis, isControl, - minNumSessions, + minNumImagingSessions, + minNumPhenotypicSessions, setIsControl, assessmentTool, imagingModality, @@ -40,7 +41,8 @@ function QueryForm({ diagnosis: FieldInput; isControl: boolean; setIsControl: (value: boolean) => void; - minNumSessions: number | null; + minNumImagingSessions: number | null; + minNumPhenotypicSessions: number | null; assessmentTool: FieldInput; imagingModality: FieldInput; updateCategoricalQueryParams: (label: string, value: FieldInput) => void; @@ -64,21 +66,23 @@ function QueryForm({ const minAgeHelperText: string = validateContinuousValue(minAge); const maxAgeHelperText: string = validateContinuousValue(maxAge); - const minNumSessionsHelperText: string = validateContinuousValue(minNumSessions); + const minNumImagingSessionsHelperText: string = validateContinuousValue(minNumImagingSessions); + const minNumPhenotypicSessionsHelperText: string = + validateContinuousValue(minNumPhenotypicSessions); const minAgeExceedsMaxAge: boolean = minAge && maxAge ? minAge > maxAge : false; const disableSubmit: boolean = minAgeExceedsMaxAge || minAgeHelperText !== '' || maxAgeHelperText !== '' || - minNumSessionsHelperText !== ''; + minNumImagingSessionsHelperText !== ''; return (
{isFederationAPI && ( @@ -153,12 +157,19 @@ function QueryForm({
+ +
+
({ label: a.Label, id: a.TermURL }))} @@ -166,7 +177,7 @@ function QueryForm({ inputValue={assessmentTool} />
-
+
({ @@ -177,7 +188,7 @@ function QueryForm({ inputValue={imagingModality} />
-
+
-
+
{response.responses.map((item) => (