ez7gen 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/ez7gen +6 -0
- data/lib/ez7gen.rb +23 -0
- data/lib/ez7gen/config/schema/2.4/2.4.HL7.xml +13904 -0
- data/lib/ez7gen/config/schema/2.4/VAZ2.4.HL7.xml +3085 -0
- data/lib/ez7gen/config/schema/2.4/added/coded-tables.xml +730 -0
- data/lib/ez7gen/config/schema/2.4/rules/2.4.HL7.yml +4 -0
- data/lib/ez7gen/config/schema/2.4/rules/VAZ2.4.HL7.yml +6 -0
- data/lib/ez7gen/config/schema/2.5/2.5.HL7.xml +10008 -0
- data/lib/ez7gen/config/schema/2.5/VAZ2.5.HL7.xml +7 -0
- data/lib/ez7gen/config/schema/2.5/added/coded-tables.xml +549 -0
- data/lib/ez7gen/config/schema/readme.txt +0 -0
- data/lib/ez7gen/config/templates/2.4/eiv table update-mfn_m01 20151201.xml +416 -0
- data/lib/ez7gen/config/templates/2.4/eiv table update-mfn_y01.xml +416 -0
- data/lib/ez7gen/config/templates/2.4/eiv-ec-MFN_X01_reg request 20160126.xml +659 -0
- data/lib/ez7gen/config/templates/2.4/examples/ADT_A60.txt +69 -0
- data/lib/ez7gen/config/templates/2.4/examples/eiv table update-mfn_m01 20151201.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_dss_units-query_qbp_q13-qbp_q13.txt +26 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_ecs_procedures_query_qbp_q13-qbp_q13.txt +26 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_patient eligibility_response-rsp_k11-080714.txt +44 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_diagnosis_query_qbp_q11-qbp_q11.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_diagnosis_response_rsp_k11-rsp_k11.txt +42 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_dss_units_response_rtb_k13-rtb_k13.txt +49 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_ecs_filer_request_dft_p03-dft_p03-080714.txt +31 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_ecs_filer_response_ack_p03-ack_p03.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_esc_procedures_response_rtb_k13-rtb_k13.txt +40 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patient_eclass_query_qbp_q11-qbp_q11.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patient_problems_query_qbp_q11-qbp_q11.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patinet_problems_response_rsp_k11-rsp_k11.txt +33 -0
- data/lib/ez7gen/config/templates/2.4/examples/orur01rvbecv2.txt +31 -0
- data/lib/ez7gen/config/templates/2.4/examples/sqwm vitals-oru_ro1.txt +52 -0
- data/lib/ez7gen/config/templates/2.4/examples/vista sqwm-adt_a60.txt +40 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_dss_units_query_qbp_q13-qbp_q13.xml +312 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_ecs_procedures_query_qbp_q13-qbp_q13.xml +314 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_patient eligibility_response-rsp_k11-080714.xml +640 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_diagnosis_query_qbp_q11-qbp_q11.xml +284 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_diagnosis_response_rsp_k11-rsp_k11-rsp_k11.xml +563 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_dss_units_response_rtb_k13-rtb_k13-rtb_k13.xml +365 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_filer_request_dft_p03-dft_p03-080714.xml +2172 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_filer_response_ack_p03-ack_p03.xml +269 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_procedures_response_rtb_k13-rtb_k13-rtb_k13.xml +354 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_eclass_query_qbp_q11-qbp_q11.xml +284 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_problems_query_qbp_q11-qbp_q11.xml +282 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_problems_response_rsp_k11-rsp_k11-rsp_k11.xml +565 -0
- data/lib/ez7gen/config/templates/2.4/orur01rvbecv2.xml +1529 -0
- data/lib/ez7gen/config/templates/2.4/sqwm vitals-oru_r01.xml +2975 -0
- data/lib/ez7gen/config/templates/2.4/vista sqwm-adt_a60.xml +1360 -0
- data/lib/ez7gen/message_factory.rb +142 -0
- data/lib/ez7gen/msg_error_handler.rb +33 -0
- data/lib/ez7gen/profile_parser.rb +321 -0
- data/lib/ez7gen/resources/properties-with-comments.yml +51 -0
- data/lib/ez7gen/resources/properties.yml +325 -0
- data/lib/ez7gen/service/2.4/dynamic_field_generator.rb +45 -0
- data/lib/ez7gen/service/2.4/field_generator.rb +1586 -0
- data/lib/ez7gen/service/2.5/field_generator.rb +75 -0
- data/lib/ez7gen/service/base_field_generator.rb +451 -0
- data/lib/ez7gen/service/segment_generator.rb +218 -0
- data/lib/ez7gen/service/segment_picker.rb +147 -0
- data/lib/ez7gen/service/template_generator.rb +213 -0
- data/lib/ez7gen/service/type_aware_field_generator.rb +1583 -0
- data/lib/ez7gen/service/utils.rb +75 -0
- data/lib/ez7gen/structure_parser.rb +331 -0
- data/lib/ez7gen/version.rb +38 -0
- data/test/Additional Tables with values_v1.1.txt +1653 -0
- data/test/added_shema_test.rb +143 -0
- data/test/app-tmp.rb +225 -0
- data/test/at.txt +1 -0
- data/test/backburner.zip +0 -0
- data/test/codes.txt +262 -0
- data/test/codes1.txt +1240 -0
- data/test/data_types_exploration_test.rb +213 -0
- data/test/dynamic_field_generated_test.rb +292 -0
- data/test/message_factory_24_custom_test.rb +648 -0
- data/test/message_factory_25_test.rb +50 -0
- data/test/message_factory_adm_test.rb +558 -0
- data/test/message_factory_gen_test.rb +63 -0
- data/test/message_factory_lab_test.rb +107 -0
- data/test/message_factory_pharm_test.rb +121 -0
- data/test/message_factory_template_24_test.rb +730 -0
- data/test/message_factory_test.rb +220 -0
- data/test/msg_error_handler_test.rb +59 -0
- data/test/profile_parser_test.rb +542 -0
- data/test/quick_run.rb +880 -0
- data/test/segment_generator_test.rb +656 -0
- data/test/segment_picker_test.rb +279 -0
- data/test/structrure_parser_test.rb +355 -0
- data/test/template_generator_test.rb +164 -0
- data/test/type_aware_field_generator_test.rb +582 -0
- data/test/utils_test.rb +97 -0
- metadata +215 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# require 'yaml'
|
|
2
|
+
require_relative '../../profile_parser'
|
|
3
|
+
require_relative '../base_field_generator'
|
|
4
|
+
|
|
5
|
+
class FieldGenerator < BaseFieldGenerator
|
|
6
|
+
include Utils
|
|
7
|
+
|
|
8
|
+
# constructor
|
|
9
|
+
def initialize(parser, helper_parser=nil)
|
|
10
|
+
super(parser, helper_parser)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# base data types ["DT", "FT", "ID", "IS", "NM", "SI", "ST", "TM", "TN", "TX"]
|
|
14
|
+
|
|
15
|
+
def dt (name, attrs)
|
|
16
|
+
dt = generate_dt(name, attrs)
|
|
17
|
+
|
|
18
|
+
if(dt.is_a?(Array))
|
|
19
|
+
dt = dt.map {|it| if(it.instance_of? (Array)) then it.join(@@SUB) else it end}.join(@@HAT)
|
|
20
|
+
# dt.join(@@HAT)
|
|
21
|
+
end
|
|
22
|
+
return dt
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def generate_dt(name, attrs, force=false)
|
|
26
|
+
#check if the field is optional and randomly generate it of skip
|
|
27
|
+
return '' if(!generate?(attrs, force))
|
|
28
|
+
# p "generate_dt : #{name}"
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
# value << method(sub_type[:datatype]).call(sub_type,[true, false].sample)
|
|
32
|
+
dt = method(name).call(attrs, true)
|
|
33
|
+
# p "generate_dt basic dt: #{name}"
|
|
34
|
+
|
|
35
|
+
rescue NameError => e
|
|
36
|
+
# puts e
|
|
37
|
+
# sub_values = dynamic(sub_type[:datatype], sub_type, [true, false].sample)
|
|
38
|
+
dt = generate_dt_by_name(name)
|
|
39
|
+
|
|
40
|
+
# TODO :remove trailing empty fields
|
|
41
|
+
#while( blank?(value.last)) do value.pop end
|
|
42
|
+
# value << value.join(@@SUB)
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
# puts dt
|
|
46
|
+
# return (dt.is_a?(Array))?dt.join(@@SUB) :dt
|
|
47
|
+
return dt
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Method to generate field using schema description
|
|
52
|
+
def generate_dt_by_name(name)
|
|
53
|
+
#p "generate_dt_by_name: #{name}, not a basic type"
|
|
54
|
+
$log.info("#{self.class.to_s}:#{__method__.to_s}") {"generate_dt_by_name: #{name}, not a basic type"}
|
|
55
|
+
|
|
56
|
+
dt = []
|
|
57
|
+
types = []
|
|
58
|
+
@pp.xml.Export.Document.Category.locate('DataType').select{|it| it.attributes[:name] == name}.first.locate('DataSubType').each{ |it| types << it.attributes}
|
|
59
|
+
|
|
60
|
+
# types.slice!(0, types.length/2)
|
|
61
|
+
# TODO: logic to build subtypes 1) look for Reqs, if not build # of sub-types, then build the rest
|
|
62
|
+
for type in types
|
|
63
|
+
# check if field is required
|
|
64
|
+
dt << ((generate?(type)) ? generate_dt(type[:datatype], type) : '')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# remove trailing empty elements
|
|
68
|
+
while( blank?(dt) and blank?(dt.last)) do
|
|
69
|
+
dt.pop
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
return dt
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require_relative '../profile_parser'
|
|
3
|
+
|
|
4
|
+
class BaseFieldGenerator
|
|
5
|
+
|
|
6
|
+
include Utils
|
|
7
|
+
|
|
8
|
+
attr_accessor :yml,:pp
|
|
9
|
+
# @@UP_TO_3_DGTS = 1000 # up to 3 digits
|
|
10
|
+
@@REQ_LEN_3_DGTS = 3 #up to 3 digits
|
|
11
|
+
@@RANGE_INDICATOR = '...'
|
|
12
|
+
@@HAT = '^' # Component separator, aka hat
|
|
13
|
+
@@SUB ='&' # Subcomponent separator
|
|
14
|
+
# @@MONEY_FORMAT_INDICATORS = ['Money', 'Balance', 'Charge', 'Adjustment', 'Income', 'Amount', 'Payment','Cost']
|
|
15
|
+
@@MONEY_FORMAT_REGEX = /\bMoney\b|\bBalance\b|\bCharge|\bAdjustment\b|\bIncome\b|\bAmount\b|\bPayment\b|\bCost\b|\bPercentage\b/
|
|
16
|
+
@@INITIALS = ('A'..'Z').to_a
|
|
17
|
+
@@GENERAL_TEXT = 'Notes'
|
|
18
|
+
|
|
19
|
+
@@random = Random.new
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# constructor
|
|
23
|
+
def initialize(parser, helper_parser=nil)
|
|
24
|
+
@pp = parser
|
|
25
|
+
# Coded table value lookup profiler. Only set for custom schemas.
|
|
26
|
+
# Custom schema has types which coming from the base schema but using coded tables from primary schema.
|
|
27
|
+
# Also primary schemas use base type coded tables.
|
|
28
|
+
@hp = helper_parser
|
|
29
|
+
|
|
30
|
+
# dirname = File.join(File.dirname(File.expand_path(__FILE__)),'../../resources/properties.yml')
|
|
31
|
+
propertiesFile = File.expand_path('../../resources/properties.yml', __FILE__)
|
|
32
|
+
@yml = YAML.load_file propertiesFile
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# base_dt = ["AD", "DT", "DTM", "FT", "GTS", "ID", "IS", "NM", "SI", "ST", "TM", "TX"]
|
|
36
|
+
#Generate HL7 AD (address) data type.
|
|
37
|
+
def AD(map, force=false)
|
|
38
|
+
#check if the field is optional and randomly generate it of skip
|
|
39
|
+
return '' if(!generate?(map, force))
|
|
40
|
+
|
|
41
|
+
# match cities, states and zips
|
|
42
|
+
sample = @yml['address.states'].sample
|
|
43
|
+
idx = @yml['address.states'].index(sample) #index of random element
|
|
44
|
+
|
|
45
|
+
val=[]
|
|
46
|
+
#street address (ST) (ST)
|
|
47
|
+
val << @yml['address.streetNames'].sample
|
|
48
|
+
#other designation (ST)
|
|
49
|
+
val <<''
|
|
50
|
+
#city (ST)
|
|
51
|
+
val << @yml['address.cities'].at(idx)
|
|
52
|
+
#state or province (ST)
|
|
53
|
+
val << @yml['address.states'].at(idx)
|
|
54
|
+
#zip or postal code (ST)
|
|
55
|
+
val << @yml['address.zips'].at(idx)
|
|
56
|
+
#country (ID)
|
|
57
|
+
val << @yml['address.countries'].at(idx)
|
|
58
|
+
# address type (ID)
|
|
59
|
+
# ot5 her geographic designation (ST)
|
|
60
|
+
|
|
61
|
+
val.join(@@HAT)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Generate HL7 CE (coded element) data type
|
|
66
|
+
def CE(map, force=false)
|
|
67
|
+
#check if the field is optional and randomly generate it of skip
|
|
68
|
+
return '' if(!generate?(map, force))
|
|
69
|
+
|
|
70
|
+
if(map[:max_length] && map[:max_length].to_i <3)
|
|
71
|
+
# if CE Element has lenght of 2 or less use only value
|
|
72
|
+
return ID(map, true)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
#TODO: Refactor this method
|
|
76
|
+
if (map[:description] == 'Role Action Reason' || map[:description] == 'Species Code' || map[:description] == 'Breed Code' || map[:description] == 'Production Class Code')
|
|
77
|
+
return '' #Per requirement, PID.35 – PID.38
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
val = []
|
|
81
|
+
# CE ce = (CE) map?.fld
|
|
82
|
+
codes = get_coded_map(map)
|
|
83
|
+
if(blank?(codes))
|
|
84
|
+
case map[:description]
|
|
85
|
+
when 'Allergen Code/Mnemonic/Description'
|
|
86
|
+
pair = yml['codes.allergens.icd10'].to_a.sample(1).to_h.first # randomly pick a pair
|
|
87
|
+
val<<pair.first
|
|
88
|
+
val<<pair.last
|
|
89
|
+
|
|
90
|
+
else
|
|
91
|
+
# TODO: only for elements that don't have look up table set the id randomly
|
|
92
|
+
# if codetable is empty
|
|
93
|
+
val << ((blank?(map[:codetable])) ? ID(map, true) : '')
|
|
94
|
+
end
|
|
95
|
+
else
|
|
96
|
+
#identifier (ST) (ST)
|
|
97
|
+
val<<codes[:value]
|
|
98
|
+
#text (ST)
|
|
99
|
+
val<<codes[:description]
|
|
100
|
+
#name of coding system (IS)
|
|
101
|
+
#alternate identifier (ST) (ST)
|
|
102
|
+
#alternate text (ST)
|
|
103
|
+
#name of alternate coding system (IS)
|
|
104
|
+
end
|
|
105
|
+
return val.join(@@HAT)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#Generates an HL7 DT (date) datatype.
|
|
109
|
+
def DTM(map, force=false)
|
|
110
|
+
#check if the field is optional and randomly generate it of skip
|
|
111
|
+
return '' if(!generate?(map, force))
|
|
112
|
+
|
|
113
|
+
is_year_only = (map[:max_length]) ? map[:max_length].to_i == 4 : false
|
|
114
|
+
# #time of an event (TSComponentOne)
|
|
115
|
+
(is_year_only)? to_datetime(map).strftime('%Y') : to_datetime(map).strftime('%Y%m%d') #format('YYYYMMdd.SSS')Date.iso8601
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
#Generates an HL7 DT (date) datatype.
|
|
119
|
+
def DT(map, force=false)
|
|
120
|
+
#check if the field is optional and randomly generate it of skip
|
|
121
|
+
return '' if(!generate?(map, force))
|
|
122
|
+
|
|
123
|
+
# #time of an event (TSComponentOne)
|
|
124
|
+
to_datetime(map).strftime('%Y%m%d') #format('YYYYMMdd.SSS')Date.iso8601
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
#Generates an HL7 FN (familiy name) data type.
|
|
128
|
+
def FN(map, force=false)
|
|
129
|
+
#check if the field is optional and randomly generate it of skip
|
|
130
|
+
return '' if(!generate?(map, force))
|
|
131
|
+
|
|
132
|
+
#surname (ST)
|
|
133
|
+
@yml['person.names.last'].sample
|
|
134
|
+
#own surname prefix (ST)
|
|
135
|
+
#own surname (ST)
|
|
136
|
+
#surname prefix from partner/spouse (ST)
|
|
137
|
+
#surname from partner/spouse (ST)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Formatted text data.
|
|
141
|
+
# The FT field is of arbitrary length (up to 64k) and may contain formatting commands enclosed in escape characters.
|
|
142
|
+
def FT(map, force=false)
|
|
143
|
+
#check if the field is optional and randomly generate it of skip
|
|
144
|
+
return '' if(!generate?(map, force))
|
|
145
|
+
|
|
146
|
+
ID(map, true)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#Generates an HL7 General Timing Specification.
|
|
150
|
+
def GTS(map, force=false)
|
|
151
|
+
#check if the field is optional and randomly generate it of skip
|
|
152
|
+
return '' if(!generate?(map, force))
|
|
153
|
+
|
|
154
|
+
DT(map, true)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Generate HL7 ID, usually using value from code table
|
|
158
|
+
def ID(map, force=false)
|
|
159
|
+
#check if the field is optional and randomly generate it of skip
|
|
160
|
+
return '' if(!generate?(map, force))
|
|
161
|
+
|
|
162
|
+
#value only
|
|
163
|
+
#Case when max_len overrides requirements
|
|
164
|
+
len = safe_len(map[:max_length], @@REQ_LEN_3_DGTS)
|
|
165
|
+
(!blank?(map[:codetable]))? get_coded_value(map): generate_length_bound_id(len)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
#Generates HL7 IS (namespace id) data type
|
|
169
|
+
def IS(map, force=false)
|
|
170
|
+
#check if the field is optional and randomly generate it of skip
|
|
171
|
+
return '' if(!generate?(map, force))
|
|
172
|
+
|
|
173
|
+
#TODO: same as ID?
|
|
174
|
+
ID(map,true)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
#Generates an HL7 NM (numeric) data type. A NM contains a single String value.
|
|
178
|
+
def NM(map, force=false)
|
|
179
|
+
#check if the field is optional and randomly generate it of skip
|
|
180
|
+
return '' if(!generate?(map, force))
|
|
181
|
+
val = 0
|
|
182
|
+
case map[:description]
|
|
183
|
+
when'Guarantor Household Size','Birth Order'
|
|
184
|
+
val = generate_length_bound_id(1)
|
|
185
|
+
when 'Guarantor Household Annual Income'
|
|
186
|
+
val = '%.2f' % generate_length_bound_id(5)
|
|
187
|
+
when @@MONEY_FORMAT_REGEX
|
|
188
|
+
val = '%.2f' % ID(map,true)
|
|
189
|
+
when /Date\/Time/
|
|
190
|
+
val = to_datetime(map)
|
|
191
|
+
else
|
|
192
|
+
val = ID(map,true) # general rule for a number
|
|
193
|
+
if (map[:datatype] == 'CP' || map[:datatype] == 'MO') # money
|
|
194
|
+
val = '%.2f' % val
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
return val
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Street address
|
|
202
|
+
# Appears in the XAD data type, and found in templates
|
|
203
|
+
def SAD(map, force=false)
|
|
204
|
+
#check if the field is optional and randomly generate it of skip
|
|
205
|
+
return '' if(!generate?(map, force))
|
|
206
|
+
|
|
207
|
+
# <street or mailing address (ST)>
|
|
208
|
+
@yml['address.streetNames'].sample
|
|
209
|
+
# <street name (ST)>
|
|
210
|
+
# <dwelling number (ST)>
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
#Generate HL7 SI (sequence ID) data type. A SI contains a single String value.
|
|
214
|
+
def SI(map, force=false)
|
|
215
|
+
#check if the field is optional and randomly generate it of skip
|
|
216
|
+
return '' if(!generate?(map, force))
|
|
217
|
+
|
|
218
|
+
#SI pt = (SI) map.fld
|
|
219
|
+
#pt.setValue(generate_length_bound_id((map.max_length)?map.max_length.toInteger():1))
|
|
220
|
+
len = (!blank?(map[:max_length]))?map[:max_length].to_i : 1
|
|
221
|
+
generate_length_bound_id(len)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
#Generate HL7 ST (string data) data type. A ST contains a single String value.
|
|
225
|
+
def ST(map, force=false)
|
|
226
|
+
#check if the field is optional and randomly generate it of skip
|
|
227
|
+
return '' if(!generate?(map, force))
|
|
228
|
+
|
|
229
|
+
# TODO add provider type ln 840 ROL
|
|
230
|
+
case map[:description]
|
|
231
|
+
when 'Guarantor SSN','Insured’s Social Security Number','Medicare health ins Card Number','Military ID Number', 'Contact Person Social Security Number'
|
|
232
|
+
generate_length_bound_id(9)
|
|
233
|
+
when 'Allergy Reaction Code'
|
|
234
|
+
yml['codes.allergens'].keys.sample()
|
|
235
|
+
when 'Strain'
|
|
236
|
+
#PID.35 – PID.38 should be always blank, as they deal with animals, not humans.
|
|
237
|
+
when 'AGENT ORANGE EXPOSURE LOCATION'
|
|
238
|
+
#ZEL.29 should be 1 digit integer.
|
|
239
|
+
generate_length_bound_id(1)
|
|
240
|
+
when /family name|surname/i
|
|
241
|
+
@yml['person.names.last'].sample
|
|
242
|
+
when /given name/i
|
|
243
|
+
@yml['person.names.first'].sample
|
|
244
|
+
when /suffix/i
|
|
245
|
+
@yml['person.suffix'].sample
|
|
246
|
+
when /prefix/i
|
|
247
|
+
@yml['person.prefix'].sample
|
|
248
|
+
else
|
|
249
|
+
#Case when max_len overrides requirements
|
|
250
|
+
len = safe_len(map[:max_length], @@REQ_LEN_3_DGTS)
|
|
251
|
+
(!blank?(map[:codetable]))? get_coded_value(map): generate_length_bound_id(len)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
end
|
|
255
|
+
# An Elementary Data Type Format: HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]
|
|
256
|
+
# EX: 160140.761
|
|
257
|
+
def TM(map, force=false)
|
|
258
|
+
#check if the field is optional and randomly generate it of skip
|
|
259
|
+
return '' if(!generate?(map, force))
|
|
260
|
+
to_datetime(map).strftime('%H%M%S.%L')
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
#Generate an HL7 TN (telephone number) data type. A TN contains a single String value.
|
|
264
|
+
def TN(map, force=false)
|
|
265
|
+
#check if the field is optional and randomly generate it of skip
|
|
266
|
+
return '' if(!generate?(map, force))
|
|
267
|
+
|
|
268
|
+
# TN tn = (TN) map.fld
|
|
269
|
+
# tn.setValue(phones.getAt(Math.abs(random.nextInt()%phones.size())))
|
|
270
|
+
@yml['address.phones'].sample # pick a phone
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
#Generate HL7 TS (time stamp), within number of weeks in the future or past
|
|
274
|
+
def TS(map, force=false)
|
|
275
|
+
#check if the field is optional and randomly generate it of skip
|
|
276
|
+
return '' if(!generate?(map, force))
|
|
277
|
+
|
|
278
|
+
#time of an event (TSComponentOne)
|
|
279
|
+
ts = to_datetime(map).strftime('%Y%m%d%H%M%S.%L') #format('YYYYMMDDHHSS.SSS')Date.iso8601
|
|
280
|
+
# TS is lenght sensetive, check max_len and trim appropriate
|
|
281
|
+
if (ts.size > (maxlen = (map[:max_length]) ? map[:max_length].to_i : ts.size))
|
|
282
|
+
# puts ts
|
|
283
|
+
# ts = ts[0,maxlen]
|
|
284
|
+
ts = ts.slice(0...maxlen)
|
|
285
|
+
end
|
|
286
|
+
return ts
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
#Generate HL7 TX (text data) data type. A TX contains a single String value.
|
|
290
|
+
def TX(map, force=false)
|
|
291
|
+
#check if the field is optional and randomly generate it of skip
|
|
292
|
+
return '' if(!generate?(map, force))
|
|
293
|
+
# @@GENERAL_TEXT
|
|
294
|
+
ID(map,true)
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# End of Data Types #
|
|
298
|
+
|
|
299
|
+
# convention method to modify values of attirbutes
|
|
300
|
+
def reset_map_attr(map, key, value)
|
|
301
|
+
map[key.to_sym]=value
|
|
302
|
+
return map
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# @return DateTime generated with consideration of description string for dates in the future
|
|
306
|
+
def to_datetime(map)
|
|
307
|
+
#for Time Stamp one way to figure out if event is in the future of in the past to look for key words in description
|
|
308
|
+
isFutureEvent = !blank?(map[:description])&& map[:description].include?('End') #so 'Role End Date/Time'
|
|
309
|
+
seed = 365 #seed bounds duration of time to a year
|
|
310
|
+
days = @@random.rand(seed)
|
|
311
|
+
|
|
312
|
+
if (map[:description]=='Date/Time Of Birth')
|
|
313
|
+
isFutureEvent = false
|
|
314
|
+
years = rand(30..50)
|
|
315
|
+
days = days + 365*years # make a person 30 years old
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
(isFutureEvent) ? DateTime.now().next_day(days) : DateTime.now().prev_day(days)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# If field are X,W,B (Not Supported, Withdrawn Fields or Backward Compatible) returns false.
|
|
322
|
+
# Conditional (C) ?
|
|
323
|
+
# For Optional field (O) makes random choice
|
|
324
|
+
# R - Required returns true
|
|
325
|
+
# def autoGenerate?(map, force=false)
|
|
326
|
+
def generate?(map, force=false)
|
|
327
|
+
# return true
|
|
328
|
+
return true if (force) #forced generation, ignore mappings
|
|
329
|
+
|
|
330
|
+
#look up in the mapping
|
|
331
|
+
case map[:required]
|
|
332
|
+
when 'X', 'W', 'B' then
|
|
333
|
+
false;
|
|
334
|
+
when 'R' then
|
|
335
|
+
true;
|
|
336
|
+
when 'O' then
|
|
337
|
+
[true, false].sample();
|
|
338
|
+
else
|
|
339
|
+
false;
|
|
340
|
+
end
|
|
341
|
+
# if(['X','W','B'].include?(map[:required]))then return false end
|
|
342
|
+
# if(map[:required] =='R') then return true end
|
|
343
|
+
# if(map[:required] == 'O') then return true end #random boolean
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Returns randomly generated Id of required length, of single digit id
|
|
347
|
+
def generate_length_bound_id(maxlen, str=@@random.rand(100000000).to_s)
|
|
348
|
+
idx = maxlen
|
|
349
|
+
if (maxlen > str.size)
|
|
350
|
+
str = str.ljust(maxlen, '0')
|
|
351
|
+
idx = str.size
|
|
352
|
+
end
|
|
353
|
+
#safe fail
|
|
354
|
+
#this handles case when maxlen is less then 1
|
|
355
|
+
idx = [0, idx-1].max
|
|
356
|
+
return str[0..idx]
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def handle_ranges(code)
|
|
360
|
+
# if (code.include?(@@RANGE_INDICATOR))
|
|
361
|
+
ends = code.delete(' ').split('...').map { |it| it }
|
|
362
|
+
if (ends.empty?) # This is indication that codetable does not have any values
|
|
363
|
+
code = ''
|
|
364
|
+
else #Handle range values, build range and pick random value
|
|
365
|
+
# range = ends.first..ends.last
|
|
366
|
+
# code = range.to_a.sample
|
|
367
|
+
|
|
368
|
+
# per Galina: Invalid value 'Q8' appears in segment 4:PD1, field 20, repetition 1, component 1, subcomponent 1,
|
|
369
|
+
# but does not appear in code table 2.4:141.
|
|
370
|
+
# I think you had to fix this one before to pull only the first and the last values from each row.
|
|
371
|
+
code = ends.sample
|
|
372
|
+
end
|
|
373
|
+
return code
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def handle_length(codes, maxlen)
|
|
377
|
+
idx = codes.find_index { |it| it[:value].size <= maxlen }
|
|
378
|
+
|
|
379
|
+
if (!idx)
|
|
380
|
+
code, description = '', ''
|
|
381
|
+
else
|
|
382
|
+
# puts codes
|
|
383
|
+
code = codes[idx][:value]
|
|
384
|
+
description = codes[idx][:description]
|
|
385
|
+
end
|
|
386
|
+
return code, description
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Handle range values specified by '...' sequence, including empty range
|
|
390
|
+
#TODO refactor candidate
|
|
391
|
+
def apply_rules(codes, attributes)
|
|
392
|
+
#safety check, no codes returns an empty map
|
|
393
|
+
return {} if blank?(codes)
|
|
394
|
+
|
|
395
|
+
#index of random element
|
|
396
|
+
idx = sample_index(codes.size)
|
|
397
|
+
code = codes[idx][:value]
|
|
398
|
+
description = codes[idx][:description]
|
|
399
|
+
|
|
400
|
+
if (code.include?(@@RANGE_INDICATOR))
|
|
401
|
+
code = handle_ranges(code)
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
if (code.size > (maxlen = (attributes[:max_length]) ? attributes[:max_length].to_i : code.size))
|
|
405
|
+
#remove all codes wich values violate
|
|
406
|
+
#codes.select! {|it| it[:value].size <= maxlen }
|
|
407
|
+
code, description = handle_length(codes, maxlen)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# got to have code, get an id, most basic - TODO: verify this.
|
|
411
|
+
# if(Utils.blank?(code))
|
|
412
|
+
# code, description = ID({},true), ''
|
|
413
|
+
# end
|
|
414
|
+
# return code, description
|
|
415
|
+
|
|
416
|
+
# puts code + ', ' + description
|
|
417
|
+
return {:value => code, :description => description}
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def get_code_table(attributes)
|
|
421
|
+
codes = @pp.get_code_table(attributes[:codetable])
|
|
422
|
+
# Case when we are looking for code values defined in base schema for types
|
|
423
|
+
# which are in custom/primary schema, or the other way around
|
|
424
|
+
if (@hp && (codes.first == Utils::DATA_LOOKUP_MIS))
|
|
425
|
+
codes = @hp.get_code_table(attributes[:codetable])
|
|
426
|
+
end
|
|
427
|
+
return codes
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# Values and Description from code table returned as a pair.
|
|
431
|
+
def get_coded_map(attributes)
|
|
432
|
+
codes = get_code_table(attributes)
|
|
433
|
+
|
|
434
|
+
# puts codes
|
|
435
|
+
#Apply rules to find a value and description
|
|
436
|
+
#Returns map with code and description
|
|
437
|
+
apply_rules(codes, attributes)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
# Value of coded table returned as as single value
|
|
441
|
+
def get_coded_value(attributes)
|
|
442
|
+
# codes = get_code_table(attributes)
|
|
443
|
+
# puts codes
|
|
444
|
+
#Apply rules to find a value and description
|
|
445
|
+
# map = apply_rules(codes, attributes)
|
|
446
|
+
#Return value only
|
|
447
|
+
# return map[:value]
|
|
448
|
+
get_coded_map(attributes)[:value]
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
end
|