ez7gen 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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