Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ENH] Impemented validation for output JSON #602

Merged
merged 21 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ module.exports = defineConfig({
findFiles,
isFileExist
});
}
},
experimentalStudio: true
},

component: {
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/app/simple-e2etest.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe("End to end test using a simple UI path through the app", () => {
expect(fileContent.iq.Annotations.IsAbout.Label).to.eq("Assessment tool");
expect(fileContent.iq.Annotations.IsAbout.TermURL).to.eq("nb:Assessment");
expect(fileContent.iq.Annotations.IsPartOf.Label).to.eq("Wechsler Abbreviated Scale of Intelligence");
expect(fileContent.iq.Annotations.IsPartOf.TermURL).to.eq("cogAtlas:trm_4b94affc43245");
expect(fileContent.iq.Annotations.IsPartOf.TermURL).to.eq("cogatlas:trm_4b94affc43245");
});
});

Expand Down
114 changes: 114 additions & 0 deletions cypress/e2e/app/validate-output-example-synthetic-e2etest.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
describe("to annotate an assessment ", () => {
it("sets up some stuff", () => {
// Load some data
cy.visit('/');
cy.get('[data-cy="data-table-selector"]').get('input').selectFile('cypress/fixtures/examples/good/example_synthetic.tsv', { force: true });

/* ==== Generated with Cypress Studio ==== */
cy.get('[data-cy="data-dictionary-selector"] > .row > form > .file-selector-button').click();
cy.get('[data-cy="data-dictionary-selector"] > .row > form > .file-selector-button > input').selectFile('cypress/fixtures/examples/good/example_synthetic.json', { force: true });
/* ==== End Cypress Studio ==== */
cy.get("[data-cy='button-nextpage']").click();

// Categorize some columns
const desiredColumnMappings = [
{
"column": "participant_id",
"category": "Subject ID"
},
{
"column": "pheno_age",
"category": "Age"

},
{
"column": "pheno_sex",
"category": "Sex"
},
{
"column": "pheno_group",
"category": "Diagnosis"
},
{
"column": "tool1_item1",
"category": "Assessment Tool"
},
{
"column": "tool1_item2",
"category": "Assessment Tool"
},
{
"column": "tool2_item1",
"category": "Assessment Tool"
}
];
desiredColumnMappings.forEach(desiredColumnMapping => {
cy.get("[data-cy='categorization-table']").contains(desiredColumnMapping.category).click();
cy.get("[data-cy='column-linking-table']").contains(desiredColumnMapping.column).click();
});
// Create two tools
cy.get("[data-cy='toolgroup-select']").type("Montreal Cognitive Assessment{enter}");
cy.get("[data-cy='toolgroup-select']").type("Unified Parkinson's Disease Rating Scale{enter}");
// Map columns to tools
const desiredColumnToolMappings = [
{
"column": "tool1_item1",
"tool": "Montreal Cognitive Assessment"
},
{
"column": "tool1_item2",
"tool": "Montreal Cognitive Assessment"
},
{
"column": "tool2_item1",
"tool": "Unified Parkinson's Disease Rating Scale"
}
];
desiredColumnToolMappings.forEach(desiredColumnToolMapping => {
cy.get("[data-cy='assessment-tool-table']").contains(desiredColumnToolMapping.tool).click();
cy.get("[data-cy='assessment-column-table']").contains(desiredColumnToolMapping.column).click();
});

cy.get("[data-cy='button-nextpage']").click();

/* ==== Generated with Cypress Studio ==== */
cy.get('#vs2__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs2__option-1').click();
cy.get('[data-cy="dataTable-pheno_age"] > tbody > :nth-child(3) > [aria-colindex="3"] > [data-cy="missingValueButton_2"]').click();
cy.get("[data-cy='annotation-category-tabs'] ul").contains("li", "Sex").click();
cy.get('#vs3__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs3__option-1').click();
cy.get('#vs4__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs4__option-0').click();
cy.get('[data-cy="annot-categorical-Sex"] > .card > .card-body > [data-cy="categoricalTable"] > tbody > :nth-child(3) > [aria-colindex="5"] > [data-cy="missingValueButton_2"]').click();
cy.get("[data-cy='annotation-category-tabs'] ul").contains("li", "Diagnosis").click();
cy.get('#vs6__combobox > .vs__selected-options > .vs__search').click();
cy.get('thead > tr > [aria-colindex="6"]').click();
cy.get('[data-cy="isControlButton_0"]').click();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').click();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').clear('a');
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').type('attention');
cy.get('#vs7__combobox').click();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').clear();
cy.get('#vs7__combobox > .vs__selected-options > .vs__search').type('Attention deficit hyperactivity disorder');
cy.get('#vs7__option-2').click();
cy.get('[aria-colindex="5"] > [data-cy="missingValueButton_2"]').click();
cy.get("[data-cy='annotation-category-tabs'] ul").contains("li", "Assessment Tool").click();
cy.get('[data-cy="tool-annotation-for-cogatlas:tsk_4a57abb949ece"] > tbody > :nth-child(4) > [aria-colindex="3"] > [data-cy="missingValueButton_3"]').click();
cy.get('[data-cy="Assessment Tool"]').contains("Montreal Cognitive Assessment").click();
cy.get('[data-cy="tool-annotation-for-cogatlas:trm_57964b8a66aed"] > tbody > :nth-child(7) > [aria-colindex="3"] > [data-cy="missingValueButton_6"]').click();
cy.get('[data-cy="tool-annotation-for-cogatlas:trm_57964b8a66aed"] > tbody > :nth-child(4) > [aria-colindex="3"] > [data-cy="missingValueButton_3"]').click();
cy.get('[data-cy="button-nextpage"]').click();
cy.get('[data-cy="download-button"]').click();
/* ==== End Cypress Studio ==== */

cy.task("downloads", "cypress/downloads").then(folderStateAfter => {
cy.readFile('cypress/downloads/' + folderStateAfter[folderStateAfter.length - 1]).then((outputContent) => {

const expectedOutput = require('../../fixtures/examples/good/example_synthetic_expected_output.json');
expect(outputContent).to.deep.equal(expectedOutput);
});
});
});

});
24 changes: 14 additions & 10 deletions cypress/e2e/page/download-pagetests.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,26 @@ describe("tests on download page ui via programmatic state loading and store int
// 2. Check contents of downloads folder
cy.task("downloads", "cypress/downloads").then(folderStateAfter => {

// A. Check number of files in downloads folder has increased by one
// Check that we actually downloaded a file
expect(folderStateAfter.length).to.be.eq(folderStateBefore.length + 1);

// B. Check that the last file retrieved by fs.readdirSync contains the data dictionary input file prefix
// as stored in dataDictionary.filename

cy.getVuexStoreValue("dataDictionary").then(dataDictionary => {

// Using the datadictionary name to locate the file downloaded by test for further verification
// may not be a foolproof solution since if another test ends up using the same data dictionary
// as input this approach may grab the wrong file and lead to test(s) failing
const dataDictionaryFilenameNoExt = dataDictionary.filename.split(".").slice(0, -1).join(".");

expect(folderStateAfter[folderStateAfter.length - 1]).to.contain(dataDictionaryFilenameNoExt);
expect(folderStateAfter.some(filename => filename.includes(dataDictionaryFilenameNoExt))).to.be.true;

// Because we only have access to dataDictionary within the scope of this promise,
// we need to run our next assertion in here
const targetFile = folderStateAfter.filter(filename => filename.includes(dataDictionaryFilenameNoExt))[0];
cy.readFile('cypress/downloads/' + targetFile).then((fileContent) => {
expect(fileContent.participant_id.Annotations).to.have.property("Identifies");
expect(fileContent.participant_id.Annotations.Identifies).to.eq("participant");
});
});
// C. Check if the last file retrieved contains the Identifies property and its value under the participant_id key
cy.readFile('cypress/downloads/' + folderStateAfter[folderStateAfter.length - 1]).then((fileContent) => {
expect(fileContent.participant_id.Annotations).to.have.property("Identifies");
expect(fileContent.participant_id.Annotations.Identifies).to.eq("participant");
});
});
});
});
Expand Down
58 changes: 0 additions & 58 deletions cypress/e2e/page/my_happy_annotation-page_tests.cy.js

