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,1583 @@
1
+ require 'yaml'
2
+ require_relative '../profile_parser'
3
+
4
+ class TypeAwareFieldGenerator
5
+ include Utils
6
+
7
+ attr_accessor :yml,:pp
8
+ # @@UP_TO_3_DGTS = 1000 # up to 3 digits
9
+ @@REQ_LEN_3_DGTS = 3 #up to 3 digits
10
+ @@RANGE_INDICATOR = '...'
11
+ @@HAT = '^' # Component separator, aka hat
12
+ @@SUB ='&' # Subcomponent separator
13
+ # @@MONEY_FORMAT_INDICATORS = ['Money', 'Balance', 'Charge', 'Adjustment', 'Income', 'Amount', 'Payment','Cost']
14
+ # @@MONEY_FORMAT_REGEX = /\bMoney\b|\bBalance\b|\bCharge|\bAdjustment\b|\bIncome\b|\bAmount\b|\bPayment\b|\bCost\b|\bPercentage\b/
15
+ @@MONEY_FORMAT_REGEX = /\bMoney\b|\bBalance\b|\bCharge|\bAdjustment\b|\bIncome\b|\bAmount\b|\bPayment\b|\bCost\b/
16
+ @@PERCENT_FORMAT_REGEX = /\bPercentage\b|%/
17
+ @@INITIALS = ('A'..'Z').to_a
18
+ @@GENERAL_TEXT = 'Notes'
19
+
20
+ @@random = Random.new
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
+ #Generate HL7 AD (address) data type.
36
+ def AD(map, force=false)
37
+ #check if the field is optional and randomly generate it of skip
38
+ return '' if(!generate?(map, force))
39
+
40
+ # match cities, states and zips
41
+ sample = @yml['address.states'].sample
42
+ idx = @yml['address.states'].index(sample) #index of random element
43
+
44
+ val=[]
45
+ #street address (ST) (ST)
46
+ val << @yml['address.streetNames'].sample
47
+ #other designation (ST)
48
+ val <<''
49
+ #city (ST)
50
+ val << @yml['address.cities'].at(idx)
51
+ #state or province (ST)
52
+ val << @yml['address.states'].at(idx)
53
+ #zip or postal code (ST)
54
+ val << @yml['address.zips'].at(idx)
55
+ #country (ID)
56
+ val << @yml['address.countries'].at(idx)
57
+ # address type (ID)
58
+ # ot5 her geographic designation (ST)
59
+
60
+ val.join(@@HAT)
61
+ end
62
+
63
+ # Authorization information
64
+ def AUI(map, force=false)
65
+ #check if the field is optional and randomly generate it of skip
66
+ return '' if(!generate?(map, force))
67
+
68
+ # authorization number (ST)
69
+ ST(map, true)
70
+ # date (DT)
71
+ # source (ST)
72
+ end
73
+
74
+ #Charge time
75
+ def CCD(map, force=false)
76
+ #check if the field is optional and randomly generate it of skip
77
+ return '' if(!generate?(map, force))
78
+
79
+ #<when to charge code (ID)>
80
+ ID(map, force=false)
81
+ # <date/time (TS)>
82
+ end
83
+
84
+ #Channel calibration parameters
85
+ def CCP(map, force=false)
86
+ #check if the field is optional and randomly generate it of skip
87
+ return '' if(!generate?(map, force))
88
+
89
+ # <channel calibration sensitivity correction (NM)>
90
+ NM(map,true)
91
+ # <channel calibration baseline (NM)>
92
+ # <channel calibration time skew (NM)>
93
+ end
94
+
95
+ # Channel definition
96
+ def CD(map, force=false)
97
+ # check if the field is optional and randomly generate it of skip
98
+ return '' if(!generate?(map, force))
99
+
100
+ #<channel identifier (WVI)>
101
+ WVI(map, force=true)
102
+ #<waveform source (WVS)>
103
+ # <channel sensitivity/units (SCU)>
104
+ #<channel calibration parameters (CCP)>
105
+ # <sampling frequency (NM)>
106
+ # <minimum/maximum data values (NR)>
107
+ end
108
+
109
+ # Generate HL7 CE (coded element) data type
110
+ def CE(map, force=false)
111
+ #check if the field is optional and randomly generate it of skip
112
+ return '' if(!generate?(map, force))
113
+
114
+ if(map[:max_length] && map[:max_length].to_i <3)
115
+ # if CE Element has lenght of 2 or less use only value
116
+ return ID(map, true)
117
+ end
118
+
119
+ #TODO: Refactor this method
120
+ if (map[:description] == 'Role Action Reason' || map[:description] == 'Species Code' || map[:description] == 'Breed Code' || map[:description] == 'Production Class Code')
121
+ return '' #Per requirement, PID.35 – PID.38
122
+ end
123
+
124
+ val = []
125
+ # CE ce = (CE) map?.fld
126
+ codes = get_coded_map(map)
127
+ if(blank?(codes))
128
+ case map[:description]
129
+ when 'Allergen Code/Mnemonic/Description'
130
+ pair = yml['codes.allergens.icd10'].to_a.sample(1).to_h.first # randomly pick a pair
131
+ val<<pair.first
132
+ val<<pair.last
133
+
134
+ else
135
+ # TODO: only for elements that don't have look up table set the id randomly
136
+ # if codetable is empty
137
+ val << ((blank?(map[:codetable])) ? ID(map, true) : '')
138
+ end
139
+ else
140
+ #identifier (ST) (ST)
141
+ val<<codes[:value]
142
+ #text (ST)
143
+ val<<codes[:description]
144
+ #name of coding system (IS)
145
+ #alternate identifier (ST) (ST)
146
+ #alternate text (ST)
147
+ #name of alternate coding system (IS)
148
+ end
149
+ return val.join(@@HAT)
150
+ end
151
+
152
+ # Coded element with formatted values
153
+ def CF(map, force=false)
154
+ #check if the field is optional and randomly generate it of skip
155
+ return '' if(!generate?(map, force))
156
+
157
+ # <identifier (ID)>
158
+ ID(map, true)
159
+ # <formatted text (FT)>
160
+ # <name of coding system (IS)>
161
+ # <alternate identifier (ID)>
162
+ # <alternate formatted text (FT)>
163
+ # <name of alternate coding system (IS)>
164
+ end
165
+
166
+ #Composite ID with check digit
167
+ def CK(map, force=false)
168
+ #check if the field is optional and randomly generate it of skip
169
+ return '' if(!generate?(map, force))
170
+ # <ID number (NM)>
171
+ NM(map,true)
172
+ # <check digit (NM)>
173
+ # <code identifying the check digit scheme employed (ID)>
174
+ # < assigning authority (HD)>
175
+ end
176
+
177
+ #Composite
178
+ def CM(map, force=false)
179
+ #check if the field is optional and randomly generate it of skip
180
+ return '' if(!generate?(map, force))
181
+
182
+ val=[]
183
+ # <penalty type (IS)>
184
+ val=IS(map,true)
185
+ # <penalty amount (NM)>
186
+ val<<NM({},true)
187
+ val.join(@@HAT)
188
+ end
189
+
190
+ # CN Composite ID number and name
191
+ # "<ID number (ST)> ^ <family name (FN)> ^ <given name (ST)> ^ < second and further given names or initials thereof (ST)> ^ <suffix (e.g., JR or III) (ST)> ^ <prefix (e.g. DR) (ST)> ^ <degree (e.g., MD) (IS)> ^ <source table (IS)> ^ <assigning authority(HD)>
192
+ # Replaced by XCN data type as of v 2.3"
193
+
194
+ # CNE Coded with no exceptions
195
+ def CNE(map, force=false)
196
+ #check if the field is optional and randomly generate it of skip
197
+ return '' if(!generate?(map, force))
198
+
199
+ # <identifier (ST)> ^ <text (ST)>
200
+ ST(map, true)
201
+ # <name of coding system (IS)>
202
+ # <alternate identifier(ST)>
203
+ # <alternate text (ST)>
204
+ # <name of alternate coding system (IS)>
205
+ # <coding system version ID (ST)>
206
+ # alternate coding system version ID (ST)>
207
+ # <original text (ST)>
208
+ end
209
+
210
+ # Composite ID number and name (special DT for NDL)
211
+ def CNN(map, force=false)
212
+ #check if the field is optional and randomly generate it of skip
213
+ return '' if(!generate?(map, force))
214
+
215
+ # ID number (ST) (ST)
216
+
217
+ # val << ID(map, true)
218
+ # family name (FN), used for ST
219
+ # val << FN(map, true)
220
+ # given name (ST)
221
+ # val << @yml['person.names.first'].sample
222
+ # second and further given names or initials thereof (ST)
223
+ # val << @@INITIALS.to_a.sample
224
+ # < suffix (e.g., JR or III) (ST)> ^ < prefix (e.g., DR) (ST)>
225
+ # < degree (e.g., MD) (IS)> ^ < source table (IS)>
226
+ PN(map, true)
227
+
228
+ # < assigning authority namespace ID (IS)>
229
+ # < assigning authority universal ID (ST)>
230
+ # < assigning authority universal ID type (ID)>
231
+ # val.join(@@SUB)
232
+ end
233
+
234
+ def CP(map, force=false)
235
+ #check if the field is optional and randomly generate it of skip
236
+ return '' if(!generate?(map, force))
237
+
238
+ #price (MO)
239
+ MO(map,true)
240
+ #price type (ID)
241
+ #from value (NM)
242
+ #to value (NM)
243
+ #range units (CE)
244
+ #range type (ID)
245
+ end
246
+
247
+ # Composite quantity with units
248
+ def CQ(map, force=false)
249
+ #check if the field is optional and randomly generate it of skip
250
+ return '' if(!generate?(map, force))
251
+ # val =[]
252
+ # <quantity (NM)>
253
+ NM({},true)
254
+
255
+ # val<<NM({},true)
256
+ # <units (CE)>
257
+ # val<<CE(map,true) # Per request with QBP_Q21 issue
258
+ # CE always get values from code table 335, schema does not always specify it.
259
+ # only ID part from CE is used for this data type
260
+ # val<<ID(reset_map_attr(map, :codetable, '335'), true)
261
+ # val<<ID(map,true)
262
+ # val.join(@@HAT)
263
+ # val.join(@@SUB)
264
+ end
265
+
266
+ #Channel sensitivity/units
267
+ def CSU(map, force=false)
268
+ #check if the field is optional and randomly generate it of skip
269
+ return '' if(!generate?(map, force))
270
+
271
+ # < channel sensitivity (NM)>
272
+ NM(map,true)
273
+ # < unit of measure identifier (ST)>
274
+ # < unit of measure description (ST)>
275
+ # < unit of measure coding system (IS)>
276
+ # < alternate unit of measure identifier (ST)>
277
+ # < alternate unit of measure description (ST)>
278
+ # < alternate unit of measure coding system (IS)>
279
+ end
280
+
281
+ #Coded with exceptions
282
+ def CWE(map, force=false)
283
+ #check if the field is optional and randomly generate it of skip
284
+ return '' if(!generate?(map, force))
285
+ # <identifier (ST)>
286
+ # <text (ST)> ^ <name of coding system (IS)> ^ <alternate identifier(ST)> ^ <alternate text (ST)> ^ <name of alternate coding system (IS)> ^ <coding system version ID (ST)> ^ alternate coding system version ID (ST)> ^ <original text (ST)>
287
+ end
288
+
289
+ #Generate HL7 CX (extended composite ID with check digit) data type.
290
+ def CX(map, force=false)
291
+ #check if the field is optional and randomly generate it of skip
292
+ return '' if(!generate?(map, force))
293
+
294
+ #ID (ST)
295
+ ST(reset_map_attr(map, :description, 'Number'), true)
296
+ #check digit (ST) (ST)
297
+ #code identifying the check digit scheme employed (ID)
298
+ #assigning authority (HD)
299
+ #identifier type code (ID) (ID)
300
+ #assigning facility (HD)
301
+ #effective date (DT) (DT)
302
+ #expiration date (DT)
303
+ end
304
+
305
+ # Daily deductible
306
+ def DDI(map, force=false)
307
+ #check if the field is optional and randomly generate it of skip
308
+ return '' if(!generate?(map, force))
309
+
310
+ # < delay days (NM)>
311
+ # < amount (NM)>
312
+ NM(map,true)
313
+ # < number of days (NM)>
314
+ end
315
+
316
+ # Activation date
317
+ def DIN(map, force=false)
318
+ #check if the field is optional and randomly generate it of skip
319
+ return '' if(!generate?(map, force))
320
+ # date < date (TS)>
321
+ TS(map,true)
322
+ # <institution name (CE)>
323
+ end
324
+
325
+ #Generate HL7 DLD (discharge location) data type. This type consists of the following components=>
326
+ def DLD(map, force=false)
327
+ #check if the field is optional and randomly generate it of skip
328
+ return '' if(!generate?(map, force))
329
+
330
+ val=[]
331
+ #discharge location (ID)
332
+ val<<ID(map, true)
333
+ #effective date (TS)
334
+ val<<TS({},true)
335
+ val.join(@@HAT)
336
+ end
337
+
338
+ #Generate HHL7 DLN (driver's license number) data type
339
+ def DLN(map, force=false)
340
+ #check if the field is optional and randomly generate it of skip
341
+ return '' if(!generate?(map, force))
342
+ val=[]
343
+ # DLN dln = (DLN) map.fld
344
+ #Driver´s License Number (ST)
345
+ val << generate_length_bound_id(10) # 10 vs 7 Numeric, as for some states in real life
346
+ #Issuing State, province, country (IS)
347
+ #is(['fld'=>dln.getIssuingStateProvinceCountry(), 'required'=>'R','codetable'=>map.codetable])
348
+ #dln.getIssuingStateProvinceCountry().setValue(allStates.get(Math.abs(random.nextInt()%allStates.size())))
349
+ # val << @yml['address.states'].sample # pick a state
350
+ # val << @yml['address.states'].sample # pick a state
351
+ val << IS({:codetable =>'333'},true) # pick a state
352
+ #expiration date (DT)
353
+ val << DT(reset_map_attr(map, :description, 'End'), true)
354
+ val.join(@@HAT)
355
+ end
356
+
357
+
358
+ # Delta check
359
+ def DLT(map, force=false)
360
+ #check if the field is optional and randomly generate it of skip
361
+ return '' if(!generate?(map, force))
362
+ # <range (NR)>
363
+ NR(map,true)
364
+ # <numeric threshold (NM)>
365
+ # <change computation (ST)>
366
+ # <length of time-days (NM)>
367
+ end
368
+
369
+ #Generates HL7 DR (date/time range) data type.
370
+ def DR(map, force=false)
371
+ #check if the field is optional and randomly generate it of skip
372
+ return '' if(!generate?(map, force))
373
+ val=[]
374
+ #range start date/time (TS)
375
+ val<<TS(map,true)
376
+ #range end date/time (TS)
377
+ val<<TS(reset_map_attr(map, :description, 'End'), true)
378
+ val.join(@@HAT)
379
+ end
380
+
381
+ #Generates an HL7 DT (date) datatype.
382
+ def DT(map, force=false)
383
+ #check if the field is optional and randomly generate it of skip
384
+ return '' if(!generate?(map, force))
385
+
386
+ is_year_only = (map[:max_length]) ? map[:max_length].to_i == 4 : false
387
+ # #time of an event (TSComponentOne)
388
+ (is_year_only)? to_datetime(map).strftime('%Y') : to_datetime(map).strftime('%Y%m%d') #format('YYYYMMdd.SSS')Date.iso8601
389
+ end
390
+
391
+ # Day Type and Number
392
+ def DTN(map, force=false)
393
+ #check if the field is optional and randomly generate it of skip
394
+ return '' if(!generate?(map, force))
395
+
396
+ # val=[]
397
+ # # <day type (IS)>
398
+ # val<<IS(map,true)
399
+ # per Galina request: Ensemble limits the length of IN3.11 to 3. So, we need to remove the second component from being populated ...
400
+ IS(map,true)
401
+ # # <number of days (NM)>
402
+ # val<<NM({},true)
403
+ # val.join(@@HAT)
404
+ end
405
+
406
+ # Encapsulated data
407
+ def ED(map, force=false)
408
+ #check if the field is optional and randomly generate it of skip
409
+ return '' if(!generate?(map, force))
410
+ # <source application (HD) >
411
+ HD(map, true)
412
+ # <type of data (ID)>
413
+ # <data subtype (ID)>
414
+ # <encoding (ID)>
415
+ # <data (ST)>
416
+ end
417
+
418
+ # Entity identifier
419
+ def EI(map, force=false)
420
+ #check if the field is optional and randomly generate it of skip
421
+ return '' if(!generate?(map, force))
422
+ # <entity identifier (ST)>
423
+ ST(map,true)
424
+ # <namespace ID (IS)>
425
+ # <universal ID (ST)>
426
+ # < universal ID type (ID)>
427
+ end
428
+
429
+ # Parent order
430
+ def EIP(map, force=false)
431
+ #check if the field is optional and randomly generate it of skip
432
+ return '' if(!generate?(map, force))
433
+
434
+ # <parent’s placer order number (EI)>
435
+ EI(map,true)
436
+ # <parent’s filler order number (EI)>
437
+ end
438
+
439
+ # Error segment
440
+ def ELD(map, force=false)
441
+ #check if the field is optional and randomly generate it of skip
442
+ return '' if(!generate?(map, force))
443
+ val = []
444
+ # <segment ID (ST)>
445
+ val<<''
446
+ # <sequence (NM)>
447
+ val<<''
448
+ # <field position (NM)>
449
+ val<<''
450
+ # <code identifying error (CE)>
451
+ val<<CE(map,true)
452
+ val.join(@@HAT)
453
+ end
454
+
455
+ #Generates HL7 FC (financial class) data type.
456
+ def FC(map, force=false)
457
+ #check if the field is optional and randomly generate it of skip
458
+ return '' if(!generate?(map, force))
459
+
460
+ val = []
461
+ #Financial Class (IS)
462
+ val << IS(map, true)
463
+ #Effective Date (TS) (TS)
464
+ val << TS(map, true)
465
+ val.join(@@HAT)
466
+ end
467
+
468
+ #Generates an HL7 FN (familiy name) data type.
469
+ def FN(map, force=false)
470
+ #check if the field is optional and randomly generate it of skip
471
+ return '' if(!generate?(map, force))
472
+
473
+ #surname (ST)
474
+ @yml['person.names.last'].sample
475
+ #own surname prefix (ST)
476
+ #own surname (ST)
477
+ #surname prefix from partner/spouse (ST)
478
+ #surname from partner/spouse (ST)
479
+ end
480
+
481
+ # Formatted text data.
482
+ # The FT field is of arbitrary length (up to 64k) and may contain formatting commands enclosed in escape characters.
483
+ def FT(map, force=false)
484
+ #check if the field is optional and randomly generate it of skip
485
+ return '' if(!generate?(map, force))
486
+
487
+ ID(map, true)
488
+ end
489
+
490
+
491
+ #Generates HL7 HD (hierarchic designator) data type
492
+ def HD(map, force=false)
493
+ #check if the field is optional and randomly generate it of skip
494
+ return '' if(!generate?(map, force))
495
+
496
+ #namespace ID (IS)
497
+ IS(map, true)
498
+ #universal ID (ST)
499
+ #universal ID type (ID)
500
+ end
501
+
502
+
503
+ # Generate HL7 ID, usually using value from code table
504
+ def ID(map, force=false)
505
+ #check if the field is optional and randomly generate it of skip
506
+ return '' if(!generate?(map, force))
507
+
508
+ #value only
509
+ #Case when max_len overrides requirements
510
+ len = safe_len(map[:max_length], @@REQ_LEN_3_DGTS)
511
+ (!blank?(map[:codetable]))? get_coded_value(map): generate_length_bound_id(len)
512
+ end
513
+
514
+ #Generates HL7 IS (namespace id) data type
515
+ def IS(map, force=false)
516
+ #check if the field is optional and randomly generate it of skip
517
+ return '' if(!generate?(map, force))
518
+
519
+ #TODO: same as ID?
520
+ ID(map,true)
521
+ end
522
+
523
+
524
+ #Generates HL7 JCC (job code/class) data type.
525
+ def JCC(map, force=false)
526
+ #check if the field is optional and randomly generate it of skip
527
+ return '' if(!generate?(map, force))
528
+
529
+ val=[]
530
+ #job code (IS)
531
+ # is(['fld'=>jcc.getComponent(0), 'required'=>'R', 'codetable'=>map.codetable])
532
+ val << IS(map, true)
533
+ #job class (IS)
534
+ val << IS(map, true)
535
+ # is(['fld'=>jcc.getComponent(1), 'required'=>'R'])
536
+ val.join(@@HAT)
537
+ end
538
+
539
+ # Location with address information (variant 1)
540
+ def LA1(map, force=false)
541
+ #check if the field is optional and randomly generate it of skip
542
+ return '' if(!generate?(map, force))
543
+ val =[]
544
+ # <point of care (IS)>
545
+ val<<IS(map,true)
546
+ # <room (IS) >
547
+ val<<IS({:codetable =>'303'},true)
548
+ # <bed (IS)>
549
+ val<<IS({:codetable =>'304'},true)
550
+ # <facility (HD) >
551
+ val<<HD({},true)
552
+ # <location status (IS)
553
+ val<<''
554
+ # <patient location type (IS)>
555
+ val<<''
556
+ # <building (IS)>
557
+ val<<IS({:codetable =>'307'},true)
558
+ # <floor (IS)>
559
+ # <address(AD)>
560
+ val.join(@@HAT)
561
+ end
562
+
563
+ # Location with address information (variant 2)
564
+ def LA2(map, force=false)
565
+ #check if the field is optional and randomly generate it of skip
566
+ return '' if(!generate?(map, force))
567
+
568
+ val =[]
569
+ # <point of care (IS)>
570
+ val<<IS(map,true)
571
+ # <room (IS)
572
+ val<<IS({:codetable =>'303'},true)
573
+ # <bed (IS)>
574
+ val<<IS({:codetable =>'304'},true)
575
+ # <facility (HD) >
576
+ val<<HD({},true)
577
+ # <location status (IS)
578
+ val<<''
579
+ # <patient location type (IS)>
580
+ val<<''
581
+ # <building (IS)>
582
+ val<<IS({:codetable =>'307'},true)
583
+ # <floor (IS)>
584
+ # < street address (ST)>
585
+ # <other designation (ST)>
586
+ # <city (ST)>
587
+ # <state or province (ST)>
588
+ # <zip or postal code (ST)>
589
+ # <country (ID)>
590
+ # <address type (ID)>
591
+ # <other geographic designation (ST)>
592
+ val.join(@@HAT)
593
+ end
594
+
595
+ #MA segment
596
+ def MA(map, force=false)
597
+ #check if the field is optional and randomly generate it of skip
598
+ return '' if(!generate?(map, force))
599
+
600
+ # <sample 1 from channel 1 (NM)>
601
+ NM(map,true)
602
+ # <sample 1 from channel 2 (NM)>
603
+ # <sample 1 from channel 3 (NM)>
604
+ # <sample 2 from channel 1 (NM)>
605
+ # <sample 2 from channel 2 (NM)>
606
+ # <sample 2 from channel 3 (NM)>
607
+ end
608
+
609
+
610
+ #Generates an HL7 MO (money) data type.
611
+ def MO(map, force=false)
612
+ #check if the field is optional and randomly generate it of skip
613
+ return '' if(!generate?(map, force))
614
+ # val = []
615
+ #quantity (NM)Guarantor Household Annual Income
616
+ # val << NM(reset_map_attr(map,:description,'Money'),true)
617
+ #val << NM(map,true)
618
+ NM(map,true)
619
+ #denomination (ID)
620
+ # val << 'USD' # Per request.
621
+ # return val.join(@@SUB)
622
+ end
623
+
624
+ # Charge to practice
625
+ def MOC(map, force=false)
626
+ #check if the field is optional and randomly generate it of skip
627
+ return '' if(!generate?(map, force))
628
+
629
+ # <dollar amount (MO)>
630
+ MO(map,true)
631
+ # <charge code (CE)>
632
+ end
633
+
634
+ # Money or percentage
635
+ def MOP(map, force=false)
636
+ #check if the field is optional and randomly generate it of skip
637
+ return '' if(!generate?(map, force))
638
+ val=[]
639
+ # <money or percentage indicator (IS)>
640
+ val<<IS({:codetable =>'148'},true)
641
+ # <money or percentage quantity (NM)>
642
+ val<<NM({:description =>'Percentage'},true)
643
+ val.join(@@HAT)
644
+ end
645
+
646
+ # MSG segment
647
+ def MSG(map, force=false)
648
+ #check if the field is optional and randomly generate it of skip
649
+ return '' if(!generate?(map, force))
650
+ val=[]
651
+ # <message type (ID)>
652
+ val<<IS({:codetable =>'76'},true)
653
+ # <trigger event (ID)>
654
+ val<<IS({:codetable =>'3'},true)
655
+ # <message structure (ID)>
656
+ val<<IS({:codetable =>'354'},true)
657
+ val.join(@@HAT)
658
+ end
659
+
660
+ #Generates HL7 MSG (Message Type) data type.
661
+ def MSH(map, force=false)
662
+ #Message type set while initializing MSH segment, do nothing.
663
+
664
+ #message type (ID)
665
+ #trigger event (ID)
666
+ #message structure (ID)
667
+ end
668
+
669
+
670
+ # Numeric Array
671
+ def NA(map, force=false)
672
+ #check if the field is optional and randomly generate it of skip
673
+ return '' if(!generate?(map, force))
674
+ # <value1 (NM)>
675
+ NM(map,true)
676
+ # <value2 (NM)>
677
+ # <value3 (NM)>
678
+ # <value4 (NM)>
679
+ end
680
+
681
+ # Observing practitioner
682
+ def NDL(map, force=false)
683
+ #check if the field is optional and randomly generate it of skip
684
+ return '' if(!generate?(map, force))
685
+ # <name (CNN)>
686
+ CNN(map,true)
687
+ # <start date/time (TS)>
688
+ # <end date/time (TS)>
689
+ # <point of care (IS)>
690
+ # <room (IS)>
691
+ # <bed (IS)>
692
+ # <facility (HD)>
693
+ # <location status (IS)>
694
+ # <person location type (IS)>
695
+ # <building (IS)>
696
+ # <floor (IS)>
697
+ end
698
+
699
+
700
+ #Generates an HL7 NM (numeric) data type. A NM contains a single String value.
701
+ def NM(map, force=false)
702
+ #check if the field is optional and randomly generate it of skip
703
+ return '' if(!generate?(map, force))
704
+ val = 0
705
+ case map[:description]
706
+ when'Guarantor Household Size','Birth Order'
707
+ val = generate_length_bound_id(1)
708
+ when 'Guarantor Household Annual Income'
709
+ val = '%.2f' % generate_length_bound_id(5)
710
+ when @@MONEY_FORMAT_REGEX
711
+ val = '%.2f' % ID(map,true)
712
+ when @@PERCENT_FORMAT_REGEX
713
+ val = @@random.rand(0..100) # generate proper % value < 100
714
+ else
715
+ val = ID(map,true) # general rule for a number
716
+ if (map[:datatype] == 'CP' || map[:datatype] == 'MO') # money
717
+ val = '%.2f' % val
718
+ end
719
+ end
720
+ # #money
721
+ # if (!Utils.blank?(map[:description]) && @@MONEY_FORMAT_INDICATORS.index{|it| map[:description].include?(it)}) #check for specific numeric for money
722
+ # val = '%.2f' % @@random.rand(@@UP_TO_3_DGTS) #under $1,000
723
+ # else #quantity (NM)
724
+ # val = @@random.rand(@@UP_TO_3_DGTS).to_s #under 20
725
+ # end
726
+ return val
727
+ end
728
+
729
+
730
+ #Numeric Range
731
+ def NR(map, force=false)
732
+ #check if the field is optional and randomly generate it of skip
733
+ return '' if(!generate?(map, force))
734
+ val=[]
735
+ # Low Value (NM)
736
+ val=NM(map,true)
737
+ # High Value (NM)
738
+ val<<''
739
+ val.join(@@SUB)
740
+ end
741
+
742
+ #Generates an HL7 OCD (occurence) data type.
743
+ #The code and associated date defining a significant event relating to a bill that may affect payer processing
744
+ def OCD(map, force=false)
745
+ #check if the field is optional and randomly generate it of skip
746
+ return '' if(!generate?(map, force))
747
+ val = []
748
+ #occurrence code (IS)
749
+ val << IS(map, true)
750
+ # is(['fld'=>ocd.getComponent(0), 'required'=>'R', 'codetable'=>map.codetable])
751
+ #occurrence date (DT)
752
+ #dt(['fld'=>ocd.getComponent(1), 'required'=>'R'])
753
+ val << DT(map, true)
754
+ val.join(@@HAT)
755
+ end
756
+
757
+ # Order sequence
758
+ def OSD(map, force=false)
759
+ #check if the field is optional and randomly generate it of skip
760
+ return '' if(!generate?(map, force))
761
+
762
+ # <sequence/results flag (ID)>
763
+ ID(map, force)
764
+ # <placer order number: entity identifier (ST)>
765
+ # <placer order number: namespace ID (IS)>
766
+ # <filler order number: entity identifier (ST)>
767
+ # <filler order number: namespace ID (IS)>
768
+ # <sequence condition value (ST)>
769
+ # <maximum number of repeats (NM)>
770
+ # <placer order number: universal ID (ST)>
771
+ # <placer order number; universal ID type (ID)>
772
+ # <filler order number: universal ID (ST)>
773
+ # <filler order number: universal ID type (ID)>
774
+ end
775
+
776
+ #Generate an HL7 OSP (occurence span) data type.
777
+ def OSP(map, force=false)
778
+ #check if the field is optional and randomly generate it of skip
779
+ return '' if(!generate?(map, force))
780
+ val = []
781
+
782
+ #occurrence span code (CE)
783
+ # val << CE(map, true) # Per request with ADT_A05 and ADT_A06 issues
784
+ val << ID(map, true)
785
+ #occurrence span start date (DT)
786
+ val << DT({},true)
787
+ #occurrence span stop date (DT)
788
+ val << DT(reset_map_attr(map, :description, 'End'), true)
789
+ val.join(@@HAT)
790
+ end
791
+
792
+ # Pre-certification required
793
+ def PCF(map, force=false)
794
+ #check if the field is optional and randomly generate it of skip
795
+ return '' if(!generate?(map, force))
796
+
797
+ # <pre-certification patient type (IS)>
798
+ IS({:codetable =>'150'},true)
799
+ # <pre-certification required>
800
+ # <pre-certification window>
801
+ end
802
+
803
+ # Person identifier
804
+ def PI(map, force=false)
805
+ #check if the field is optional and randomly generate it of skip
806
+ return '' if(!generate?(map, force))
807
+ # <ID number (ST)>
808
+ ST(map,true)
809
+ # <type of ID number>
810
+ # <other qualifying info>
811
+ end
812
+
813
+ # Privileges
814
+ def PIP(map, force=false)
815
+ #check if the field is optional and randomly generate it of skip
816
+ return '' if(!generate?(map, force))
817
+
818
+ # <privilege (CE)>
819
+ IS(map,true)
820
+ # <privilege class (CE)>
821
+ # <expiration date (DT)>
822
+ # <activation date (DT)>
823
+ # <facility (EI)>
824
+ end
825
+
826
+
827
+ #Generate an HL7 PL (person location) data type.
828
+ def PL(map, force=false)
829
+ #check if the field is optional and randomly generate it of skip
830
+ return '' if(!generate?(map, force))
831
+ val = []
832
+ #point of care (IS)
833
+ val<<IS({:codetable =>'302'},true)
834
+ #room (IS)
835
+ val<<IS({:codetable =>'303'},true)
836
+ #bed (IS)
837
+ val<<IS({:codetable =>'304'},true)
838
+ #facility (HD) (HD)
839
+ val << HD({:codetable =>'300'}, true)
840
+ #location status (IS)
841
+ val << ''
842
+ #person location type (IS)
843
+ val << ''
844
+ #building (IS)
845
+ val<<IS({:codetable =>'307'},true)
846
+ #floor (IS)
847
+ #Location description (ST)
848
+ val.join(@@HAT)
849
+ end
850
+
851
+ # Practitioner ID Numbers
852
+ def PLN(map, force=false)
853
+ #check if the field is optional and randomly generate it of skip
854
+ return '' if(!generate?(map, force))
855
+
856
+ # <ID number (ST)>
857
+ ST(map,true)
858
+ # <type of ID number (IS)>
859
+ # <state/other qualifying info (ST)>
860
+ # <expiration date (DT)>
861
+ end
862
+
863
+
864
+ # Person name
865
+ def PN(map, force=false)
866
+ #check if the field is optional and randomly generate it of skip
867
+ return '' if(!generate?(map, force))
868
+ val=[]
869
+ # family name (FN)
870
+ val << FN(map, true)
871
+ # given name (ST)
872
+ val << @yml['person.names.first'].sample
873
+ # second and further given names or initials thereof (ST)
874
+ val << @@INITIALS.to_a.sample
875
+ # suffix (e.g., JR or III) (ST)
876
+ # prefix (e.g., DR) (ST)
877
+ # degree (e.g., MD) (IS)
878
+ val.join(@@HAT)
879
+ end
880
+
881
+ # Performing person time stamp
882
+ def PPN(map, force=false)
883
+ # check if the field is optional and randomly generate it of skip
884
+ return '' if(!generate?(map, force))
885
+ val = []
886
+ # <ID number (ST)>
887
+ val << ST(map,true)
888
+
889
+ # PN will work for the subset of fields used below
890
+ val << PN({},true)
891
+ # <family name (FN)>
892
+ # <given name (ST)>
893
+ # <second and further given names or initials thereof (ST)>
894
+ # <suffix (e.g., JR or III) (ST)>
895
+ # <prefix (e.g., DR) (ST)>
896
+ # <degree (e.g., MD) (IS)>
897
+ # <source table (IS)>
898
+ # <assigning authority (HD)>
899
+ # <name type code(ID)>
900
+ # <identifier check digit (ST)>
901
+ # <code identifying the check digit scheme employed (ID )>
902
+ # <identifier type code (IS)>
903
+ # <assigning facility (HD)>
904
+ # < date/time action performed (TS)>
905
+ # <name representation code (ID)>
906
+ # <name context (CE)>
907
+ # <name validity range (DR)>
908
+ # <name assembly order (ID)>
909
+
910
+ val.join(@@HAT)
911
+ end
912
+
913
+ # Parent result link
914
+ def PRL(map, force=false)
915
+ #check if the field is optional and randomly generate it of skip
916
+ return '' if(!generate?(map, force))
917
+
918
+ # <OBX-3-observation identifier of parent result (CE)>
919
+ CE(map,true)
920
+ # <OBX-4-sub-ID of parent result(ST)>
921
+ # <part of OBX-5 observation result from parent (TX)see discussion>
922
+ end
923
+
924
+ #Generate HL7 S PT (processing type) data type.
925
+ def PT(map, force=false)
926
+ #check if the field is optional and randomly generate it of skip
927
+ return '' if(!generate?(map, force))
928
+
929
+ #map.maxlen
930
+ # PT pt = (PT) map.fld
931
+ # Map mp = map.clone()
932
+ # mp.fld = pt.getComponent(0)
933
+ # mp.required = 'R'
934
+ ID(map, true)
935
+ #processing ID (ID)
936
+ #processing mode (ID)
937
+ end
938
+
939
+ # Processing type
940
+ def PTA(map, force=false)
941
+ #check if the field is optional and randomly generate it of skip
942
+ return '' if(!generate?(map, force))
943
+
944
+ # <processing ID (ID)>
945
+ ID(map, true)
946
+ # <processing mode (ID)>
947
+ # <processing ID (ID)>
948
+ end
949
+
950
+ # Query input parameter list
951
+ def OIP(map, force=false)
952
+ #check if the field is optional and randomly generate it of skip
953
+ return '' if(!generate?(map, force))
954
+
955
+ # <segment field name (ST) >
956
+ # <value1 (ST) & value2 (ST) & value3 (ST) ...>
957
+ #Example: |@PID.5.1^EVANS|
958
+ #TBD
959
+ ''
960
+ end
961
+
962
+ # QSC Query selection criteria
963
+ def QSC(map, force=false)
964
+ #check if the field is optional and randomly generate it of skip
965
+ return '' if(!generate?(map, force))
966
+
967
+ # <segment field name(ST)>
968
+ # <relational operator (ID)>
969
+ # <value (ST)> <relational conjunction (ID)>
970
+ # Example: |@PID.5.1^EQ^EVANS|"
971
+ #TBD
972
+ ''
973
+ end
974
+
975
+ # RCD Row column definition
976
+ def RCD(map, force=false)
977
+ #check if the field is optional and randomly generate it of skip
978
+ return '' if(!generate?(map, force))
979
+
980
+ # <segment field name (ST)>
981
+ ST(reset_map_attr(map,:codetable, '440'),true)
982
+ # <HL7 data type (ID)>
983
+ # <maximum column width (NM)>
984
+ # Example: |@PID.5.1^ST^20|"
985
+ #TBD
986
+ # ''
987
+ end
988
+
989
+ # RDF Table row definition
990
+ def RDT(map, force=false)
991
+ #check if the field is optional and randomly generate it of skip
992
+ return '' if(!generate?(map, force))
993
+ val = []
994
+ # <parameter class (IS)>
995
+ # randomly generated 3-digit number
996
+ # v << generate_length_bound_id(@@REQ_LEN_3_DGTS)
997
+ ID({},true)
998
+ end
999
+
1000
+ # Reference range
1001
+ def RFR(map, force=false)
1002
+ #check if the field is optional and randomly generate it of skip
1003
+ return '' if(!generate?(map, force))
1004
+
1005
+ # <numeric range (NR)>
1006
+ NR(map, true)
1007
+ # <administrative sex (IS)
1008
+ # <age range (NR)>
1009
+ # <gestational range (NR)>
1010
+ # <species (TX)>
1011
+ # <race/subspecies (ST)>
1012
+ # <conditions (TX)>
1013
+ end
1014
+
1015
+ # Repeat interval
1016
+ def RI(map, force=false)
1017
+ #check if the field is optional and randomly generate it of skip
1018
+ return '' if(!generate?(map, force))
1019
+ # <repeat pattern (IS)>
1020
+ # IS(map,true)
1021
+ IS(reset_map_attr(map, :codetable, '335'), true)
1022
+ # <explicit time interval (ST)>
1023
+ end
1024
+
1025
+ # Room coverage
1026
+ def RMC(map, force=false)
1027
+ #check if the field is optional and randomly generate it of skip
1028
+ return '' if(!generate?(map, force))
1029
+
1030
+ # <room type> (IS)
1031
+ IS(map,true)
1032
+ # <amount type>
1033
+ # <coverage amount>
1034
+ end
1035
+
1036
+ # Reference pointer
1037
+ def PR(map, force=false)
1038
+ #check if the field is optional and randomly generate it of skip
1039
+ return '' if(!generate?(map, force))
1040
+
1041
+ # <pointer (ST) >
1042
+ ST(map,true)
1043
+ # < application ID (HD)>
1044
+ # <type of data (ID)>
1045
+ # <subtype (ID)>
1046
+ end
1047
+
1048
+ # Street address
1049
+ # Appears ONLY in the XAD data type.
1050
+ # TODO: Future refactoring, handled in XAD.
1051
+ # def SAD(map, force=false)
1052
+ # #check if the field is optional and randomly generate it of skip
1053
+ # return '' if(!autoGenerate?(map,force))
1054
+ #
1055
+ # # <street or mailing address (ST)>
1056
+ # # <street name (ST)>
1057
+ # # <dwelling number (ST)>
1058
+ # end
1059
+
1060
+ # SCV Scheduling class value pair
1061
+ def SCV(map, force=false)
1062
+ #check if the field is optional and randomly generate it of skip
1063
+ return '' if(!generate?(map, force))
1064
+
1065
+ val = []
1066
+ # <parameter class (IS)>
1067
+ val<<''
1068
+ # <parameter value (ST)>
1069
+ # val<<ST(map,true)
1070
+ val<<ST(reset_map_attr(map,:codetable, '294'),true)
1071
+ val.join(@@HAT)
1072
+ end
1073
+
1074
+ #Generate HL7 SI (sequence ID) data type. A SI contains a single String value.
1075
+ def SI(map, force=false)
1076
+ #check if the field is optional and randomly generate it of skip
1077
+ return '' if(!generate?(map, force))
1078
+
1079
+ #SI pt = (SI) map.fld
1080
+ #pt.setValue(generate_length_bound_id((map.max_length)?map.max_length.toInteger():1))
1081
+ len = (!blank?(map[:max_length]))?map[:max_length].to_i : 1
1082
+ # per Galina: - the set IDs should be max 4 digits, please remove all the zeros.
1083
+ len = (len >4 )? 4 :len
1084
+ generate_length_bound_id(len)
1085
+ end
1086
+
1087
+ # Structured numeric
1088
+ def SN(map, force=false)
1089
+ #check if the field is optional and randomly generate it of skip
1090
+ return '' if(!generate?(map, force))
1091
+ val = []
1092
+ # <comparator (ST)>
1093
+ val << ''
1094
+ # <num1 (NM)>
1095
+ val << NM(map,true)
1096
+ # <separator/suffix (ST)>
1097
+ # <num2 (NM)>
1098
+ val.join(@@HAT)
1099
+ end
1100
+
1101
+ # Specialty
1102
+ def SPD(map, force=false)
1103
+ #check if the field is optional and randomly generate it of skip
1104
+ return '' if(!generate?(map, force))
1105
+
1106
+ # <specialty name (ST)>
1107
+ ST(map,true)
1108
+ # <governing board (ST)>
1109
+ # <eligible or certified (ID)>
1110
+ # <date of certification (DT)>
1111
+ end
1112
+
1113
+ # Specimen source
1114
+ def SPS(map, force=false)
1115
+ #check if the field is optional and randomly generate it of skip
1116
+ return '' if(!generate?(map, force))
1117
+
1118
+ #<specimen source name or code (CE)>
1119
+ CE(map,true)
1120
+ # <additives (TX)>
1121
+ # <freetext (TX)>
1122
+ # <body site (CE)>
1123
+ # <site modifier (CE)>
1124
+ # <collection modifier method code (CE)>
1125
+ # <specimen role (CE)>
1126
+ end
1127
+
1128
+ #Sort order
1129
+ def SRT(map, force=false)
1130
+ #check if the field is optional and randomly generate it of skip
1131
+ return '' if(!generate?(map, force))
1132
+
1133
+ # <sort-by field (ST)>
1134
+ # <sequencing (ID)>
1135
+ ID(map,true)
1136
+ end
1137
+
1138
+ #Generate HL7 ST (string data) data type. A ST contains a single String value.
1139
+ def ST(map, force=false)
1140
+ #check if the field is optional and randomly generate it of skip
1141
+ return '' if(!generate?(map, force))
1142
+
1143
+ # TODO add provider type ln 840 ROL
1144
+ case map[:description]
1145
+ when 'Guarantor SSN','Insured’s Social Security Number','Medicare health ins Card Number','Military ID Number', 'Contact Person Social Security Number'
1146
+ generate_length_bound_id(9)
1147
+ when 'Allergy Reaction Code'
1148
+ yml['codes.allergens'].keys.sample()
1149
+ when 'Strain'
1150
+ #PID.35 – PID.38 should be always blank, as they deal with animals, not humans.
1151
+ when 'AGENT ORANGE EXPOSURE LOCATION'
1152
+ #ZEL.29 should be 1 digit integer.
1153
+ generate_length_bound_id(1)
1154
+ else
1155
+ #Case when max_len overrides requirements
1156
+ len = safe_len(map[:max_length], @@REQ_LEN_3_DGTS)
1157
+ (!blank?(map[:codetable]))? get_coded_value(map): generate_length_bound_id(len)
1158
+ end
1159
+
1160
+ end
1161
+
1162
+ # An Elementary Data Type Format: HH[MM[SS[.S[S[S[S]]]]]][+/-ZZZZ]
1163
+ # EX: 160140.761
1164
+ def TM(map, force=false)
1165
+ #check if the field is optional and randomly generate it of skip
1166
+ return '' if(!generate?(map, force))
1167
+ to_datetime(map).strftime('%H%M%S.%L')
1168
+ end
1169
+
1170
+ #Generate an HL7 TN (telephone number) data type. A TN contains a single String value.
1171
+ def TN(map, force=false)
1172
+ #check if the field is optional and randomly generate it of skip
1173
+ return '' if(!generate?(map, force))
1174
+
1175
+ # TN tn = (TN) map.fld
1176
+ # tn.setValue(phones.getAt(Math.abs(random.nextInt()%phones.size())))
1177
+ @yml['address.phones'].sample # pick a phone
1178
+ end
1179
+
1180
+ # Timing quantity
1181
+ def TQ(map, force=false)
1182
+ #check if the field is optional and randomly generate it of skip
1183
+ return '' if(!generate?(map, force))
1184
+
1185
+ # <quantity (CQ)>
1186
+ # CQ(reset_map_attr(map, :codetable, '335'), true)
1187
+ CQ(map,true)
1188
+ # <interval (RI)
1189
+ # < duration (ST)>
1190
+ # <start date/time (TS)>
1191
+ # <end date/time (TS)>
1192
+ # <priority (ST)>
1193
+ # <condition (ST)>
1194
+ # <text (TX)>
1195
+ # <conjunction component (ID)>
1196
+ # <order sequencing (OSD)>
1197
+ # <occurrence duration (CE)>
1198
+ # <total occurrences (NM)>
1199
+ end
1200
+
1201
+ #Generate HL7 TS (time stamp), within number of weeks in the future or past
1202
+ def TS(map, force=false)
1203
+ #check if the field is optional and randomly generate it of skip
1204
+ return '' if(!generate?(map, force))
1205
+
1206
+ #time of an event (TSComponentOne)
1207
+ ts = to_datetime(map).strftime('%Y%m%d%H%M%S.%L') #format('YYYYMMDDHHSS.SSS')Date.iso8601
1208
+ # TS is lenght sensetive, check max_len and trim appropriate
1209
+ if (ts.size > (maxlen = (map[:max_length]) ? map[:max_length].to_i : ts.size))
1210
+ # puts ts
1211
+ # ts = ts[0,maxlen]
1212
+ ts = ts.slice(0...maxlen)
1213
+ end
1214
+ return ts
1215
+ end
1216
+
1217
+ #Generate HL7 TX (text data) data type. A TX contains a single String value.
1218
+ def TX(map, force=false)
1219
+ #check if the field is optional and randomly generate it of skip
1220
+ return '' if(!generate?(map, force))
1221
+ # @@GENERAL_TEXT
1222
+ ID(map,true)
1223
+ end
1224
+
1225
+ #Generate an HL7 UVC (Value code and amount) data type.
1226
+ def UVC(map, force=false)
1227
+ #check if the field is optional and randomly generate it of skip
1228
+ return '' if(!generate?(map, force))
1229
+ val =[]
1230
+ # UVC uvc = ((UVC)map.fld)
1231
+ #value code (IS)
1232
+ val << IS(map, true)
1233
+ # is(['fld'=>uvc.getComponent(0), 'required'=>'R', 'codetable'=>map.codetable])
1234
+ #value amount (NM)
1235
+ # nm(['fld'=>uvc.getComponent(1), 'required'=>'R'])
1236
+ val << NM({},true)# description confuses NM generator
1237
+ val.join(@@HAT)
1238
+ end
1239
+
1240
+ # Visiting hours
1241
+ def VH(map, force=false)
1242
+ #check if the field is optional and randomly generate it of skip
1243
+ return '' if(!generate?(map, force))
1244
+
1245
+ val =[]
1246
+ # <start day range (ID)>
1247
+ val << ID(map,true)
1248
+ # <end day range (ID)>
1249
+ val << ID(map,true)
1250
+ # <start hour range (TM)>
1251
+ # <end hour range (TM)>
1252
+ val.join(@@HAT)
1253
+ end
1254
+
1255
+ #Generate an HL7 VID (version identifier) data type.
1256
+ def VID(map, force=false)
1257
+ #check if the field is optional and randomly generate it of skip
1258
+ return '' if(!generate?(map, force))
1259
+
1260
+ # VID vid = ((VID)map.fld)
1261
+ #version ID (ID)
1262
+ ID(map, true)
1263
+ #id(['fld'=>vid.getComponent(0), 'required'=>'R', 'codetable'=>map.codetable])
1264
+
1265
+ # internationalization code (CE)
1266
+ # international version ID (CE)
1267
+ end
1268
+
1269
+ # Value qualifier
1270
+ def VR(map, force=false)
1271
+ #check if the field is optional and randomly generate it of skip
1272
+ return '' if(!generate?(map, force))
1273
+ # <first data code value (ST)>
1274
+ ST(map,true)
1275
+ # <Last data code value (ST)>
1276
+ end
1277
+
1278
+ #Channel identifier
1279
+ def WVI(map, force=true)
1280
+ #check if the field is optional and randomly generate it of skip
1281
+ return '' if(!generate?(map, force))
1282
+
1283
+ # <channel number (NM)>
1284
+ NM(map, true)
1285
+ # <channel name (ST)>
1286
+ end
1287
+
1288
+
1289
+ # def WVS(map, force=false)
1290
+ # #check if the field is optional and randomly generate it of skip
1291
+ # return '' if(!autoGenerate?(map,force))
1292
+ # end
1293
+
1294
+ #Generate HL7 XAD (extended address)
1295
+ def XAD(map, force=false)
1296
+ #check if the field is optional and randomly generate it of skip
1297
+ return '' if(!generate?(map, force))
1298
+
1299
+ # same as address AD
1300
+ AD(map,true)
1301
+ #street address (SAD) (SAD)
1302
+ #other designation (ST)
1303
+ #city (ST)
1304
+ #state or province (ST)
1305
+ #zip or postal code (ST)
1306
+ #country (ID)
1307
+ #address type (ID)
1308
+ #other geographic designation (ST)
1309
+ #county/parish code (IS)
1310
+ #census tract (IS)
1311
+ #address representation code (ID)
1312
+ #address validity range (DR)
1313
+ end
1314
+
1315
+ # Generate HL7 XCN (extended composite ID number and name for persons)
1316
+ def XCN(map, force=false)
1317
+ #check if the field is optional and randomly generate it of skip
1318
+ return '' if(!generate?(map, force))
1319
+
1320
+ val=[]
1321
+ # XCN xcn = (XCN) map.fld
1322
+ # ID number (ST) (ST)
1323
+ val << ID(map, true)
1324
+ # xcn.getIDNumber().setValue(Math.abs(random.nextInt() % 300).toString())
1325
+
1326
+ val << PN(map, true)
1327
+ # family name (FN)
1328
+ # val << FN(map, true)
1329
+ # given name (ST)
1330
+ # val << @yml['person.names.first'].sample
1331
+ # second and further given names or initials thereof (ST)
1332
+ # val << @@INITIALS.to_a.sample
1333
+ # suffix (e.g., JR or III) (ST)
1334
+ # prefix (e.g., DR) (ST)
1335
+ # degree (e.g., MD) (IS)
1336
+
1337
+ # source table (IS)
1338
+ # assigning authority (HD)
1339
+ # name type code (ID)
1340
+ # identifier check digit (ST)
1341
+ # code identifying the check digit scheme employed (ID)
1342
+ # identifier type code (IS) (IS)
1343
+ # assigning facility (HD)
1344
+ # Name Representation code (ID)
1345
+ # name context (CE)
1346
+ # name validity range (DR)
1347
+ # name assembly order (ID)
1348
+ val.join(@@HAT)
1349
+ end
1350
+
1351
+ #Generate an HL7 XON (extended composite name and identification number for organizations) data type.
1352
+ def XON(map, force=false)
1353
+ #check if the field is optional and randomly generate it of skip
1354
+ return '' if(!generate?(map, force))
1355
+
1356
+ # XON xtn = (XON) map.fld
1357
+ val=[]
1358
+ #organization name (ST)
1359
+ val << ST(map, true)
1360
+ # st(['fld'=>xtn.getComponent(0), 'required'=>'R', 'codetable'=>map.codetable])
1361
+ #organization name type code (IS)
1362
+ val << ''
1363
+ #ID number (NM) (NM)
1364
+ val << NM(map, true)
1365
+ #nm(['fld'=>xtn.getComponent(2), 'required'=>'R'])
1366
+ #check digit (NM) (ST)
1367
+ #code identifying the check digit scheme employed (ID)
1368
+ #assigning authority (HD)
1369
+ #identifier type code (IS) (IS)
1370
+ #assigning facility ID (HD)
1371
+ #Name Representation code (ID)
1372
+ val.join(@@HAT)
1373
+ end
1374
+
1375
+ #Generate an HL7 XPN (extended person name) data type. This type consists of the following components=>
1376
+ def XPN(map, force=false)
1377
+ #check if the field is optional and randomly generate it of skip
1378
+ return '' if(!generate?(map, force))
1379
+
1380
+ # val=[]
1381
+ #family name (FN)
1382
+ # val << FN(map, true)
1383
+ # fn(['fld'=> xpn.getComponent(0),'required'=>'R'])
1384
+ #given name (ST)
1385
+ # val << @yml['person.names.first'].sample
1386
+ #xpn.givenName.setValue(firstNames.getAt(Math.abs(random.nextInt()%firstNames.size())));
1387
+ #xpn.getComponent(1).setValue(firstNames.getAt(Math.abs(random.nextInt()%firstNames.size())))
1388
+ #second and further given names or initials thereof (ST)
1389
+ # val << @@INITIALS.to_a.sample
1390
+ #xpn.secondAndFurtherGivenNamesOrInitialsThereof.setValue('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.getAt(Math.abs(random.nextInt()%26)))
1391
+ #suffix (e.g., JR or III) (ST)
1392
+ #prefix (e.g., DR) (ST)
1393
+ #degree (e.g., MD) (IS)
1394
+ PN(map,true)
1395
+
1396
+ #name type code (ID)
1397
+ # val.join(@@HAT)
1398
+
1399
+ end
1400
+
1401
+ #Generate HL7 XTN (extended telecommunication number)
1402
+ def XTN(map, force=false)
1403
+ #check if the field is optional and randomly generate it of skip
1404
+ return '' if(!generate?(map, force))
1405
+
1406
+ #[(999)] 999-9999 [X99999][C any text] (TN)
1407
+ TN(map, true)
1408
+ #xtn.get9999999X99999CAnyText().setValue(phones.getAt(Math.abs(random.nextInt()%phones.size())))
1409
+ # telecommunication use code (ID)
1410
+ # telecommunication equipment type (ID) (ID)
1411
+ # Email address (ST)
1412
+ # Country Code (NM)
1413
+ # Area/city code (NM)
1414
+ # Phone number (NM)
1415
+ # Extension (NM)
1416
+ # any text (ST)
1417
+ end
1418
+
1419
+ # def xx(map, force=false)
1420
+ # #check if the field is optional and randomly generate it of skip
1421
+ # return '' if(!autoGenerate?(map,force))
1422
+ # end
1423
+
1424
+ # End of Data Types #
1425
+
1426
+ # Value of coded table returned as as single value
1427
+ def get_coded_value(attributes)
1428
+ codes = get_code_table(attributes)
1429
+
1430
+ # puts codes
1431
+ #Apply rules to find a value and description
1432
+ map = apply_rules(codes, attributes)
1433
+ #Return value only
1434
+ return map[:value]
1435
+ end
1436
+
1437
+ # Values and Description from code table returned as a pair.
1438
+ def get_coded_map(attributes)
1439
+ codes = get_code_table(attributes)
1440
+
1441
+ # puts codes
1442
+ #Apply rules to find a value and description
1443
+ #Returns map with code and description
1444
+ apply_rules(codes, attributes)
1445
+ end
1446
+
1447
+ def get_code_table(attributes)
1448
+ codeTable = get_name_without_base(attributes[:codetable])
1449
+ codes = @pp.get_code_table(codeTable)
1450
+ # Case when we are looking for code values defined in base schema for types
1451
+ # which are in custom/primary schema or the othere way around.
1452
+ if(@hp && (codes.first == Utils::DATA_LOOKUP_MIS))
1453
+ codes = @hp.get_code_table(codeTable)
1454
+ end
1455
+ return codes
1456
+ end
1457
+
1458
+ # Handle range values specified by '...' sequence, including empty range
1459
+ #TODO refactor candidate
1460
+ def apply_rules(codes, attributes)
1461
+ #safety check, no codes returns an empty map
1462
+ return {} if blank?(codes)
1463
+
1464
+ #index of random element
1465
+ idx = sample_index(codes.size)
1466
+ code = codes[idx][:value]
1467
+ description = codes[idx][:description]
1468
+
1469
+ if (code.include?(@@RANGE_INDICATOR))
1470
+ code = handle_ranges(code)
1471
+ end
1472
+
1473
+ if (code.size > (maxlen = (attributes[:max_length]) ? attributes[:max_length].to_i : code.size))
1474
+ #remove all codes wich values violate
1475
+ #codes.select! {|it| it[:value].size <= maxlen }
1476
+ code, description = handle_length(codes, maxlen)
1477
+ end
1478
+
1479
+ # got to have code, get an id, most basic - TODO: verify this.
1480
+ # if(Utils.blank?(code))
1481
+ # code, description = ID({},true), ''
1482
+ # end
1483
+ # return code, description
1484
+
1485
+ # puts code + ', ' + description
1486
+ return {:value => code, :description => description}
1487
+ end
1488
+
1489
+ def handle_length(codes, maxlen)
1490
+ idx = codes.find_index { |it| it[:value].size <= maxlen }
1491
+
1492
+ if (!idx)
1493
+ code, description = '', ''
1494
+ else
1495
+ # puts codes
1496
+ code = codes[idx][:value]
1497
+ description = codes[idx][:description]
1498
+ end
1499
+ return code, description
1500
+ end
1501
+
1502
+ def handle_ranges(code)
1503
+ # if (code.include?(@@RANGE_INDICATOR))
1504
+ ends = code.delete(' ').split('...').map { |it| it }
1505
+ if (ends.empty?) # This is indication that codetable does not have any values
1506
+ code = ''
1507
+ else #Handle range values, build range and pick random value
1508
+ # range = ends.first..ends.last
1509
+ # code = range.to_a.sample
1510
+
1511
+ # per Galina: Invalid value 'Q8' appears in segment 4:PD1, field 20, repetition 1, component 1, subcomponent 1,
1512
+ # but does not appear in code table 2.4:141.
1513
+ # I think you had to fix this one before to pull only the first and the last values from each row.
1514
+ code = ends.sample
1515
+ end
1516
+ return code
1517
+ end
1518
+
1519
+ # Returns randomly generated Id of required length, of single digit id
1520
+ def generate_length_bound_id(maxlen, str=@@random.rand(100000000).to_s)
1521
+ idx = maxlen
1522
+ if(maxlen > str.size)
1523
+ str = str.ljust(maxlen,'0')
1524
+ idx = str.size
1525
+ end
1526
+ #safe fail
1527
+ #this handles case when maxlen is less then 1
1528
+ idx = [0,idx-1].max
1529
+ return str[0..idx]
1530
+ end
1531
+
1532
+
1533
+ # # Returns randomly generated Id of required length, of single digit id
1534
+ # def generateLengthBoundId1(map, str=@@random.rand(100000000).to_s)
1535
+ # (!Utils.blank?(map[:codetable]))? get_coded_value(map): @@random.rand(@@UP_TO_3_DGTS).to_s
1536
+ #
1537
+ # map[:maxlen]
1538
+ # end
1539
+
1540
+ # If field are X,W,B (Not Supported, Withdrawn Fields or Backward Compatible) returns false.
1541
+ # Conditional (C) ?
1542
+ # For Optional field (O) makes random choice
1543
+ # R - Required returns true
1544
+ # def autoGenerate?(map, force=false)
1545
+ def generate?(map, force=false)
1546
+ # return true
1547
+ return true if(force) #forced generation, ignore mappings
1548
+
1549
+ #look up in the mapping
1550
+ case map[:required]
1551
+ when 'X','W','B' then false;
1552
+ when 'R' then true;
1553
+ when 'O' then [true,false].sample();
1554
+ else false ;
1555
+ end
1556
+ # if(['X','W','B'].include?(map[:required]))then return false end
1557
+ # if(map[:required] =='R') then return true end
1558
+ # if(map[:required] == 'O') then return true end #random boolean
1559
+ end
1560
+
1561
+ # @return DateTime generated with consideration of description string for dates in the future
1562
+ def to_datetime(map)
1563
+ #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
1564
+ isFutureEvent = !blank?(map[:description])&& map[:description].include?('End') #so 'Role End Date/Time'
1565
+ seed = 365 #seed bounds duration of time to a year
1566
+ days = @@random.rand(seed)
1567
+
1568
+ if(map[:description]=='Date/Time Of Birth')
1569
+ isFutureEvent = false
1570
+ years = rand(30..50)
1571
+ days = days + 365*years # make a person 30 years old
1572
+ end
1573
+
1574
+ (isFutureEvent) ? DateTime.now().next_day(days) : DateTime.now().prev_day(days)
1575
+ end
1576
+
1577
+ # convention method to modify values of attirbutes
1578
+ def reset_map_attr(map, key, value)
1579
+ map[key.to_sym]=value
1580
+ return map
1581
+ end
1582
+
1583
+ end