hqmf-parser 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/Gemfile +23 -0
  2. data/README.md +903 -0
  3. data/Rakefile +19 -0
  4. data/VERSION +1 -0
  5. data/lib/hqmf-generator/hqmf-generator.rb +308 -0
  6. data/lib/hqmf-model/attribute.rb +35 -0
  7. data/lib/hqmf-model/data_criteria.rb +322 -0
  8. data/lib/hqmf-model/document.rb +172 -0
  9. data/lib/hqmf-model/population_criteria.rb +90 -0
  10. data/lib/hqmf-model/precondition.rb +85 -0
  11. data/lib/hqmf-model/types.rb +318 -0
  12. data/lib/hqmf-model/utilities.rb +52 -0
  13. data/lib/hqmf-parser.rb +54 -0
  14. data/lib/hqmf-parser/1.0/attribute.rb +68 -0
  15. data/lib/hqmf-parser/1.0/comparison.rb +34 -0
  16. data/lib/hqmf-parser/1.0/data_criteria.rb +105 -0
  17. data/lib/hqmf-parser/1.0/document.rb +209 -0
  18. data/lib/hqmf-parser/1.0/expression.rb +52 -0
  19. data/lib/hqmf-parser/1.0/population_criteria.rb +79 -0
  20. data/lib/hqmf-parser/1.0/precondition.rb +89 -0
  21. data/lib/hqmf-parser/1.0/range.rb +65 -0
  22. data/lib/hqmf-parser/1.0/restriction.rb +157 -0
  23. data/lib/hqmf-parser/1.0/utilities.rb +41 -0
  24. data/lib/hqmf-parser/2.0/data_criteria.rb +319 -0
  25. data/lib/hqmf-parser/2.0/document.rb +165 -0
  26. data/lib/hqmf-parser/2.0/population_criteria.rb +53 -0
  27. data/lib/hqmf-parser/2.0/precondition.rb +44 -0
  28. data/lib/hqmf-parser/2.0/types.rb +223 -0
  29. data/lib/hqmf-parser/2.0/utilities.rb +30 -0
  30. data/lib/hqmf-parser/converter/pass1/data_criteria_converter.rb +254 -0
  31. data/lib/hqmf-parser/converter/pass1/document_converter.rb +183 -0
  32. data/lib/hqmf-parser/converter/pass1/population_criteria_converter.rb +135 -0
  33. data/lib/hqmf-parser/converter/pass1/precondition_converter.rb +164 -0
  34. data/lib/hqmf-parser/converter/pass1/precondition_extractor.rb +159 -0
  35. data/lib/hqmf-parser/converter/pass1/simple_data_criteria.rb +35 -0
  36. data/lib/hqmf-parser/converter/pass1/simple_operator.rb +89 -0
  37. data/lib/hqmf-parser/converter/pass1/simple_population_criteria.rb +10 -0
  38. data/lib/hqmf-parser/converter/pass1/simple_precondition.rb +63 -0
  39. data/lib/hqmf-parser/converter/pass1/simple_restriction.rb +64 -0
  40. data/lib/hqmf-parser/converter/pass2/comparison_converter.rb +91 -0
  41. data/lib/hqmf-parser/converter/pass2/operator_converter.rb +169 -0
  42. data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter.rb +86 -0
  43. data/lib/hqmf-parser/converter/pass3/specific_occurrence_converter_bak.rb +70 -0
  44. data/lib/hqmf-parser/parser.rb +22 -0
  45. data/lib/hqmf-parser/value_sets/value_set_parser.rb +206 -0
  46. data/lib/tasks/coverme.rake +8 -0
  47. data/lib/tasks/hqmf.rake +141 -0
  48. data/lib/tasks/value_sets.rake +23 -0
  49. metadata +159 -0
