test-patient-generator 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,5 @@
1
- require 'set'
2
-
3
1
  module HQMF
4
- # Contains functions that can be used to randomly generate fields for patients.
5
- # Also includes utility functions for randomly generating numbers and dates or choosing options within a range.
6
2
  class Randomizer
7
- UNIQUE_PATIENT_IDS = Set.new # Used to make sure we don't duplicate randomly generated patient IDs
8
-
9
3
  # Add trivial demographics info to a Record. Trivial fields are ones that identify a patient as unique to a human eye
10
4
  # but have no impact on CQM, e.g. address. This is essentially an upsert, so any preexisting info will be wiped.
11
5
  #
@@ -21,7 +15,7 @@ module HQMF
21
15
  patient.languages << randomize_language
22
16
  patient.first = randomize_first_name(patient.gender) if patient.gender
23
17
  patient.last = randomize_last_name
24
- patient.medical_record_number = randomize_patient_id
18
+ patient.medical_record_number = Digest::MD5.hexdigest("#{patient.first} #{patient.last}")
25
19
 
26
20
  patient
27
21
  end
@@ -34,10 +28,10 @@ module HQMF
34
28
  # Black persons 12.6%
35
29
  # Hispanic 16.3%
36
30
  # White 63.7%
37
- def self.randomize_race_and_ethnicity
38
- race_percent = rand(999)
31
+ def self.randomize_race_and_ethnicity(percent = nil)
32
+ percent ||= rand(999)
39
33
 
40
- case race_percent
34
+ case percent
41
35
  when 0..1
42
36
  {race: '2076-8', ethnicity: '2186-5'} # pacific islander
43
37
  when 2..10
@@ -73,11 +67,11 @@ module HQMF
73
67
  # 00.1% persian
74
68
  # 00.1% us sign
75
69
  # 03.0% other
76
- def self.randomize_language
77
- language_percent = rand(999)
70
+ def self.randomize_language(percent = nil)
71
+ percent ||= rand(999)
78
72
 
79
- case language_percent
80
- when 0..802
73
+ case percent
74
+ when 0..802
81
75
  'en-US' # english
82
76
  when 802..925
83
77
  'es-US' # spanish
@@ -175,26 +169,17 @@ module HQMF
175
169
  'postalCode' => zip
176
170
  }.to_json
177
171
  end
178
-
179
- # Randomize patient IDs for a given patients. We ensure that this ID is unique.
180
- #
181
- # @return The same patient with a random, non-duplicate ID assigned.
182
- def self.randomize_patient_id
183
- # Keep trying to add a new ID to the set. Return the ID when we find one that's unique.
184
- loop do
185
- id = (0...10).map{ ('0'..'9').to_a[rand(10)] }.join.to_s
186
- break id if UNIQUE_PATIENT_IDS.size < UNIQUE_PATIENT_IDS.add(id).size
187
- end
188
- end
189
172
 
190
173
  # More accurately, randomize a believable birthdate. Given a patient, find all coded entries that
191
174
  # have age-related implications and make sure the patient is at least that old. Since that is complicated,
192
- # for now let's just make them 45
175
+ # for now let's just make them 40
193
176
  #
194
177
  # @param A patient with coded entries that dictate potential birthdates
195
178
  # @return A realistic birthdate for the given patient
196
179
  def self.randomize_birthdate(patient)
197
- Time.now
180
+ now = Time.now
181
+ range = randomize_range(now.advance(years: -40), now.advance(years: -35))
182
+ range.low.to_time_object
198
183
  end
199
184
 
200
185
  # Randomly generate a Range object that is within a lower and upper bounds. It is guaranteed that the high of the
@@ -202,12 +187,14 @@ module HQMF
202
187
  #
203
188
  # @param [Time] earliest_time The lowest possible value for the low in this Range. nil implies unbounded.
204
189
  # @param [Time] latest_time The highest possible value for the high in this Range. nil implies unbounded.
