Skip to content

Commit a671a00

Browse files
author
Matthew Gramigna
committed
CSV Validation
* Include schemas and tools for validating CSVs * Add BaseCSVExtractor class that all CSV extractors extend * Update excel sheet to indicate all fields used * Update tests to use proper calls
1 parent 7243379 commit a671a00

20 files changed

+235
-72
lines changed

docs/CSV_Templates.xlsx

59 Bytes
Binary file not shown.

package-lock.json

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"ajv": "^6.12.6",
2323
"antlr4": "4.8.0",
2424
"commander": "^6.2.0",
25+
"csv-file-validator": "^1.10.1",
2526
"csv-parse": "^4.8.8",
2627
"fhir-crud-client": "^1.2.2",
2728
"fhirpath": "2.1.5",

src/client/BaseClient.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,30 @@ class BaseClient {
2525
}
2626

2727
// Given an extractor configuration, initialize all the necessary extractors
28-
initializeExtractors(extractorConfig, commonExtractorArgs) {
29-
extractorConfig.forEach((curExtractorConfig) => {
28+
async initializeExtractors(extractorConfig, commonExtractorArgs) {
29+
const extractorInits = extractorConfig.map(async (curExtractorConfig) => {
3030
const { label, type, constructorArgs } = curExtractorConfig;
3131
logger.debug(`Initializing ${label} extractor with type ${type}`);
3232
const ExtractorClass = this.extractorClasses[type];
33+
3334
try {
3435
const newExtractor = new ExtractorClass({ ...commonExtractorArgs, ...constructorArgs });
35-
this.extractors.push(newExtractor);
36+
37+
if (newExtractor.validate) {
38+
await newExtractor.validate();
39+
}
40+
41+
return newExtractor;
3642
} catch (e) {
3743
throw new Error(`Unable to initialize ${label} extractor with type ${type}`);
3844
}
3945
});
46+
47+
await Promise.all(extractorInits).then((extractors) => {
48+
this.extractors.push(...extractors);
49+
});
50+
51+
logger.info('Validation succeeded');
4052
}
4153

4254
// NOTE: Async because in other clients that extend this, we need async helper functions (ex. auth)

src/extractors/BaseCSVExtractor.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const path = require('path');
2+
const { Extractor } = require('./Extractor');
3+
const { CSVModule } = require('../modules');
4+
const { validateCSV } = require('../helpers/csvValidator');
5+
const logger = require('../helpers/logger');
6+
7+
class BaseCSVExtractor extends Extractor {
8+
constructor({ filePath, csvSchema }) {
9+
super();
10+
this.csvSchema = csvSchema;
11+
this.filePath = path.resolve(filePath);
12+
this.csvModule = new CSVModule(this.filePath);
13+
}
14+
15+
async validate() {
16+
if (this.csvSchema) {
17+
logger.info(`Validating CSV file for ${this.filePath}`);
18+
await validateCSV(this.filePath, this.csvSchema);
19+
} else {
20+
logger.warn(`No CSV schema provided for ${this.filePath}`);
21+
}
22+
}
23+
}
24+
25+
module.exports = {
26+
BaseCSVExtractor,
27+
};

src/extractors/CSVAdverseEventExtractor.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
const path = require('path');
2-
const { CSVModule } = require('../modules');
1+
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
32
const { generateMcodeResources } = require('../templates');
4-
const { Extractor } = require('./Extractor');
53
const logger = require('../helpers/logger');
64
const { formatDateTime } = require('../helpers/dateUtils');
75

@@ -53,15 +51,14 @@ function formatData(adverseEventData) {
5351
});
5452
}
5553

56-
class CSVAdverseEventExtractor extends Extractor {
54+
class CSVAdverseEventExtractor extends BaseCSVExtractor {
5755
constructor({ filePath }) {
58-
super();
59-
this.CSVModule = new CSVModule(path.resolve(filePath));
56+
super({ filePath });
6057
}
6158

6259
async getAdverseEventData(mrn) {
6360
logger.debug('Getting Adverse Event Data');
64-
return this.CSVModule.get('mrn', mrn);
61+
return this.csvModule.get('mrn', mrn);
6562
}
6663

6764
async get({ mrn }) {

src/extractors/CSVCancerDiseaseStatusExtractor.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
const path = require('path');
2-
const { CSVModule } = require('../modules');
1+
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
32
const { formatDateTime } = require('../helpers/dateUtils');
43
const { getDiseaseStatusDisplay, getDiseaseStatusEvidenceDisplay } = require('../helpers/diseaseStatusUtils');
54
const { generateMcodeResources } = require('../templates');
65
const { getEmptyBundle } = require('../helpers/fhirUtils');
76
const logger = require('../helpers/logger');
7+
const { CSVCancerDiseaseStatusSchema } = require('../helpers/schemas/csv');
88

9-
class CSVCancerDiseaseStatusExtractor {
9+
class CSVCancerDiseaseStatusExtractor extends BaseCSVExtractor {
1010
constructor({ filePath, implementation }) {
11-
this.csvModule = new CSVModule(path.resolve(filePath));
11+
super({ filePath, csvSchema: CSVCancerDiseaseStatusSchema });
1212
this.implementation = implementation;
1313
}
1414

src/extractors/CSVCancerRelatedMedicationExtractor.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
const path = require('path');
2-
const { CSVModule } = require('../modules');
1+
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
32
const { generateMcodeResources } = require('../templates');
4-
const { Extractor } = require('./Extractor');
53
const logger = require('../helpers/logger');
64
const { formatDateTime } = require('../helpers/dateUtils');
75

@@ -35,10 +33,9 @@ function formatData(medicationData) {
3533
});
3634
}
3735

38-
class CSVCancerRelatedMedicationExtractor extends Extractor {
36+
class CSVCancerRelatedMedicationExtractor extends BaseCSVExtractor {
3937
constructor({ filePath }) {
40-
super();
41-
this.csvModule = new CSVModule(path.resolve(filePath));
38+
super({ filePath });
4239
}
4340

4441
async getMedicationData(mrn) {

src/extractors/CSVClinicalTrialInformationExtractor.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
const path = require('path');
2-
const { Extractor } = require('./Extractor');
3-
const { CSVModule } = require('../modules');
1+
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
42
const { firstEntryInBundle, getBundleResourcesByType } = require('../helpers/fhirUtils');
53
const { generateMcodeResources } = require('../templates');
64
const logger = require('../helpers/logger');
5+
const { CSVClinicalTrialInformationSchema } = require('../helpers/schemas/csv');
76

87
function getPatientId(context) {
98
const patientInContext = getBundleResourcesByType(context, 'Patient', {}, true);
@@ -16,10 +15,9 @@ function getPatientId(context) {
1615
return undefined;
1716
}
1817

19-
class CSVClinicalTrialInformationExtractor extends Extractor {
18+
class CSVClinicalTrialInformationExtractor extends BaseCSVExtractor {
2019
constructor({ filePath, clinicalSiteID }) {
21-
super();
22-
this.csvModule = new CSVModule(path.resolve(filePath));
20+
super({ filePath, csvSchema: CSVClinicalTrialInformationSchema });
2321
if (!clinicalSiteID) logger.warn(`${this.constructor.name} expects a value for clinicalSiteID but got ${clinicalSiteID}`);
2422
this.clinicalSiteID = clinicalSiteID;
2523
}

src/extractors/CSVConditionExtractor.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
const path = require('path');
2-
const { CSVModule } = require('../modules');
1+
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
32
const { generateMcodeResources } = require('../templates');
4-
const { Extractor } = require('./Extractor');
53
const logger = require('../helpers/logger');
64
const { formatDateTime } = require('../helpers/dateUtils');
5+
const { CSVConditionSchema } = require('../helpers/schemas/csv');
76

87
// Formats data to be passed into template-friendly format
98
function formatData(conditionData) {
@@ -37,10 +36,9 @@ function formatData(conditionData) {
3736
});
3837
}
3938

40-
class CSVConditionExtractor extends Extractor {
39+
class CSVConditionExtractor extends BaseCSVExtractor {
4140
constructor({ filePath }) {
42-
super();
43-
this.csvModule = new CSVModule(path.resolve(filePath));
41+
super({ filePath, csvSchema: CSVConditionSchema });
4442
}
4543

4644
async getConditionData(mrn) {

0 commit comments

Comments
 (0)