app/assets/javascripts/specifics.js.coffee in hqmf2js-1.2.0 vs app/assets/javascripts/specifics.js.coffee in hqmf2js-1.2.1

- old
+ new

@@ -71,12 +71,17 @@ entry = row.tempValue entry = new hQuery.CodedEntry(entry.json) entry.specificRow = row entry - intersectSpecifics: (populations...) -> - value = @intersectAll(new Boolean(populations[0].isTrue()), populations) + intersectSpecifics: (nextPopulation, previousPopulation, occurrenceIDs) -> + # we need to pass the episode indicies all the way down through the interesection to the match function + # this must be done because we need to ensure that on intersection of populations the * does not allow an episode through + # that was not part of a previous population + episodeIndices = null + episodeIndices = (@getColumnIndex(occurrenceID) for occurrenceID in occurrenceIDs) if occurrenceIDs? + value = @intersectAll(new Boolean(nextPopulation.isTrue()), [nextPopulation, previousPopulation], false, episodeIndices) value # Returns a count of the unique events that match the criteria for the supplied # specific occurrence. Call after validating that population criteria are met. Returns # 1 if occurrenceID is null, for use with patient based measures. @@ -102,23 +107,23 @@ return result else if @validate(exclusions) return @maintainSpecifics(new Boolean(false), initial) else return initial - + # Returns a boolean indication of whether all of the supplied population criteria are # met validate: (intersectedPopulation) -> intersectedPopulation.isTrue() and intersectedPopulation.specificContext.hasRows() - intersectAll: (boolVal, values, negate=false) -> + intersectAll: (boolVal, values, negate=false, episodeIndices) -> result = new hqmf.SpecificOccurrence # add identity row result.addIdentityRow() for value in values if value.specificContext? - result = result.intersect(value.specificContext) + result = result.intersect(value.specificContext, episodeIndices) if negate and (!result.hasRows() or result.hasSpecifics()) result = result.negate() result = result.compactReusedEvents() # this is a little odd, but it appears when we have a negation with specifics we can # ignore the logical result of the negation. The reason we do this is because we may @@ -200,15 +205,15 @@ union: (other) -> value = new hqmf.SpecificOccurrence() value.rows = @rows.concat(other.rows) value.removeDuplicateRows() - intersect: (other) -> + intersect: (other, episodeIndices) -> value = new hqmf.SpecificOccurrence() for leftRow in @rows for rightRow in other.rows - result = leftRow.intersect(rightRow) + result = leftRow.intersect(rightRow, episodeIndices) value.rows.push(result) if result? value.removeDuplicateRows() getLeftMost: -> leftMost = undefined @@ -331,12 +336,10 @@ new hqmf.SpecificOccurrence(resultRows) addIdentityRow: -> @addRows(hqmf.SpecificsManager.identity().rows) - - class Row # {'OccurrenceAEncounter':1, 'OccurrenceBEncounter'2} constructor: (leftMost, occurrences={}) -> throw "left most key must be a string or undefined was: #{leftMost}" if typeof(leftMost) != 'string' and typeof(leftMost) != 'undefined' @length = hqmf.SpecificsManager.occurrences.length @@ -368,22 +371,34 @@ equal &&= Row.valuesEqual(@tempValue, other.tempValue) for value,i in @values equal &&= Row.valuesEqual(value, other.values[i]) equal - intersect: (other) -> + intersect: (other, episodeIndices) -> intersectedRow = new Row(@leftMost, {}) intersectedRow.tempValue = @tempValue - allMatch = true + + # if all the episodes are any, then they were not referenced by the parent population. This occurs when an intersection is done + # against the identity row. In this case we want to allow the specific occurrences through. This happens when we intersect against a measure + # without a denomninator, and on regular intersections since we start with the identity row in the context. + allEpisodesAny = (episodeIndices? && (@allValuesAny(episodeIndices) || other.allValuesAny(episodeIndices))) + for value,i in @values - result = Row.match(value, other.values[i]) + # check if the value is an episode of care. If so it will be treated differently in the match function + isEpisodeOfCare = (episodeIndices? && !allEpisodesAny && episodeIndices.indexOf(i) >= 0) + result = Row.match(value, other.values[i], isEpisodeOfCare) if result? intersectedRow.values[i] = result else return undefined intersectedRow + allValuesAny: (indicies) -> + for i in indicies + return false if @values[i] != hqmf.SpecificsManager.any + return true + groupKeyForLeftMost: -> @groupKey(@leftMost) groupKey: (key=null) -> keyForGroup = '' @@ -395,16 +410,24 @@ else keyForGroup += "#{value}_" keyForGroup - @match: (left, right) -> - return right if left == hqmf.SpecificsManager.any - return left if right == hqmf.SpecificsManager.any + @match: (left, right, isEpisodeOfCare) -> + return @checkEpisodeOfCare(right, isEpisodeOfCare) if left == hqmf.SpecificsManager.any + return @checkEpisodeOfCare(left, isEpisodeOfCare) if right == hqmf.SpecificsManager.any return left if left.id == right.id return undefined + # if we are dealing with an episode of care we don't want to match against the any (*) indicator. This is because + # the any indicator from a previous population indicates that we did not evaluate against that occurrence in a positive path. + # this is typically OK with specific occurrences, but not if they represent episodes of care. + @checkEpisodeOfCare: (value, isEpisodeOfCare) -> + # return the any indicator to signify that the episode of care was unobserved. This will eliminate it from the counts. + return hqmf.SpecificsManager.any if (isEpisodeOfCare) + return value + @valuesEqual: (left, right) -> return true if !left? and !right? return false if !left? return false if !right? return true if left == hqmf.SpecificsManager.any and right == hqmf.SpecificsManager.any @@ -415,10 +438,15 @@ @buildRowsForMatching: (entryKey, entry, matchesKey, matches) -> rows = [] for match in matches occurrences={} occurrences[entryKey] = entry - occurrences[matchesKey] = match + # from unions and crossproducts we may have a matches key that is a hash of object ids mapped to the specific occurrence key. + # this is because we may have multiple different specific occurrences on the right hand side if it came from a group + if (_.isObject(matchesKey)) + occurrences[matchesKey[match.id]] = match + else + occurrences[matchesKey] = match rows.push(new Row(entryKey, occurrences)) rows # build specific for a given entry (there are no temporal references) @buildForDataCriteria: (entryKey, entries) ->