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.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/bin/ez7gen +6 -0
  3. data/lib/ez7gen.rb +23 -0
  4. data/lib/ez7gen/config/schema/2.4/2.4.HL7.xml +13904 -0
  5. data/lib/ez7gen/config/schema/2.4/VAZ2.4.HL7.xml +3085 -0
  6. data/lib/ez7gen/config/schema/2.4/added/coded-tables.xml +730 -0
  7. data/lib/ez7gen/config/schema/2.4/rules/2.4.HL7.yml +4 -0
  8. data/lib/ez7gen/config/schema/2.4/rules/VAZ2.4.HL7.yml +6 -0
  9. data/lib/ez7gen/config/schema/2.5/2.5.HL7.xml +10008 -0
  10. data/lib/ez7gen/config/schema/2.5/VAZ2.5.HL7.xml +7 -0
  11. data/lib/ez7gen/config/schema/2.5/added/coded-tables.xml +549 -0
  12. data/lib/ez7gen/config/schema/readme.txt +0 -0
  13. data/lib/ez7gen/config/templates/2.4/eiv table update-mfn_m01 20151201.xml +416 -0
  14. data/lib/ez7gen/config/templates/2.4/eiv table update-mfn_y01.xml +416 -0
  15. data/lib/ez7gen/config/templates/2.4/eiv-ec-MFN_X01_reg request 20160126.xml +659 -0
  16. data/lib/ez7gen/config/templates/2.4/examples/ADT_A60.txt +69 -0
  17. data/lib/ez7gen/config/templates/2.4/examples/eiv table update-mfn_m01 20151201.txt +21 -0
  18. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_dss_units-query_qbp_q13-qbp_q13.txt +26 -0
  19. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_ecs_procedures_query_qbp_q13-qbp_q13.txt +26 -0
  20. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_patient eligibility_response-rsp_k11-080714.txt +44 -0
  21. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_diagnosis_query_qbp_q11-qbp_q11.txt +21 -0
  22. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_diagnosis_response_rsp_k11-rsp_k11.txt +42 -0
  23. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_dss_units_response_rtb_k13-rtb_k13.txt +49 -0
  24. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_ecs_filer_request_dft_p03-dft_p03-080714.txt +31 -0
  25. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_ecs_filer_response_ack_p03-ack_p03.txt +21 -0
  26. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_esc_procedures_response_rtb_k13-rtb_k13.txt +40 -0
  27. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patient_eclass_query_qbp_q11-qbp_q11.txt +21 -0
  28. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patient_problems_query_qbp_q11-qbp_q11.txt +21 -0
  29. data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patinet_problems_response_rsp_k11-rsp_k11.txt +33 -0
  30. data/lib/ez7gen/config/templates/2.4/examples/orur01rvbecv2.txt +31 -0
  31. data/lib/ez7gen/config/templates/2.4/examples/sqwm vitals-oru_ro1.txt +52 -0
  32. data/lib/ez7gen/config/templates/2.4/examples/vista sqwm-adt_a60.txt +40 -0
  33. data/lib/ez7gen/config/templates/2.4/mhvsm_dss_units_query_qbp_q13-qbp_q13.xml +312 -0
  34. data/lib/ez7gen/config/templates/2.4/mhvsm_ecs_procedures_query_qbp_q13-qbp_q13.xml +314 -0
  35. data/lib/ez7gen/config/templates/2.4/mhvsm_patient eligibility_response-rsp_k11-080714.xml +640 -0
  36. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_diagnosis_query_qbp_q11-qbp_q11.xml +284 -0
  37. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_diagnosis_response_rsp_k11-rsp_k11-rsp_k11.xml +563 -0
  38. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_dss_units_response_rtb_k13-rtb_k13-rtb_k13.xml +365 -0
  39. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_filer_request_dft_p03-dft_p03-080714.xml +2172 -0
  40. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_filer_response_ack_p03-ack_p03.xml +269 -0
  41. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_procedures_response_rtb_k13-rtb_k13-rtb_k13.xml +354 -0
  42. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_eclass_query_qbp_q11-qbp_q11.xml +284 -0
  43. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_problems_query_qbp_q11-qbp_q11.xml +282 -0
  44. data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_problems_response_rsp_k11-rsp_k11-rsp_k11.xml +565 -0
  45. data/lib/ez7gen/config/templates/2.4/orur01rvbecv2.xml +1529 -0
  46. data/lib/ez7gen/config/templates/2.4/sqwm vitals-oru_r01.xml +2975 -0
  47. data/lib/ez7gen/config/templates/2.4/vista sqwm-adt_a60.xml +1360 -0
  48. data/lib/ez7gen/message_factory.rb +142 -0
  49. data/lib/ez7gen/msg_error_handler.rb +33 -0
  50. data/lib/ez7gen/profile_parser.rb +321 -0
  51. data/lib/ez7gen/resources/properties-with-comments.yml +51 -0
  52. data/lib/ez7gen/resources/properties.yml +325 -0
  53. data/lib/ez7gen/service/2.4/dynamic_field_generator.rb +45 -0
  54. data/lib/ez7gen/service/2.4/field_generator.rb +1586 -0
  55. data/lib/ez7gen/service/2.5/field_generator.rb +75 -0
  56. data/lib/ez7gen/service/base_field_generator.rb +451 -0
  57. data/lib/ez7gen/service/segment_generator.rb +218 -0
  58. data/lib/ez7gen/service/segment_picker.rb +147 -0
  59. data/lib/ez7gen/service/template_generator.rb +213 -0
  60. data/lib/ez7gen/service/type_aware_field_generator.rb +1583 -0
  61. data/lib/ez7gen/service/utils.rb +75 -0
  62. data/lib/ez7gen/structure_parser.rb +331 -0
  63. data/lib/ez7gen/version.rb +38 -0
  64. data/test/Additional Tables with values_v1.1.txt +1653 -0
  65. data/test/added_shema_test.rb +143 -0
  66. data/test/app-tmp.rb +225 -0
  67. data/test/at.txt +1 -0
  68. data/test/backburner.zip +0 -0
  69. data/test/codes.txt +262 -0
  70. data/test/codes1.txt +1240 -0
  71. data/test/data_types_exploration_test.rb +213 -0
  72. data/test/dynamic_field_generated_test.rb +292 -0
  73. data/test/message_factory_24_custom_test.rb +648 -0
  74. data/test/message_factory_25_test.rb +50 -0
  75. data/test/message_factory_adm_test.rb +558 -0
  76. data/test/message_factory_gen_test.rb +63 -0
  77. data/test/message_factory_lab_test.rb +107 -0
  78. data/test/message_factory_pharm_test.rb +121 -0
  79. data/test/message_factory_template_24_test.rb +730 -0
  80. data/test/message_factory_test.rb +220 -0
  81. data/test/msg_error_handler_test.rb +59 -0
  82. data/test/profile_parser_test.rb +542 -0
  83. data/test/quick_run.rb +880 -0
  84. data/test/segment_generator_test.rb +656 -0
  85. data/test/segment_picker_test.rb +279 -0
  86. data/test/structrure_parser_test.rb +355 -0
  87. data/test/template_generator_test.rb +164 -0
  88. data/test/type_aware_field_generator_test.rb +582 -0
  89. data/test/utils_test.rb +97 -0
  90. 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