Skip to content

Commit

Permalink
Merge pull request #20 in LFOR/fhirpath.js from feature/LF-1598/evalu…
Browse files Browse the repository at this point in the history
…ating-for-part-of-resource to master

* commit 'c8fbd132c80efdd387c81d23fbd3ed33b6e20b1a':
  npm audit fix
  refactor: evaluating expression for a part of a resource
  Evaluating expression for a part of a resource
  • Loading branch information
yuriy-sedinkin committed Oct 22, 2020
2 parents 33f6c5b + c8fbd13 commit f372723
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
This log documents significant changes for each release. This project follows
[Semantic Versioning](http://semver.org/).

## [2.7.0] - 2020-10-21
### Added
- Evaluating expression for a part of a resource

## [2.6.2] - 2020-10-09
### Fixed
- Comparison of dates indicated with different level of precision
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ lower case, e.g., 'dstu2', 'stu3' or 'r4').
fhirpath --expression 'Observation.value' --resourceJSON '{"resourceType": "Observation", "valueString": "Green"}' --model r4
```

Also, you can pass in a filename or a string of JSON representing a part of the resource.
In that case, you should pass in the base path from which this part of the resource was extracted.
```sh
fhirpath --basePath QuestionnaireResponse.item --expression 'answer.value' --model r4 --resourceFile questionnaire-part-example.json

> fhirpath(answer.value) =>
> [
> "2 year"
> ]
```

If given just the FHIRPath expression, the utility will print the parsed tree:

```sh
Expand Down
21 changes: 12 additions & 9 deletions bin/fhirpath
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ const fs = require('fs');
const yaml = require('js-yaml');
const options = require('commander');
options.description('Parses and/or runs a FHIRPath expression. If neither --resourceFile nor --resourceJSON is provided, the parse tree of the FHIRPath expression will be output.');
options.option('-e, --expression <expr>', 'FHIRPath expression');
options.option('-f, --resourceFile <path name>', 'A file containing the JSON resource.');
options.option('-r, --resourceJSON <JSON>', 'JSON resource.');
options.option('-b, --basePath <path>', 'base path in the resource from which the JSON data was extracted.');
options.option('-e, --expression <expr>', 'FHIRPath expression.');
options.option('-f, --resourceFile <path name>', 'A file containing the JSON resource or part of it, if basePath is passed.');
options.option('-r, --resourceJSON <JSON>', 'JSON resource or part of it, if basePath is passed.');
options.option('-v, --variables <JSON>', 'JSON hash of variables.');
options.option('-m, --model <dstu2 | stu3 | r4>', 'Include FHIR model data.');
options.parse(process.argv);
Expand All @@ -16,8 +17,10 @@ options.parse(process.argv);
// it can be extended with understading urls and files
// TODO: introduce subcommands to inspect, eval fhirpath etc

let expr = options.expression;
if (!expr)
const expression = options.expression;
const base = options.basePath;