190
+ # @param [Hash] maximum_length Optionally used to bound the length of a range. This is a hash that can be passed as a parameter to Time.advance.
205
191
  # @return A Range that represents a start time and end time within the acceptable bounds supplied.
206
- def self.randomize_range(earliest_time, latest_time)
207
- earliest_time ||= Time.now.advance(years: -18)
208
- latest_time ||= Time.now.to_i
192
+ def self.randomize_range(earliest_time, latest_time, maximum_length = nil)
193
+ earliest_time ||= Time.now.advance(years: -35)
194
+ latest_time ||= Time.now.advance(days: -1)
209
195
 
210
196
  low = Time.at(Randomizer.between(earliest_time.to_i, latest_time.to_i))
197
+ latest_time = low.advance(maximum_length) if maximum_length
211
198
  high = Time.at(Randomizer.between(low, latest_time.to_i))
212
199
 
213
200
  low = Value.new("TS", nil, Value.time_to_ts(low), true, false, false)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-patient-generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,88 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-15 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-12-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: health-data-standards
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: hquery-patient-api
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: hqmf-parser
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: hqmf2js
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.1.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.1.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: qrda_generator
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.0.1
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.0.1
14
94
  description: A utility to generate patients for unit testing clinical quality measure
15
95
  logic. The instructions for generation are guided by HQMF documents and exported
16
96
  into various health standards, e.g. C32, CCR.
@@ -21,15 +101,9 @@ extra_rdoc_files: []
21
101
  files:
22
102
  - lib/test-patient-generator.rb
23
103
  - lib/tpg/ext/coded.rb
24
- - lib/tpg/ext/conjunction.rb
25
104
  - lib/tpg/ext/data_criteria.rb
26
- - lib/tpg/ext/derivation_operator.rb
27
- - lib/tpg/ext/population_criteria.rb
28
- - lib/tpg/ext/precondition.rb
29
105
  - lib/tpg/ext/range.rb
30
106
  - lib/tpg/ext/record.rb
31
- - lib/tpg/ext/subset_operator.rb
32
- - lib/tpg/ext/temporal_reference.rb
33
107
  - lib/tpg/ext/value.rb
34
108
  - lib/tpg/generation/exporter.rb
35
109
  - lib/tpg/generation/generator.rb
