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,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