app/assets/javascripts/hqmf_util.js.coffee in hqmf2js-1.2.1 vs app/assets/javascripts/hqmf_util.js.coffee in hqmf2js-1.3.0

- old
+ new

@@ -50,10 +50,11 @@ earlier = ts.asDate() later = @date else earlier = @date later = ts.asDate() + return Number.MAX_VALUE if !earlier? || !later? if granularity=="a" TS.yearsDifference(earlier,later) else if granularity=="mo" TS.monthsDifference(earlier,later) else if granularity=="wk" @@ -89,10 +90,13 @@ @afterOrConcurrent(other) else [a,b] = TS.dropSeconds(@date, other.date) a.getTime() > b.getTime() + equals: (other) -> + (@date==null && other.date==null) || (@date.getTime()==other.date.getTime()) + # Returns whether this TS is before or concurrent with the supplied TS ignoring seconds beforeOrConcurrent: (other) -> if @date==null || other.date==null return false [a,b] = TS.dropSeconds(@date, other.date) @@ -396,43 +400,98 @@ SCWE: (other) -> if @low && other.high @low.asDate() && other.high.asDate() && @low.withinSameMinute(other.high) else false + + equals: (other) -> + (@low==null && other.low==null) || (@low.equals(other.low)) && (@high==null && other.high==null) || (@high.equals(other.high)) + @IVL_TS = IVL_TS # Used to represent a value that will match any other value that is not null. class ANYNonNull constructor: -> match: (scalarOrHash) -> val = fieldOrContainerValue(scalarOrHash, 'scalar') val != null @ANYNonNull = ANYNonNull +invokeOne = (patient, initialSpecificContext, fn) -> + if typeof(fn.isTrue)=='function' || typeof(fn)=='boolean' + fn + else + fn(patient, initialSpecificContext) +@invokeOne = invokeOne + +evalUnlessShortCircuit = (fn) -> + # if we are short circuiting then return the function uncalled, if we are not then call the function and + # evaluate the tree. If uncalled, from here the function will only be called if required + if (Logger.short_circuit) then fn else fn() +@evalUnlessShortCircuit = evalUnlessShortCircuit + +invokeAll = (patient, initialSpecificContext, fns) -> + (invokeOne(patient, initialSpecificContext, fn) for fn in fns) +@invokeAll = invokeAll + # Returns true if one or more of the supplied values is true -atLeastOneTrue = (precondition, values...) -> - trueValues = (value for value in values when value && value.isTrue()) - trueValues.length>0 - hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values) +atLeastOneTrue = (precondition, patient, initialSpecificContext, valueFns...) -> + evalUnlessShortCircuit -> + values = invokeAll(patient, initialSpecificContext, valueFns) + trueValues = (value for value in values when value && value.isTrue()) + hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values) @atLeastOneTrue = atLeastOneTrue - + # Returns true if all of the supplied values are true -allTrue = (precondition, values...) -> - trueValues = (value for value in values when value && value.isTrue()) - hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0 && trueValues.length==values.length), values) +allTrue = (precondition, patient, initialSpecificContext, valueFns...) -> + evalUnlessShortCircuit -> + values = [] + for valueFn in valueFns + value = invokeOne(patient, initialSpecificContext, valueFn) + # break if the we have a false value and we're short circuiting. + #If we're not short circuiting then we want to calculate everything + break if value.isFalse() && Logger.short_circuit + values.push(value) + trueValues = (value for value in values when value && value.isTrue()) + if trueValues.length==valueFns.length + hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0), trueValues) + else + # only intersect on false if we are not short circuiting. + # if we are not short circuiting then we want to have the specifics context returned for rationale + if Logger.short_circuit + value = new Boolean(false) + value.specificContext = hqmf.SpecificsManager.empty() + value + else + hqmf.SpecificsManager.intersectAll(new Boolean(false), values) + + @allTrue = allTrue # Returns true if one or more of the supplied values is false -atLeastOneFalse = (precondition, values...) -> - falseValues = (value for value in values when value.isFalse()) - hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true) +atLeastOneFalse = (precondition, patient, initialSpecificContext, valueFns...) -> +# values = invokeAll(patient, initialSpecificContext, valueFns) +# falseValues = (value for value in values when value.isFalse()) +# hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true) + evalUnlessShortCircuit -> + values = [] + hasFalse = false + for valueFn in valueFns + value = invokeOne(patient, initialSpecificContext, valueFn) + values.push(value) + if value.isFalse() + hasFalse = true + break if Logger.short_circuit + hqmf.SpecificsManager.intersectAll(new Boolean(values.length>0 && hasFalse), values, true) @atLeastOneFalse = atLeastOneFalse # Returns true if all of the supplied values are false -allFalse = (precondition, values...) -> - falseValues = (value for value in values when value.isFalse()) - hqmf.SpecificsManager.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true) +allFalse = (precondition, patient, initialSpecificContext, valueFns...) -> + evalUnlessShortCircuit -> + values = invokeAll(patient, initialSpecificContext, valueFns) + falseValues = (value for value in values when value.isFalse()) + hqmf.SpecificsManager.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true) @allFalse = allFalse # Return true if compareTo matches value matchingValue = (value, compareTo) -> new Boolean(compareTo.match(value)) @@ -445,11 +504,11 @@ @anyMatchingValue = anyMatchingValue # Return only those events whose value matches the supplied value filterEventsByValue = (events, value) -> matchingEvents = (event for event in events when (anyMatchingValue(event, value))) - matchingEvents + hqmf.SpecificsManager.maintainSpecifics(matchingEvents, events) @filterEventsByValue = filterEventsByValue # Return only those events with a field that matches the supplied value filterEventsByField = (events, field, value) -> respondingEvents = (event for event in events when event.respondTo(field)) @@ -528,15 +587,18 @@ @XPRODUCT = XPRODUCT # Create a new list containing all the events from the supplied event lists UNION = (eventLists...) -> union = [] - # keep track of the specific occurrences by encounter ID. This is used in eventsMatchBounds (specifically in buildRowsForMatching down the _.isObject path) + # keep track of the specific occurrences by encounter ID. This is used in + # eventsMatchBounds (specifically in buildRowsForMatching down the _.isObject path) specific_occurrence = {} for eventList in eventLists for event in eventList - specific_occurrence[event.id] = eventList.specific_occurrence if eventList.specific_occurrence + if eventList.specific_occurrence + specific_occurrence[event.id] ||= [] + specific_occurrence[event.id].push eventList.specific_occurrence union.push(event) union.specific_occurrence = specific_occurrence unless _.isEmpty(specific_occurrence) hqmf.SpecificsManager.unionAll(union, eventLists) @UNION = UNION @@ -743,42 +805,55 @@ result.specificContext = result.specificContext[operator](range) else result.specificContext = result.specificContext[operator]() result +uniqueEvents = (events) -> + hash = {} + (hash[event.id] = event for event in events) + _.values(hash) +@uniqueEvents = uniqueEvents + +# if we have multiple events at the same exact time and they happen to be the one selected by FIRST, RECENT, etc +# then we want to select all of these issues as the first, most recent, etc. +selectConcurrent = (target, events) -> + targetIVL = target.asIVL_TS() + uniqueEvents((result for result in events when result.asIVL_TS().equals(targetIVL))) +@selectConcurrent = selectConcurrent + FIRST = (events) -> result = [] - result = [events.sort(dateSortAscending)[0]] if (events.length > 0) + result = selectConcurrent(events.sort(dateSortAscending)[0], events) if (events.length > 0) applySpecificOccurrenceSubset('FIRST',hqmf.SpecificsManager.maintainSpecifics(result, events)) @FIRST = FIRST SECOND = (events) -> result = [] - result = [events.sort(dateSortAscending)[1]] if (events.length > 1) + result = selectConcurrent(events.sort(dateSortAscending)[1], events) if (events.length > 1) applySpecificOccurrenceSubset('SECOND',hqmf.SpecificsManager.maintainSpecifics(result, events)) @SECOND = SECOND THIRD = (events) -> result = [] - result = [events.sort(dateSortAscending)[2]] if (events.length > 2) + result = selectConcurrent(events.sort(dateSortAscending)[2], events) if (events.length > 2) applySpecificOccurrenceSubset('THIRD',hqmf.SpecificsManager.maintainSpecifics(result, events)) @THIRD = THIRD FOURTH = (events) -> result = [] - result = [events.sort(dateSortAscending)[3]] if (events.length > 3) + result = selectConcurrent(events.sort(dateSortAscending)[3], events) if (events.length > 3) applySpecificOccurrenceSubset('FOURTH',hqmf.SpecificsManager.maintainSpecifics(result, events)) @FOURTH = FOURTH FIFTH = (events) -> result = [] - result = [events.sort(dateSortAscending)[4]] if (events.length > 4) + result = selectConcurrent(events.sort(dateSortAscending)[4], events) if (events.length > 4) applySpecificOccurrenceSubset('FIFTH',hqmf.SpecificsManager.maintainSpecifics(result, events)) @FIFTH = FIFTH RECENT = (events) -> result = [] - result = [events.sort(dateSortDescending)[0]] if (events.length > 0) + result = selectConcurrent(events.sort(dateSortDescending)[0], events) if (events.length > 0) applySpecificOccurrenceSubset('RECENT',hqmf.SpecificsManager.maintainSpecifics(result, events)) @RECENT = RECENT LAST = (events) -> RECENT(events)