if (!expression)
options.help();
else {
let resourceJSON = options.resourceJSON;
Expand All @@ -28,8 +31,8 @@ else {
}
if (!resourceJSON) {
// Just display the parse tree
let res = fp.parse(expr);
console.log('Parse tree for fhirpath(' + expr + ') =>');
let res = fp.parse(expression);
console.log('Parse tree for fhirpath(' + expression + ') =>');
console.log(yaml.dump(res));
}
else {
Expand All @@ -44,8 +47,8 @@ else {
throw new Error('FHIR model must be one of '+supportedVersions);
model = require('../fhir-context/'+options.model);
}
let res = fp.evaluate(resource, expr, context, model);
console.log('fhirpath(' + expr + ') =>');
let res = fp.evaluate(resource, base ? {base, expression} : expression, context, model);
console.log('fhirpath(' + expression + ') =>');
console.log(JSON.stringify(res, null, " "));
}
}
6 changes: 3 additions & 3 deletions demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fhirpath",
"version": "2.6.2",
"version": "2.7.0",
"description": "A FHIRPath engine",
"main": "src/fhirpath.js",
"dependencies": {
Expand Down
20 changes: 13 additions & 7 deletions src/fhirpath.js
Original file line number Diff line number Diff line change
Expand Up @@ -670,17 +670,23 @@ function applyParsedPath(resource, parsedPath, context, model) {
}

/**
* Evaluates the "path" FHIRPath expression on the given resource, using data
* from "context" for variables mentioned in the "path" expression.
* @param {(object|object[])} resource - FHIR resource, bundle as js object or array of resources
* This resource will be modified by this function to add type information.
* @param {string} path - fhirpath expression, sample 'Patient.name.given'
* Evaluates the "path" FHIRPath expression on the given resource or part of the resource,
* using data from "context" for variables mentioned in the "path" expression.
* @param {(object|object[])} fhirData - FHIR resource, part of a resource (in this case
* path.base should be provided), bundle as js object or array of resources.
* This object/array will be modified by this function to add type information.
* @param {string|object} path - string with fhirpath expression, sample 'Patient.name.given',
* or object, if fhirData represents the part of the FHIR resource:
* @param {string} path.base - base path in resource from which fhirData was extracted
* @param {string} path.expression - fhirpath expression relative to path.base
* @param {object} context - a hash of variable name/value pairs.
* @param {object} model - The "model" data object specific to a domain, e.g. R4.
* For example, you could pass in the result of require("fhirpath/fhir-context/r4");
*/
var evaluate = function(resource, path, context, model) {
const node = parser.parse(path);
var evaluate = function(fhirData, path, context, model) {
const pathIsObject = typeof path === 'object';
const resource = pathIsObject ? makeResNode(fhirData, path.base) : fhirData;
const node = parser.parse(pathIsObject ? path.expression : path);
return applyParsedPath(resource, node, context, model);
};

Expand Down
6 changes: 6 additions & 0 deletions test/bin_fhirpath.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ describe ('bin/fhirpath', function () {
tempFileObj.removeCallback();
});

it ('should evaluate when given a file with a part of a resource', function() {
checkOutput("bin/fhirpath -b QuestionnaireResponse.item "
+"-e 'answer.value = 2 year' -m r4 -f '"
+__dirname+"/resources/questionnaire-part-example.json'", /true/g);
});

it ('should accept a hash of variables', function() {
checkOutput("bin/fhirpath -e '%v1 + 2' -r '{}' -v '{\"v1\": 5}'", /7/g);
});
Expand Down
7 changes: 7 additions & 0 deletions test/cases/simple.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ tests:
desc: 'Parenthesized expression'
result: [2,3]

- desc: Evaluating expression for a part of a resource
inputfile: questionnaire-part-example.json
model: 'r4'
expression: {base: 'QuestionnaireResponse.item', expression: 'answer.value = 2 year'}
result:
- true



subject:
Expand Down
5 changes: 3 additions & 2 deletions test/fhirpath.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ const generateTest = (test, testResource) => {
};

expressions.forEach(expression => {
const expressionText = expression instanceof Object ? JSON.stringify(expression) : expression;
switch(test.type) {
case 'skipped':
return it.skip(`Disabled test ${test.desc}`, () => {});
case 'focused':
return it.only(((test.desc || '') + ': ' + (expression || '')), () => getTestData(expression));
return it.only(((test.desc || '') + ': ' + (expressionText|| '')), () => getTestData(expression));
default:
return it(((test.desc || '') + ': ' + (expression || '')), () => getTestData(expression));
return it(((test.desc || '') + ': ' + (expressionText || '')), () => getTestData(expression));
}
});
};
Expand Down
10 changes: 10 additions & 0 deletions test/resources/questionnaire-part-example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"answer": {
"valueQuantity": {
"value": 2,
"unit": "year(real UCUM code in field 'code')",
"system": "http://unitsofmeasure.org",
"code": "a"
}
}
}

0 comments on commit f372723

Please sign in to comment.