This file was deleted.

33 changes: 33 additions & 0 deletions cypress/e2e/validate-output.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const Ajv = require('ajv');

const outputJSONFilePath = 'cypress/fixtures/examples/good/ds003653_participant_annotated_1698934398962.json';

const schemaPath = 'cypress/fixtures/schema/neurobagel_data_dictionary.schema.json';


describe('Validate Output', () => {
it('Validates example output against the schema', () => {

const ajv = new Ajv();

cy.readFile(schemaPath)
.then((schema) => {

const validate = ajv.compile(schema);

cy.readFile(outputJSONFilePath)
.then((outputJSONData) => {

const isValid = validate(outputJSONData);

if (isValid) {
cy.log('JSON is valid according to the schema');
} else {
cy.log('JSON is not valid according to the schema');
cy.log(validate.errors);
}
cy.expect(isValid).to.be.true;
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
{
"participant_id": {
"Description": "A participant ID",
"Annotations": {
"IsAbout": {
"Label": "Subject Unique Identifier",
"TermURL": "nb:ParticipantID"
},
"Identifies": "participant"
}
},
"age": {
"Annotations": {
"IsAbout": {
"Label": "Age",
"TermURL": "nb:Age"
},
"Transformation": {
"Label": "integer value",
"TermURL": "nb:FromInt"
},
"MissingValues": []
},
"Description": "age of the participant",
"Units": "years"
},
"sex": {
"Annotations": {
"IsAbout": {
"Label": "Sex",
"TermURL": "nb:Sex"
},
"Levels": {
"M": {
"Label": "male",
"TermURL": "bids:male"
},
"F": {
"Label": "female",
"TermURL": "bids:female"
}
},
"MissingValues": []
},
"Description": "sex of the participant as reported by the participant",
"Levels": {
"M": "male",
"F": "female"
}
},
"group": {
"Annotations": {
"IsAbout": {
"Label": "Diagnosis",
"TermURL": "nb:Diagnosis"
},
"Levels": {
"UD": {
"Label": "Acute depression",
"TermURL": "snomed:712823008"
},
"HC": {
"Label": "Healthy Control",
"TermURL": "ncit:C94342"
},
"HCconvertedMDD": {
"Label": "Borderline personality disorder",
"TermURL": "snomed:20010003"
}
},
"MissingValues": []
},
"Description": "diagnostic status determined by the study clinician at baseline",
"Levels": {
"UD": "individuals with unipolar depression including those diagnosed with MDD and PDD",
"HC": "healthy controls",
"HCconvertedMDD": "individual that presented as HC at baseline, but converted to MDD during the course of the study"
}
},
"group_dx": {
"Description": "specific diagnosis determined by the study clinician at baseline",
"missingValues": [],
"Levels": {
"MDD": "major depressive disorder",
"PDD": "persistent depressive disorder",
"HC": "healthy controls",
"HCconvertedMDD": "individual that presented as HC at baseline, but converted to MDD during the course of the study"
}
},
"number_comorbid_dx": {
"Description": "a number of diagnoses comorbid with UD (e.g., GAD, PTSD)",
"missingValues": [],
"Units": "number"
},
"medload": {
"Description": "reflects the number of dosage of psychotropic medications taken by participants. Higher numbers correspond to more medications and/or higher medication dosage ",
"missingValues": [],
"Units": "arbitrary units"
},
"iq": {
"Annotations": {
"IsAbout": {
"TermURL": "nb:Assessment",
"Label": "Assessment tool"
},
"IsPartOf": {
"TermURL": "cogAtlas:trm_4b94affc43245",
"Label": "Wechsler Abbreviated Scale of Intelligence"
},
"MissingValues": []
},
"Description": "IQ derived based on the NART assessment.",
"Units": "arbitrary units"
},
"session": {
"Description": "scanning session",
"missingValues": [],
"Levels": {
"1": "baseline",
"2": "6 months follow-up scan"
}
}
}
Loading
Loading