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

Implements lowBoundary and highBoundary #229

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
123 changes: 122 additions & 1 deletion sof-js/src/path.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { default as fhirpath } from 'fhirpath'
import fhir_r4_model from 'fhirpath/fhir-context/r4'
import { FP_DateTime, FP_Time } from 'fhirpath/src/types';
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: these are not exported, but could be


const identity = (ctx, v) => [v]

Expand All @@ -24,6 +26,123 @@ function getReferenceKey(nodes, opts) {
})
}

function checkDate (date, type) {
let [year, month, day] = date.split('-');
if (type == "low") {
if (!day) {
day = '01'
}
if (!month) {
month = '01'
}
} else {
if (!month) {
month = '12';
day = '31';
}
if (!day) {
if (month == "02") {
(year % 4 || !(year % 100) && year % 400) ? day = '28' : day = '29';
}
else if (["04", "06", "09", "11"].includes(month)) {
day = '30';
}
else {
day = '31';
}
}
}
return `${year}-${month}-${day}`;
}

function lowBoundary(nodes) {
return nodes.flatMap((node) => {
if (node == null) {
return null;
}
const { name: type } = node.getTypeInfo();
if (type === "decimal") {
let integer = (node.data.toString().split(".")[0] || "").length;
let precision = (node.data.toString().split(".")[1] || "").length;
return (integer > 8 || precision > 8) ? null : parseFloat(node.data.toFixed(8));
}
else if (type === "time") {
const picoSeconds = (node.data.split(".")[1] || "").padEnd(9, 0);
const time = new FP_Time(node.data.split(".")[0])._dateAtPrecision(2);
return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds);
}
else if(type === "date") {
return checkDate(node.data, "low");
}
else if (type === 'dateTime') {
var timeHMS, timeZone, picoSeconds;
var [date, time] = node.data.split("T");
if (date) var dateObj = checkDate(date, "low");
if (time) {
var matchResult = time.match(/(\d{2}:\d{2}(?::\d{2})?)(?:\.(\d+))?(Z|[\+\-]\d{2}:\d{2})?/);
if (matchResult) {
[, timeHMS, picoSeconds, timeZone] = matchResult;
}
}
if (timeHMS) dateObj = date.concat("T", timeHMS);
const dateTime = new FP_DateTime(dateObj)._dateAtPrecision(5);
const newDate = dateTime.toISOString().split("T")[0];
if (picoSeconds == undefined) picoSeconds = "0";
const newTime = dateTime.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds.padEnd(9, 0));
return newDate.concat("T", newTime, (timeZone == undefined) ? timeZone = "+14:00" : timeZone);
}
return [node];
})
}

function highBoundary(nodes) {
return nodes.flatMap((node) => {
if (node == null) {
return null;
}
const { name: type } = node.getTypeInfo();
if (type === "decimal") {
let integer = (node.data.toString().split(".")[0] || "").length;
let precision = (node.data.toString().split(".")[1] || "").length;
return (integer > 8 || precision > 8)
? null
: parseFloat(node.data.toString().split(".")[0] + '.' + (node.data.toString().split(".")[1] || "0").padEnd(8, 9));
}
else if (type === "time") {
const hasSeconds = node.data.split(":").length == 3;
const picoSeconds = (node.data.split(".")[1] || "").padEnd(9, 9);
const time = new FP_Time(node.data.split(".")[0])._dateAtPrecision(2);
if (!hasSeconds) time.setSeconds(59);
return time.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds);
}
else if (type === "date") {
return checkDate(node.data, "high");
}
else if (type === "dateTime") {
var timeHMS, timeZone, picoSeconds;
var [date, time] = node.data.split("T");
if (date) var dateObj = checkDate(date, "high");
if (time) {
var matchResult = time.match(/(\d{2}:\d{2}(?::\d{2})?)(?:\.(\d+))?(Z|[\+\-]\d{2}:\d{2})?/);
if (matchResult) {
[, timeHMS, picoSeconds, timeZone] = matchResult;
}
var [hours, minutes, seconds] = timeHMS.split(":");
}
if (timeHMS) dateObj = date.concat("T", timeHMS);
const dateTime = new FP_DateTime(dateObj)._dateAtPrecision(5);
const newDate = dateTime.toISOString().split("T")[0];
if (hours == undefined) dateTime.setHours(23);
if (minutes == undefined) dateTime.setMinutes(59);
if (seconds == undefined) dateTime.setSeconds(59);
if (picoSeconds == undefined) picoSeconds = "9";
const newTime = dateTime.toISOString().split("T")[1].slice(0, -4).concat(picoSeconds.padEnd(9, 9));
return newDate.concat("T", newTime, (timeZone == undefined) ? timeZone = "-12:00" : timeZone);
}
return [node];
})
}

function ofType(ctx, nodes, a1, a2, a3) {
console.log('of type nodes', nodes, a1, a2, a3)
return 'ups'
Expand Down Expand Up @@ -52,6 +171,8 @@ let fhirpath_options = {
getResourceKey: { fn: getResourceKey, arity: { 0: [] } },
getReferenceKey: { fn: getReferenceKey, arity: { 0: [], 1: ['TypeSpecifier'] } },
identity: { fn: (nodes) => nodes, arity: { 0: [] } },
lowBoundary: { fn: lowBoundary, arity: { 0: [] }, nullable: true, internalStructures: true},
highBoundary: { fn: highBoundary, arity: { 0: [] }, nullable: true, internalStructures: true},
}
}

Expand All @@ -72,7 +193,7 @@ function process_constants(constants) {
}

export function fhirpath_evaluate(data, path, constants = []) {
return fhirpath.evaluate(data, rewrite_path(path), process_constants(constants), null, fhirpath_options);
return fhirpath.evaluate(data, rewrite_path(path), process_constants(constants), fhir_r4_model, fhirpath_options);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: if we don't use model, TypeInfo for Resources will not work.

Question: should we use R4 in reference?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we can. Do we need it to tell date and dateTime apart?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, to determine date and dateTime, it is important to use the model

}

export function fhirpath_validate(path) {
Expand Down
Loading