app/assets/javascripts/hqmf_util.js.coffee in hqmf2js-1.0.1 vs app/assets/javascripts/hqmf_util.js.coffee in hqmf2js-1.1.0

- old
+ new

@@ -13,30 +13,30 @@ if isNaN(hour) hour = 0 minute = parseInt(hl7ts.substring(10,12), 10) if isNaN(minute) minute = 0 - @date = new Date(year, month, day, hour, minute) + @date = new Date(Date.UTC(year, month, day, hour, minute)) else @date = new Date() # Add a time period to th and return it # pq - a time period as an instance of PQ. Supports units of a (year), mo (month), # wk (week), d (day), h (hour) and min (minute). add: (pq) -> if pq.unit=="a" - @date.setFullYear(@date.getFullYear()+pq.value) + @date.setUTCFullYear(@date.getUTCFullYear()+pq.value) else if pq.unit=="mo" - @date.setMonth(@date.getMonth()+pq.value) + @date.setUTCMonth(@date.getUTCMonth()+pq.value) else if pq.unit=="wk" - @date.setDate(@date.getDate()+(7*pq.value)) + @date.setUTCDate(@date.getUTCDate()+(7*pq.value)) else if pq.unit=="d" - @date.setDate(@date.getDate()+pq.value) + @date.setUTCDate(@date.getUTCDate()+pq.value) else if pq.unit=="h" - @date.setHours(@date.getHours()+pq.value) + @date.setUTCHours(@date.getUTCHours()+pq.value) else if pq.unit=="min" - @date.setMinutes(@date.getMinutes()+pq.value) + @date.setUTCMinutes(@date.getUTCMinutes()+pq.value) else throw "Unknown time unit: "+pq.unit this # Returns the difference between this TS and the supplied TS as an absolute @@ -74,21 +74,21 @@ # Returns whether this TS is before the supplied TS ignoring seconds before: (other) -> if @date==null || other.date==null return false if other.inclusive - beforeOrConcurrent(other) + @beforeOrConcurrent(other) else [a,b] = TS.dropSeconds(@date, other.date) a.getTime() < b.getTime() # Returns whether this TS is after the supplied TS ignoring seconds after: (other) -> if @date==null || other.date==null return false if other.inclusive - afterOrConcurrent(other) + @afterOrConcurrent(other) else [a,b] = TS.dropSeconds(@date, other.date) a.getTime() > b.getTime() # Returns whether this TS is before or concurrent with the supplied TS ignoring seconds @@ -111,25 +111,25 @@ [a,b] = TS.dropSeconds(@date, other.date) a.getTime()==b.getTime() # Number of whole years between the two time stamps (as Date objects) @yearsDifference: (earlier, later) -> - if (later.getMonth() < earlier.getMonth()) - later.getFullYear()-earlier.getFullYear()-1 - else if (later.getMonth() == earlier.getMonth() && later.getDate() >= earlier.getDate()) - later.getFullYear()-earlier.getFullYear() - else if (later.getMonth() == earlier.getMonth() && later.getDate() < earlier.getDate()) - later.getFullYear()-earlier.getFullYear()-1 + if (later.getUTCMonth() < earlier.getUTCMonth()) + later.getUTCFullYear()-earlier.getUTCFullYear()-1 + else if (later.getUTCMonth() == earlier.getUTCMonth() && later.getUTCDate() >= earlier.getUTCDate()) + later.getUTCFullYear()-earlier.getUTCFullYear() + else if (later.getUTCMonth() == earlier.getUTCMonth() && later.getUTCDate() < earlier.getUTCDate()) + later.getUTCFullYear()-earlier.getUTCFullYear()-1 else - later.getFullYear()-earlier.getFullYear() + later.getUTCFullYear()-earlier.getUTCFullYear() # Number of whole months between the two time stamps (as Date objects) @monthsDifference: (earlier, later) -> - if (later.getDate() >= earlier.getDate()) - (later.getFullYear()-earlier.getFullYear())*12+later.getMonth()-earlier.getMonth() + if (later.getUTCDate() >= earlier.getUTCDate()) + (later.getUTCFullYear()-earlier.getUTCFullYear())*12+later.getUTCMonth()-earlier.getUTCMonth() else - (later.getFullYear()-earlier.getFullYear())*12+later.getMonth()-earlier.getMonth()-1 + (later.getUTCFullYear()-earlier.getUTCFullYear())*12+later.getUTCMonth()-earlier.getUTCMonth()-1 # Number of whole minutes between the two time stamps (as Date objects) @minutesDifference: (earlier, later) -> Math.floor(((later.getTime()-earlier.getTime())/1000)/60) @@ -138,14 +138,12 @@ Math.floor(TS.minutesDifference(earlier,later)/60) # Number of days betweem the two time stamps (as Date objects) @daysDifference: (earlier, later) -> # have to discard time portion for day difference calculation purposes - e = new Date(earlier.getFullYear(), earlier.getMonth(), earlier.getDate()) - e.setUTCHours(0) - l = new Date(later.getFullYear(), later.getMonth(), later.getDate()) - l.setUTCHours(0) + e = new Date(Date.UTC(earlier.getUTCFullYear(), earlier.getUTCMonth(), earlier.getUTCDate())) + l = new Date(Date.UTC(later.getUTCFullYear(), later.getUTCMonth(), later.getUTCDate())) Math.floor(TS.hoursDifference(e,l)/24) # Number of whole weeks between the two time stmaps (as Date objects) @weeksDifference: (earlier, later) -> Math.floor(TS.daysDifference(earlier,later)/7) @@ -411,29 +409,29 @@ # Returns true if one or more of the supplied values is true atLeastOneTrue = (values...) -> trueValues = (value for value in values when value && value.isTrue()) trueValues.length>0 - Specifics.unionAll(new Boolean(trueValues.length>0), values) + hqmf.SpecificsManager.unionAll(new Boolean(trueValues.length>0), values) @atLeastOneTrue = atLeastOneTrue # Returns true if all of the supplied values are true allTrue = (values...) -> trueValues = (value for value in values when value && value.isTrue()) - Specifics.intersectAll(new Boolean(trueValues.length>0 && trueValues.length==values.length), values) + hqmf.SpecificsManager.intersectAll(new Boolean(trueValues.length>0 && trueValues.length==values.length), values) @allTrue = allTrue # Returns true if one or more of the supplied values is false atLeastOneFalse = (values...) -> falseValues = (value for value in values when value.isFalse()) - Specifics.intersectAll(new Boolean(falseValues.length>0), values, true) + hqmf.SpecificsManager.intersectAll(new Boolean(falseValues.length>0), values, true) @atLeastOneFalse = atLeastOneFalse # Returns true if all of the supplied values are false allFalse = (values...) -> falseValues = (value for value in values when value.isFalse()) - Specifics.unionAll(new Boolean(falseValues.length>0 && falseValues.length==values.length), values, true) + 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)) @@ -452,16 +450,60 @@ @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)) - event for event in respondingEvents when value.match(event[field]()) + result = (event for event in respondingEvents when value.match(event[field]())) + hqmf.SpecificsManager.maintainSpecifics(result, events) @filterEventsByField = filterEventsByField +shiftTimes = (event, field) -> + shiftedEvent = new event.constructor(event.json) + shiftedEvent.setTimestamp(shiftedEvent[field]()) + shiftedEvent +@shiftTimes = shiftTimes + +adjustBoundsForField = (events, field) -> + validEvents = (event for event in events when (event.respondTo(field) and event[field]())) + shiftedEvents = (shiftTimes(event, field) for event in validEvents) + hqmf.SpecificsManager.maintainSpecifics(shiftedEvents, events) +@adjustBoundsForField = adjustBoundsForField + +# Clone the supplied event and replace any facilities with just the supplied one +narrowEventForFacility = (event, facility) -> + narrowed = new event.constructor(event.json) + # uncomment the following line when patient API is modified to support multiple + # facilities + # narrowed._facilities = [facility] + narrowed +@narrowEventForFacility = narrowEventForFacility + +# Return a cloned set of events, each with just one of the original facilities +denormalizeEvent = (event) -> + # the following line should be changed when the patient API is modified to support + # more than one facility per encounter + # narrowed = (narrowEventForFacility(event, facility) for facility in event.facilities) + narrowed = (narrowEventForFacility(event, facility) for facility in [event.facility]) +@denormalizeEvent = denormalizeEvent + +# Creates a new set of events with one location per event. Input events with more than +# one location will be duplicated once per location and each resulting event will +# be assigned one location. Start and end times of the event will be adjusted to match the +# value of the supplied field +denormalizeEventsByLocation = (events, field) -> + respondingEvents = (event for event in events when event.respondTo("facility") and event.facility()) + denormalizedEvents = (denormalizeEvent(event) for event in respondingEvents) + denormalizedEvents = [].concat denormalizedEvents... + result = adjustBoundsForField(denormalizedEvents, field) + hqmf.SpecificsManager.maintainSpecifics(result, events) +@denormalizeEventsByLocation = denormalizeEventsByLocation + # Utility method to obtain the value set for an OID getCodes = (oid) -> - OidDictionary[oid] + codes = OidDictionary[oid] + throw "value set oid could not be found: #{oid}" unless codes? + codes @getCodes = getCodes # Used for representing XPRODUCTs of arrays, holds both a flattened array that contains # all the elements of the compoent arrays and the component arrays themselves class CrossProduct extends Array @@ -473,27 +515,27 @@ for event in eventList this.push(event) # Create a CrossProduct of the supplied event lists. XPRODUCT = (eventLists...) -> - Specifics.intersectAll(new CrossProduct(eventLists), eventLists) + hqmf.SpecificsManager.intersectAll(new CrossProduct(eventLists), eventLists) @XPRODUCT = XPRODUCT # Create a new list containing all the events from the supplied event lists UNION = (eventLists...) -> union = [] for eventList in eventLists for event in eventList union.push(event) - Specifics.unionAll(union, eventLists) + hqmf.SpecificsManager.unionAll(union, eventLists) @UNION = UNION # Return true if the number of events matches the supplied range COUNT = (events, range) -> count = events.length result = new Boolean(range.match(count)) - applySpecificOccurrenceSubset('COUNT', Specifics.maintainSpecifics(result, events), range) + applySpecificOccurrenceSubset('COUNT', hqmf.SpecificsManager.maintainSpecifics(result, events), range) @COUNT = COUNT # Convert an hQuery.CodedEntry or JS Date into an IVL_TS getIVL = (eventOrTimeStamp) -> if eventOrTimeStamp.asIVL_TS @@ -519,11 +561,12 @@ 'EDU': 'high', 'ECW': 'high' 'SCW': 'low', 'ECWS': 'high' 'SCWE': 'low', - 'CONCURRENT': 'low' + 'CONCURRENT': 'low', + 'DATEDIFF': 'low' } boundAccessor = { 'DURING': 'low', 'OVERLAP': 'low', @@ -539,11 +582,12 @@ 'EDU': 'low', 'ECW': 'high' 'SCW': 'low', 'ECWS': 'low' 'SCWE': 'high', - 'CONCURRENT': 'low' + 'CONCURRENT': 'low', + 'DATEDIFF': 'low' } # Determine whether the supplied event falls within range of the supplied bound # using the method to determine which property of the event and bound to use in # the comparison. E.g. if method is SBS then check whether the start of the event @@ -561,41 +605,41 @@ matchingBounds = [] for boundList in bounds.eventLists currentMatches = eventMatchesBounds(event, boundList, methodName, range) return [] if (currentMatches.length == 0) matchingBounds = matchingBounds.concat(currentMatches) - return Specifics.maintainSpecifics(matchingBounds,bounds) + return hqmf.SpecificsManager.maintainSpecifics(matchingBounds,bounds) else eventIVL = getIVL(event) matchingBounds = (bound for bound in bounds when ( boundIVL = getIVL(bound) result = eventIVL[methodName](boundIVL) if result && range result &&= withinRange(methodName, eventIVL, boundIVL, range) result )) - Specifics.maintainSpecifics(matchingBounds, bounds) + hqmf.SpecificsManager.maintainSpecifics(matchingBounds, bounds) @eventMatchesBounds = eventMatchesBounds # Determine which event match one of the supplied bounds eventsMatchBounds = (events, bounds, methodName, range) -> if (bounds.length==undefined) bounds = [bounds] if (events.length==undefined) events = [events] - specificContext = new Specifics() + specificContext = new hqmf.SpecificOccurrence() hasSpecificOccurrence = (events.specific_occurrence? || bounds.specific_occurrence?) matchingEvents = [] matchingEvents.specific_occurrence = events.specific_occurrence for event in events matchingBounds=eventMatchesBounds(event, bounds, methodName, range) matchingEvents.push(event) if matchingBounds.length > 0 if hasSpecificOccurrence matchingEvents.specific_occurrence = events.specific_occurrence - # TODO: we'll need a temporary variable for non specific occurrences on the left so that we can do rejections based on restrictions in the data criteria + # TODO: well need a temporary variable for non specific occurrences on the left so that we can do rejections based on restrictions in the data criteria specificContext.addRows(Row.buildRowsForMatching(events.specific_occurrence, event, bounds.specific_occurrence, matchingBounds)) else # add all stars specificContext.addIdentityRow() @@ -691,41 +735,41 @@ result FIRST = (events) -> result = [] result = [events.sort(dateSortAscending)[0]] if (events.length > 0) - applySpecificOccurrenceSubset('FIRST',Specifics.maintainSpecifics(result, events)) + applySpecificOccurrenceSubset('FIRST',hqmf.SpecificsManager.maintainSpecifics(result, events)) @FIRST = FIRST SECOND = (events) -> result = [] result = [events.sort(dateSortAscending)[1]] if (events.length > 1) - applySpecificOccurrenceSubset('SECOND',Specifics.maintainSpecifics(result, events)) + applySpecificOccurrenceSubset('SECOND',hqmf.SpecificsManager.maintainSpecifics(result, events)) @SECOND = SECOND THIRD = (events) -> result = [] result = [events.sort(dateSortAscending)[2]] if (events.length > 2) - applySpecificOccurrenceSubset('THIRD',Specifics.maintainSpecifics(result, events)) + applySpecificOccurrenceSubset('THIRD',hqmf.SpecificsManager.maintainSpecifics(result, events)) @THIRD = THIRD FOURTH = (events) -> result = [] result = [events.sort(dateSortAscending)[3]] if (events.length > 3) - applySpecificOccurrenceSubset('FOURTH',Specifics.maintainSpecifics(result, events)) + applySpecificOccurrenceSubset('FOURTH',hqmf.SpecificsManager.maintainSpecifics(result, events)) @FOURTH = FOURTH FIFTH = (events) -> result = [] result = [events.sort(dateSortAscending)[4]] if (events.length > 4) - applySpecificOccurrenceSubset('FIFTH',Specifics.maintainSpecifics(result, events)) + applySpecificOccurrenceSubset('FIFTH',hqmf.SpecificsManager.maintainSpecifics(result, events)) @FIFTH = FIFTH RECENT = (events) -> result = [] result = [events.sort(dateSortDescending)[0]] if (events.length > 0) - applySpecificOccurrenceSubset('RECENT',Specifics.maintainSpecifics(result, events)) + applySpecificOccurrenceSubset('RECENT',hqmf.SpecificsManager.maintainSpecifics(result, events)) @RECENT = RECENT LAST = (events) -> RECENT(events) @LAST = LAST @@ -757,19 +801,26 @@ MIN = (events, range) -> minValue = Infinity if (events.length > 0) minValue = events.sort(valueSortAscending)[0].value()["scalar"] result = new Boolean(range.match(minValue)) - applySpecificOccurrenceSubset('MIN',Specifics.maintainSpecifics(result, events), range) + applySpecificOccurrenceSubset('MIN',hqmf.SpecificsManager.maintainSpecifics(result, events), range) @MIN = MIN MAX = (events, range) -> maxValue = -Infinity if (events.length > 0) maxValue = events.sort(valueSortDescending)[0].value()["scalar"] result = new Boolean(range.match(maxValue)) - applySpecificOccurrenceSubset('MAX',Specifics.maintainSpecifics(result, events), range) + applySpecificOccurrenceSubset('MAX',hqmf.SpecificsManager.maintainSpecifics(result, events), range) @MAX = MAX + +DATEDIFF = (events, range) -> + return hqmf.SpecificsManager.maintainSpecifics(new Boolean(false), events) if events.length < 2 + throw "cannot calculate against more than 2 events" if events.length > 2 + hqmf.SpecificsManager.maintainSpecifics(new Boolean(withinRange('DATEDIFF', getIVL(events[0]), getIVL(events[1]), range)), events) +@DATEDIFF = DATEDIFF + @OidDictionary = {}; hqmfjs = hqmfjs||{} @hqmfjs = @hqmfjs||{};