@@ -0,0 +1,53 @@
1
+ module HQMF2
2
+ # Represents an HQMF population criteria, also supports all the same methods as
3
+ # HQMF2::Precondition
4
+ class PopulationCriteria
5
+
6
+ include HQMF2::Utilities
7
+
8
+ attr_reader :preconditions, :id, :hqmf_id, :title, :type
9
+
10
+ # Create a new population criteria from the supplied HQMF entry
11
+ # @param [Nokogiri::XML::Element] the HQMF entry
12
+ def initialize(entry, doc)
13
+ @doc = doc
14
+ @entry = entry
15
+ @hqmf_id = attr_val('./*/cda:id/@extension')
16
+ @title = attr_val('./*/cda:code/cda:displayName/@value')
17
+ @type = attr_val('./*/cda:code/@code')
18
+ @preconditions = @entry.xpath('./*/cda:precondition[not(@nullFlavor)]', HQMF2::Document::NAMESPACES).collect do |precondition|
19
+ Precondition.new(precondition, @doc)
20
+ end
21
+ end
22
+
23
+ def create_human_readable_id(id)
24
+ @id = id
25
+ end
26
+
27
+ # Return true of this precondition represents a conjunction with nested preconditions
28
+ # or false of this precondition is a reference to a data criteria
29
+ def conjunction?
30
+ true
31
+ end
32
+
33
+ # Get the conjunction code, e.g. allTrue, allFalse
34
+ # @return [String] conjunction code
35
+ def conjunction_code
36
+ case @type
37
+ when HQMF::PopulationCriteria::IPP, HQMF::PopulationCriteria::DENOM, HQMF::PopulationCriteria::NUMER
38
+ HQMF::Precondition::ALL_TRUE
39
+ when HQMF::PopulationCriteria::EXCEP, HQMF::PopulationCriteria::DENEX
40
+ HQMF::Precondition::AT_LEAST_ONE_TRUE
41
+ else
42
+ raise "Unknown population type [#{@type}]"
43
+ end
44
+ end
45
+
46
+ def to_model
47
+ mps = preconditions.collect {|p| p.to_model}
48
+ HQMF::PopulationCriteria.new(id, hqmf_id, type, mps, title)
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,44 @@
1
+ module HQMF2
2
+
3
+ class Precondition
4
+
5
+ include HQMF2::Utilities
6
+
7
+ attr_reader :preconditions, :reference
8
+
9
+ def initialize(entry, doc)
10
+ @doc = doc
11
+ @entry = entry
12
+ @preconditions = @entry.xpath('./*/cda:precondition', HQMF2::Document::NAMESPACES).collect do |precondition|
13
+ Precondition.new(precondition, @doc)
14
+ end
15
+ reference_def = @entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES)
16
+ if reference_def
17
+ @reference = Reference.new(reference_def)
18
+ end
19
+ end
20
+
21
+ # Return true of this precondition represents a conjunction with nested preconditions
22
+ # or false of this precondition is a reference to a data criteria
23
+ def conjunction?
24
+ @preconditions.length>0
25
+ end
26
+
27
+ # Get the conjunction code, e.g. allTrue, allFalse
28
+ # @return [String] conjunction code
29
+ def conjunction_code
30
+ if conjunction?
31
+ @entry.at_xpath('./*[1]', HQMF2::Document::NAMESPACES).name
32
+ else
33
+ nil
34
+ end
35
+ end
36
+
37
+ def to_model
38
+ pcs = preconditions.collect {|p| p.to_model}
39
+ mr = reference ? reference.to_model : nil
40
+ HQMF::Precondition.new(nil, pcs, mr, conjunction_code, false)
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,223 @@
1
+ module HQMF2
2
+ # Used to represent 'any value' in criteria that require a value be present but
3
+ # don't specify any restrictions on that value
4
+ class AnyValue
5
+ attr_reader :type
6
+
7
+ def initialize(type='ANYNonNull')
8
+ @type = type
9
+ end
10
+
11
+ def to_model
12
+ HQMF::AnyValue.new(@type)
13
+ end
14
+ end
15
+
16
+ # Represents a bound within a HQMF pauseQuantity, has a value, a unit and an
17
+ # inclusive/exclusive indicator
18
+ class Value
19
+ include HQMF2::Utilities
20
+
21
+ attr_reader :type, :unit, :value
22
+
23
+ def initialize(entry, default_type='PQ')
24
+ @entry = entry
25
+ @type = attr_val('./@xsi:type') || default_type
26
+ @unit = attr_val('./@unit')
27
+ @value = attr_val('./@value')
28
+ end
29
+
30
+ def inclusive?
31
+ case attr_val('./@inclusive')
32
+ when 'false'
33
+ false
34
+ else
35
+ true
36
+ end
37
+ end
38
+
39
+ def derived?
40
+ case attr_val('./@nullFlavor')
41
+ when 'DER'
42
+ true
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def expression
49
+ if !derived?
50
+ nil
51
+ else
52
+ attr_val('./cda:expression/@value')
53
+ end
54
+ end
55
+
56
+ def to_model
57
+ HQMF::Value.new(type,unit,value,inclusive?,derived?,expression)
58
+ end
59
+ end
60
+
61
+ # Represents a HQMF physical quantity which can have low and high bounds
62
+ class Range
63
+ include HQMF2::Utilities
64
+ attr_accessor :low, :high, :width
65
+
66
+ def initialize(entry, type=nil)
67
+ @type = type
68
+ @entry = entry
69
+ if @entry
70
+ @low = optional_value('./cda:low', default_bounds_type)
71
+ @high = optional_value('./cda:high', default_bounds_type)
72
+ @width = optional_value('./cda:width', 'PQ')
73
+ end
74
+ end
75
+
76
+ def type
77
+ @type || attr_val('./@xsi:type')
78
+ end
79
+
80
+ def to_model
81
+ lm = low ? low.to_model : nil
82
+ hm = high ? high.to_model : nil
83
+ wm = width ? width.to_model : nil
84
+ HQMF::Range.new(type, lm, hm, wm)
85
+ end
86
+
87
+ private
88
+
89
+ def optional_value(xpath, type)
90
+ value_def = @entry.at_xpath(xpath, HQMF2::Document::NAMESPACES)
91
+ if value_def
92
+ Value.new(value_def, type)
93
+ else
94
+ nil
95
+ end
96
+ end
97
+
98
+ def default_bounds_type
99
+ case type
100
+ when 'IVL_TS'
101
+ 'TS'
102
+ else
103
+ 'PQ'
104
+ end
105
+ end
106
+ end
107
+
108
+ # Represents a HQMF effective time which is a specialization of a interval
109
+ class EffectiveTime < Range
110
+ def initialize(entry)
111
+ super
112
+ end
113
+
114
+ def type
115
+ 'IVL_TS'
116
+ end
117
+ end
118
+
119
+ # Represents a HQMF CD value which has a code and codeSystem
120
+ class Coded
121
+ include HQMF2::Utilities
122
+
123
+ def initialize(entry)
124
+ @entry = entry
125
+ end
126
+
127
+ def type
128
+ attr_val('./@xsi:type') || 'CD'
129
+ end
130
+
131
+ def system
132
+ attr_val('./@codeSystem')
133
+ end
134
+
135
+ def code
136
+ attr_val('./@code')
137
+ end
138
+
139
+ def code_list_id
140
+ attr_val('./@valueSet')
141
+ end
142
+
143
+ def title
144
+ attr_val('./@displayName')
145
+ end
146
+
147
+ def value
148
+ code
149
+ end
150
+
151
+ def derived?
152
+ false
153
+ end
154
+
155
+ def unit
156
+ nil
157
+ end
158
+
159
+ def to_model
160
+ HQMF::Coded.new(type, system, code, code_list_id, title)
161
+ end
162
+
163
+ end
164
+
165
+ class SubsetOperator
166
+ include HQMF2::Utilities
167
+
168
+ attr_reader :type, :value
169
+
170
+ def initialize(entry)
171
+ @entry = entry
172
+ @type = attr_val('./cda:subsetCode/@code')
173
+ value_def = @entry.at_xpath('./*/cda:repeatNumber', HQMF2::Document::NAMESPACES)
174
+ if value_def
175
+ @value = HQMF2::Range.new(value_def, 'IVL_INT')
176
+ end
177
+ end
178
+
179
+ def to_model
180
+ vm = value ? value.to_model : nil
181
+ HQMF::SubsetOperator.new(type, vm)
182
+ end
183
+ end
184
+
185
+ class TemporalReference
186
+ include HQMF2::Utilities
187
+
188
+ attr_reader :type, :reference, :range
189
+
190
+ def initialize(entry)
191
+ @entry = entry
192
+ @type = attr_val('./@typeCode')
193
+ @reference = Reference.new(@entry.at_xpath('./*/cda:id', HQMF2::Document::NAMESPACES))
194
+ range_def = @entry.at_xpath('./cda:pauseQuantity', HQMF2::Document::NAMESPACES)
195
+ if range_def
196
+ @range = HQMF2::Range.new(range_def, 'IVL_PQ')
197
+ end
198
+ end
199
+
200
+ def to_model
201
+ rm = range ? range.to_model : nil
202
+ HQMF::TemporalReference.new(type, reference.to_model, rm)
203
+ end
204
+ end
205
+
206
+ # Represents a HQMF reference from a precondition to a data criteria
207
+ class Reference
208
+ include HQMF2::Utilities
209
+
210
+ def initialize(entry)
211
+ @entry = entry
212
+ end
213
+
214
+ def id
215
+ attr_val('./@extension')
216
+ end
217
+
218
+ def to_model
219
+ HQMF::Reference.new(id)
220
+ end
221
+ end
222
+
223
+ end
@@ -0,0 +1,30 @@
1
+ module HQMF2
2
+ module Utilities
3
+
4
+ include HQMF::Conversion::Utilities
5
+
6
+ # Utility function to handle optional attributes
7
+ # @param xpath an XPath that identifies an XML attribute
8
+ # @return the value of the attribute or nil if the attribute is missing
9
+ def attr_val(xpath)
10
+ Utilities::attr_val(@entry, xpath)
11
+ end
12
+
13
+ # Utility function to handle optional attributes
14
+ # @param xpath an XPath that identifies an XML attribute
15
+ # @return the value of the attribute or nil if the attribute is missing
16
+ def self.attr_val(node, xpath)
17
+ attr = node.at_xpath(xpath, HQMF2::Document::NAMESPACES)
18
+ if attr
19
+ attr.value
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ def to_xml
26
+ @entry.to_xml
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,254 @@
1
+ module HQMF
2
+ # Class representing an HQMF document
3
+ class DataCriteriaConverter
4
+
5
+ attr_reader :v1_data_criteria_by_id, :v2_data_criteria, :v2_data_criteria_to_delete, :measure_period_criteria, :measure_period_v1_keys, :specific_occurrences
6
+
7
+ def initialize(doc, measure_period)
8
+ @doc = doc
9
+ @v1_data_criteria_by_id = {}
10
+ @v2_data_criteria = []
11
+ @v2_data_criteria_to_delete = {}
12
+ @specific_occurrences = {}
13
+ @measure_period = measure_period
14
+ parse()
15
+ end
16
+
17
+ def final_v2_data_criteria
18
+ @v2_data_criteria.delete_if {|criteria| @v2_data_criteria_to_delete[criteria.id] }
19
+ end
20
+
21
+ # duplicates a data criteria. This is important because we may be modifying source data criteria like patient characteristic birthdate to add restrictions
22
+ # the restrictions added may be different for the numerator, denominator, different IPP_1, IPP_2, etc.
23
+ def duplicate_data_criteria(data_criteria, parent_id)
24
+
25
+ # if this is a specific occurrence, then we do not want to duplicate it.
26
+ # we may need to duplicate it for a population however.
27
+ # return data_criteria if (specific_occurrences[data_criteria.id])
28
+
29
+ if (data_criteria.is_a? HQMF::Converter::SimpleDataCriteria and data_criteria.precondition_id == parent_id)
30
+ new_data_criteria = data_criteria
31
+ else
32
+ new_data_criteria = HQMF::Converter::SimpleDataCriteria.from_data_criteria(data_criteria)
33
+ new_data_criteria.assign_precondition(parent_id)
34
+ @v2_data_criteria << new_data_criteria
35
+ # we want to delete the original for data criteria that have been duplicated
36
+ @v2_data_criteria_to_delete[data_criteria.id] = true if !@v2_data_criteria_to_delete.keys.include? data_criteria.id
37
+ end
38
+
39
+ new_data_criteria
40
+ end
41
+
42
+ # make sure that if a data criteria is used as a target, that it is not deleted by someone else.
43
+ # this is required for birthdate in NQF0106
44
+ def validate_not_deleted(target)
45
+ @v2_data_criteria_to_delete[target] = false
46
+ end
47
+
48
+ # grouping data criteria are used to allow a single reference off of a temporal reference or subset operator
49
+ # grouping data criteria can reference either regular data criteria as children, or other grouping data criteria
50
+ def create_group_data_criteria(preconditions, type, value, parent_id, id, standard_category, qds_data_type)
51
+ extract_group_data_criteria_tree(HQMF::DataCriteria::UNION,preconditions, type, parent_id)
52
+ end
53
+
54
+ def build_group_data_criteria(children, section, parent_id, derivation_operator)
55
+
56
+ criteria_ids = children.map(&:id)
57
+ # make sure nobody else is going to delete the criteria we've grouped
58
+ criteria_ids.each {|target| validate_not_deleted(target)}
59
+
60
+ id = "#{parent_id}_#{section}_#{@@ids.next}"
61
+ title = "#{id}"
62
+ description = ""
63
+ definition = 'derived'
64
+ _display_name,_code_list_id,_status,_value,_field_values,_effective_time,_inline_code_list,_negation_code_list_id, = nil
65
+ _negation = false
66
+
67
+ group_criteria = HQMF::DataCriteria.new(id, title, _display_name, description, _code_list_id, criteria_ids, derivation_operator, definition, _status,
68
+ _value, _field_values, _effective_time, _inline_code_list,_negation,_negation_code_list_id,nil,nil,nil,nil)
69
+
70
+ @v2_data_criteria << group_criteria
71
+
72
+ group_criteria
73
+
74
+ end
75
+
76
+ # pull the children data criteria out of a set of preconditions
77
+ def extract_group_data_criteria_tree(conjunction, preconditions, type, parent_id)
78
+
79
+ children = []
80
+ preconditions.each do |precondition|
81
+ if (precondition.comparison?)
82
+ if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
83
+ children << measure_period_criteria
84
+ else
85
+ children << v2_data_criteria_by_id[precondition.reference.id]
86
+ end
87
+ else
88
+ converted_conjunction = convert_grouping_conjunction(precondition.conjunction_code)
89
+ children << extract_group_data_criteria_tree(converted_conjunction, precondition.preconditions, type, parent_id)
90
+ end
91
+ end
92
+
93
+ # if we have just one child element, just return it. An AND or OR of a single item is not useful.
94
+ if (children.size > 1)
95
+ build_group_data_criteria(children, type, parent_id, conjunction)
96
+ else
97
+ children.first
98
+ end
99
+
100
+ end
101
+
102
+ def convert_grouping_conjunction(conjunction)
103
+ case conjunction
104
+ when HQMF::Precondition::AT_LEAST_ONE_TRUE
105
+ HQMF::DataCriteria::UNION
106
+ when HQMF::Precondition::ALL_TRUE
107
+ HQMF::DataCriteria::XPRODUCT
108
+ else
109
+ 'unknown'
110
+ end
111
+ end
112
+
113
+ # pull the children data criteria out of a set of preconditions
114
+ def self.extract_data_criteria(preconditions, data_criteria_converter)
115
+ flattened = []
116
+ preconditions.each do |precondition|
117
+ if (precondition.comparison?)
118
+ if (precondition.reference.id == HQMF::Document::MEASURE_PERIOD_ID)
119
+ flattened << data_criteria_converter.measure_period_criteria
120
+ else
121
+ flattened << data_criteria_converter.v2_data_criteria_by_id[precondition.reference.id]
122
+ end
123
+ else
124
+ flattened.concat(extract_data_criteria(precondition.preconditions,data_criteria_converter))
125
+ end
126
+ end
127
+ flattened
128
+ end
129
+
130
+ def v2_data_criteria_by_id
131
+ criteria_by_id = {}
132
+ @v2_data_criteria.each do |criteria|
133
+ criteria_by_id[criteria.id] = criteria
134
+ end
135
+ criteria_by_id
136
+ end
137
+
138
+ private
139
+
140
+ def parse()
141
+ @doc[:data_criteria].each do |key,criteria|
142
+ parsed_criteria = HQMF::DataCriteriaConverter.convert(key, criteria)
143
+ @v2_data_criteria << parsed_criteria
144
+ @v1_data_criteria_by_id[criteria[:id]] = parsed_criteria
145
+ @specific_occurrences[parsed_criteria.id] = criteria[:derived_from] != nil
146
+ end
147
+ create_measure_period_v1_data_criteria(@doc,@measure_period,@v1_data_criteria_by_id)
148
+ end
149
+
150
+ def self.convert(key, criteria)
151
+
152
+ # @param [String] id
153
+ # @param [String] title
154
+ # @param [String] standard_category
155
+ # @param [String] qds_data_type
156
+ # @param [String] subset_code
157
+ # @param [String] code_list_id
158
+ # @param [String] property
159
+ # @param [String] type
160
+ # @param [String] status
161
+ # @param [boolean] negation
162
+ # @param [String] negation_code_list_id
163
+ # @param [Value|Range|Coded] value
164
+ # @param [Range] effective_time
165
+ # @param [Hash<String,String>] inline_code_list
166
+
167
+ id = convert_key(key)
168
+ title = criteria[:title]
169
+ title = title.match(/.*:\s+(.+)/)[1]
170
+ description = criteria[:description]
171
+ code_list_id = criteria[:code_list_id]
172
+ definition = criteria[:definition]
173
+ status = criteria[:status]
174
+ negation = criteria[:negation]
175
+ negation_code_list_id = criteria[:negation_code_list_id]
176
+ specific_occurrence = criteria[:specific_occurrence]
177
+ specific_occurrence_const = criteria[:specific_occurrence_const]
178
+
179
+ value = nil # value is filled out by backfill_patient_characteristics for things like gender and by REFR restrictions
180
+ effective_time = nil # filled out by temporal reference code
181
+ temporal_references = # filled out by operator code
182
+ subset_operators = nil # filled out by operator code
183
+ children_criteria = nil # filled out by operator and temporal reference code
184
+ derivation_operator = nil # filled out by operator and temporal reference code
185
+ negation_code_list_id = nil # filled out by RSON restrictions
186
+ field_values = nil # field values are filled out by SUBJ and REFR restrictions
187
+ inline_code_list = nil # inline code list is only used in HQMF V2, so we can just pass in nil
188
+ display_name=nil
189
+
190
+ HQMF::DataCriteria.new(id, title, display_name, description, code_list_id, children_criteria, derivation_operator, definition, status,
191
+ value, field_values, effective_time, inline_code_list, negation, negation_code_list_id, temporal_references, subset_operators,specific_occurrence,specific_occurrence_const)
192
+
193
+ end
194
+
195
+
196
+ # this method creates V1 data criteria for the measurement period. These data criteria can be
197
+ # referenced properly within the restrictions
198
+ def create_measure_period_v1_data_criteria(doc,measure_period,v1_data_criteria_by_id)
199
+
200
+ attributes = doc[:attributes]
201
+ attributes.keys.each {|key| attributes[key.to_s] = attributes[key]}
202
+
203
+ measure_period_key = attributes['MEASUREMENT_PERIOD'][:id]
204
+ measure_start_key = attributes['MEASUREMENT_START_DATE'][:id]
205
+ measure_end_key = attributes['MEASUREMENT_END_DATE'][:id]
206
+
207
+ @measure_period_v1_keys = {measure_start: measure_start_key, measure_end: measure_end_key, measure_period: measure_period_key}
208
+
209
+ type = 'variable'
210
+ code_list_id,negation_code_list_id,property,status,field_values,effective_time,inline_code_list,children_criteria,derivation_operator,temporal_references,subset_operators=nil
211
+
212
+ #####
213
+ ##
214
+ ######### SET MEASURE PERIOD
215
+ ##
216
+ #####
217
+
218
+ measure_period_id = HQMF::Document::MEASURE_PERIOD_ID
219
+ value = measure_period
220
+ measure_criteria = HQMF::DataCriteria.new(measure_period_id,measure_period_id,nil,measure_period_id,code_list_id,children_criteria,derivation_operator,measure_period_id,status,
221
+ value,field_values,effective_time,inline_code_list, false, nil, temporal_references,subset_operators,nil,nil)
222
+
223
+ # set the measure period data criteria for all measure period keys
224
+ v1_data_criteria_by_id[measure_period_key] = measure_criteria
225
+ v1_data_criteria_by_id[measure_start_key] = measure_criteria
226
+ v1_data_criteria_by_id[measure_end_key] = measure_criteria
227
+ @measure_period_criteria = measure_criteria
228
+
229
+ end
230
+
231
+
232
+ def self.title_from_description(title, description)
233
+ title.gsub(/^#{Regexp.escape(description).gsub('\\ ',':?,?\\ ')}:\s*/i,'')
234
+ end
235
+
236
+ def self.convert_key(key)
237
+ key.to_s.downcase.gsub('_', ' ').split(' ').map {|w| w.capitalize }.join('')
238
+ end
239
+
240
+ # Simple class to issue monotonically increasing integer identifiers
241
+ class Counter
242
+ def initialize
243
+ @count = 0
244
+ end
245
+
246
+ def next
247
+ @count+=1
248
+ end
249
+ end
250
+ @@ids = Counter.new
251
+
252
+
253
+ end
254
+ end