@@ -1,23 +0,0 @@
1
- module Conjunction
2
- module AllTrue
3
- #
4
- #
5
- # @param [Array] base_patients
6
- # @return
7
- def generate(base_patients)
8
- preconditions.each do |precondition|
9
- precondition.generate(base_patients)
10
- end
11
- end
12
- end
13
-
14
- #
15
- #
16
- # @param [Array] base_patients
17
- # @return
18
- module AtLeastOneTrue
19
- def generate(base_patients)
20
- preconditions.sample.generate(base_patients)
21
- end
22
- end
23
- end
@@ -1,49 +0,0 @@
1
- module HQMF
2
- class DerivationOperator
3
- # Perform an intersection between two sets of Ranges (assuming these are timestamps).
4
- #
5
- # @param [Array] set1 One array of Ranges to be intersected.
6
- # @param [Array] set2 The other array of Ranges to be intersected.
7
- # @return A new array that contains the shared Ranges between set1 and set2.
8
- def self.intersection(set1, set2)
9
- # Special cases to account for emptiness
10
- return [] if set1.empty? && set2.empty?
11
- return set1 if set2.empty?
12
- return set2 if set1.empty?
13
-
14
- # Merge each element of the two sets together
15
- result = []
16
- set1.each do |range1|
17
- set2.each do |range2|
18
- intersect = range1.intersection(range2)
19
- result << intersect unless intersect.nil?
20
- end
21
- end
22
-
23
- result
24
- end
25
-
26
- # Perform a union between two sets of Ranges (assuming these are timestamps)
27
- #
28
- # @param [Array] set1 One array of Ranges to be unioned.
29
- # @param [Array] set2 The other array of Ranges to be unioned.
30
- # @return A new array tha contains the union of Ranges between set1 and set2.
31
- def self.union(set1, set2)
32
- # Special cases to account for emptiness
33
- return [] if set1.empty? && set2.empty?
34
- return set1 if set2.empty?
35
- return set2 if set1.empty?
36
-
37
- # Join each element of the two sets together
38
- result = []
39
- set1.each do |range1|
40
- set2.each do |range2|
41
- union = range1.union(range2)
42
- result.concat!(union)
43
- end
44
- end
45
-
46
- result
47
- end
48
- end
49
- end
@@ -1,12 +0,0 @@
1
- module HQMF
2
- class PopulationCriteria
3
- #
4
- #
5
- # @param [Array] base_patients
6
- # @return
7
- def generate(base_patients)
8
- # All population criteria begin with a single conjunction precondition
9
- preconditions.first.generate(base_patients)
10
- end
11
- end
12
- end
@@ -1,23 +0,0 @@
1
- module HQMF
2
- class Precondition
3
- #
4
- #
5
- # @param [Array] base_patients
6
- # @return
7
- def generate(base_patients)
8
- # d = HQMF::Generator.hqmf.data_criteria(preconditions[2].preconditions[1].preconditions.first.reference.id)
9
-
10
- if conjunction?
11
- # Include the matching module to override our generation functions
12
- conjunction_module = "Conjunction::#{self.conjunction_code.classify}"
13
- conjunction_module = conjunction_module.split('::').inject(Kernel) {|scope, name| scope.const_get(name)}
14
-
15
- extend conjunction_module
16
- generate(base_patients)
17
- elsif reference
18
- data_criteria = HQMF::Generator.hqmf.data_criteria(reference.id)
19
- data_criteria.generate(base_patients)
20
- end
21
- end
22
- end
23
- end
@@ -1,7 +0,0 @@
1
- module HQMF
2
- class SubsetOperator
3
- def generate(base_patients)
4
-
5
- end
6
- end
7
- end
@@ -1,66 +0,0 @@
1
- module HQMF
2
- # Generates a Range to define the timing of a data_criteria
3
- class TemporalReference
4
- #
5
- #
6
- # @param [Array] base_patients
7
- # @return
8
- def generate(base_patients)
9
- if reference.id == "MeasurePeriod"
10
- matching_time = Generator::hqmf.measure_period.clone
11
- else
12
- # First generate patients for the data criteria that this temporal reference points to
13
- data_criteria = Generator::hqmf.data_criteria(reference.id)
14
- base_patients = data_criteria.generate(base_patients)
15
-
16
- # Now that the data criteria is defined, we can set our relative time to those generated results
17
- matching_time = data_criteria.generation_range.first.clone
18
- end
19
-
20
- # TODO add nils where necessary. Ranges should be unbounded, despite the relative time's potential bounds (unless the type specifies)
21
- if range
22
- offset = range.try(:clone)
23
-
24
- case type
25
- when "DURING"
26
- # TODO differentiate between this and CONCURRENT
27
- when "SBS" # Starts before start
28
- offset.low.value.insert(0, "-")
29
- when "SAS" # Starts after start
30
- offset.low = offset.high
31
- offset.high = nil
32
- when "SBE" # Starts before end
33
- offset.low = offset.high
34
- offset.high = nil
35
- offset.low.value.insert(0, "-")
36
- matching_time.low = matching_time.high
37
- when "SAE" # Starts after end
38
-
39
- when "EBS" # Ends before start
40
-
41
- when "EAS" # Ends after start
42
-
43
- when "EBE" # Ends before end
44
-
45
- when "EAE" # Ends after end
46
-
47
- when "SDU" # Starts during
48
- matching_time.high.value = nil
49
- when "EDU" # Ends during
50
-
51
- when "ECW" # Ends concurrent with
52
-
53
- when "SCW" # Starts concurrent with
54
-
55
- when "CONCURRENT"
56
-
57
- end
58
-
59
- matching_time = Range.merge_ranges(offset, matching_time)
60
- end
61
-
62
- # Note we return the possible times to the calling data criteria, not patients
63
- [matching_time]
64
- end
65
- end
66
- end