ez7gen 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/ez7gen +6 -0
- data/lib/ez7gen.rb +23 -0
- data/lib/ez7gen/config/schema/2.4/2.4.HL7.xml +13904 -0
- data/lib/ez7gen/config/schema/2.4/VAZ2.4.HL7.xml +3085 -0
- data/lib/ez7gen/config/schema/2.4/added/coded-tables.xml +730 -0
- data/lib/ez7gen/config/schema/2.4/rules/2.4.HL7.yml +4 -0
- data/lib/ez7gen/config/schema/2.4/rules/VAZ2.4.HL7.yml +6 -0
- data/lib/ez7gen/config/schema/2.5/2.5.HL7.xml +10008 -0
- data/lib/ez7gen/config/schema/2.5/VAZ2.5.HL7.xml +7 -0
- data/lib/ez7gen/config/schema/2.5/added/coded-tables.xml +549 -0
- data/lib/ez7gen/config/schema/readme.txt +0 -0
- data/lib/ez7gen/config/templates/2.4/eiv table update-mfn_m01 20151201.xml +416 -0
- data/lib/ez7gen/config/templates/2.4/eiv table update-mfn_y01.xml +416 -0
- data/lib/ez7gen/config/templates/2.4/eiv-ec-MFN_X01_reg request 20160126.xml +659 -0
- data/lib/ez7gen/config/templates/2.4/examples/ADT_A60.txt +69 -0
- data/lib/ez7gen/config/templates/2.4/examples/eiv table update-mfn_m01 20151201.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_dss_units-query_qbp_q13-qbp_q13.txt +26 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_ecs_procedures_query_qbp_q13-qbp_q13.txt +26 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_patient eligibility_response-rsp_k11-080714.txt +44 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_diagnosis_query_qbp_q11-qbp_q11.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_diagnosis_response_rsp_k11-rsp_k11.txt +42 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_dss_units_response_rtb_k13-rtb_k13.txt +49 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_ecs_filer_request_dft_p03-dft_p03-080714.txt +31 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_ecs_filer_response_ack_p03-ack_p03.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_esc_procedures_response_rtb_k13-rtb_k13.txt +40 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patient_eclass_query_qbp_q11-qbp_q11.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patient_problems_query_qbp_q11-qbp_q11.txt +21 -0
- data/lib/ez7gen/config/templates/2.4/examples/mhvsm_standardhl7lib_patinet_problems_response_rsp_k11-rsp_k11.txt +33 -0
- data/lib/ez7gen/config/templates/2.4/examples/orur01rvbecv2.txt +31 -0
- data/lib/ez7gen/config/templates/2.4/examples/sqwm vitals-oru_ro1.txt +52 -0
- data/lib/ez7gen/config/templates/2.4/examples/vista sqwm-adt_a60.txt +40 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_dss_units_query_qbp_q13-qbp_q13.xml +312 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_ecs_procedures_query_qbp_q13-qbp_q13.xml +314 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_patient eligibility_response-rsp_k11-080714.xml +640 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_diagnosis_query_qbp_q11-qbp_q11.xml +284 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_diagnosis_response_rsp_k11-rsp_k11-rsp_k11.xml +563 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_dss_units_response_rtb_k13-rtb_k13-rtb_k13.xml +365 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_filer_request_dft_p03-dft_p03-080714.xml +2172 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_filer_response_ack_p03-ack_p03.xml +269 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_ecs_procedures_response_rtb_k13-rtb_k13-rtb_k13.xml +354 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_eclass_query_qbp_q11-qbp_q11.xml +284 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_problems_query_qbp_q11-qbp_q11.xml +282 -0
- data/lib/ez7gen/config/templates/2.4/mhvsm_standardhl7lib_patient_problems_response_rsp_k11-rsp_k11-rsp_k11.xml +565 -0
- data/lib/ez7gen/config/templates/2.4/orur01rvbecv2.xml +1529 -0
- data/lib/ez7gen/config/templates/2.4/sqwm vitals-oru_r01.xml +2975 -0
- data/lib/ez7gen/config/templates/2.4/vista sqwm-adt_a60.xml +1360 -0
- data/lib/ez7gen/message_factory.rb +142 -0
- data/lib/ez7gen/msg_error_handler.rb +33 -0
- data/lib/ez7gen/profile_parser.rb +321 -0
- data/lib/ez7gen/resources/properties-with-comments.yml +51 -0
- data/lib/ez7gen/resources/properties.yml +325 -0
- data/lib/ez7gen/service/2.4/dynamic_field_generator.rb +45 -0
- data/lib/ez7gen/service/2.4/field_generator.rb +1586 -0
- data/lib/ez7gen/service/2.5/field_generator.rb +75 -0
- data/lib/ez7gen/service/base_field_generator.rb +451 -0
- data/lib/ez7gen/service/segment_generator.rb +218 -0
- data/lib/ez7gen/service/segment_picker.rb +147 -0
- data/lib/ez7gen/service/template_generator.rb +213 -0
- data/lib/ez7gen/service/type_aware_field_generator.rb +1583 -0
- data/lib/ez7gen/service/utils.rb +75 -0
- data/lib/ez7gen/structure_parser.rb +331 -0
- data/lib/ez7gen/version.rb +38 -0
- data/test/Additional Tables with values_v1.1.txt +1653 -0
- data/test/added_shema_test.rb +143 -0
- data/test/app-tmp.rb +225 -0
- data/test/at.txt +1 -0
- data/test/backburner.zip +0 -0
- data/test/codes.txt +262 -0
- data/test/codes1.txt +1240 -0
- data/test/data_types_exploration_test.rb +213 -0
- data/test/dynamic_field_generated_test.rb +292 -0
- data/test/message_factory_24_custom_test.rb +648 -0
- data/test/message_factory_25_test.rb +50 -0
- data/test/message_factory_adm_test.rb +558 -0
- data/test/message_factory_gen_test.rb +63 -0
- data/test/message_factory_lab_test.rb +107 -0
- data/test/message_factory_pharm_test.rb +121 -0
- data/test/message_factory_template_24_test.rb +730 -0
- data/test/message_factory_test.rb +220 -0
- data/test/msg_error_handler_test.rb +59 -0
- data/test/profile_parser_test.rb +542 -0
- data/test/quick_run.rb +880 -0
- data/test/segment_generator_test.rb +656 -0
- data/test/segment_picker_test.rb +279 -0
- data/test/structrure_parser_test.rb +355 -0
- data/test/template_generator_test.rb +164 -0
- data/test/type_aware_field_generator_test.rb +582 -0
- data/test/utils_test.rb +97 -0
- metadata +215 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require 'ruby-hl7'
|
|
2
|
+
require_relative '../ez7gen/profile_parser'
|
|
3
|
+
require_relative '../ez7gen/structure_parser'
|
|
4
|
+
require_relative '../ez7gen/service/segment_generator'
|
|
5
|
+
require_relative '../ez7gen/service/template_generator'
|
|
6
|
+
require_relative '../ez7gen/service/segment_picker'
|
|
7
|
+
require_relative '../ez7gen/service/utils'
|
|
8
|
+
|
|
9
|
+
class MessageFactory
|
|
10
|
+
include Utils
|
|
11
|
+
attr_accessor :templatePath #TODO: refactor
|
|
12
|
+
# attr_accessor :std; :version; :event; :version_store; :loadFactor;
|
|
13
|
+
|
|
14
|
+
def initialize(args)
|
|
15
|
+
@attributes_hash = args
|
|
16
|
+
args.each do |k,v|
|
|
17
|
+
instance_variable_set("@#{k}", v) unless v.nil?
|
|
18
|
+
end
|
|
19
|
+
@loadFactor ||= nil
|
|
20
|
+
# lookup for a template by name if client speified using a template
|
|
21
|
+
@templatePath = (args[:use_template])? self.class.lookup_template_for_event(@std, @use_template):nil
|
|
22
|
+
# @templatePath = self.class.lookup_template_for_event(@std, @event)
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# look up for message template file for an event and standard: 2.4, ADT_A60
|
|
27
|
+
def self.lookup_template_for_event(std, template)
|
|
28
|
+
# properties_file = File.expand_path('../resources/properties.yml', __FILE__)
|
|
29
|
+
# yml = YAML.load_file properties_file
|
|
30
|
+
# path = yml['web.install.dir']
|
|
31
|
+
path = File.expand_path('../', __FILE__)
|
|
32
|
+
path = File.join(path, "config/templates/#{std}/#{template}")
|
|
33
|
+
# path = File.join(path, "config/templates/#{std}/*#{event}*")
|
|
34
|
+
# Dir.glob(path, File::FNM_CASEFOLD).sort.last
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# main factory method
|
|
39
|
+
def generate(useExVal=false)
|
|
40
|
+
parsers = {}
|
|
41
|
+
# get message structure from the schema file for message type and version
|
|
42
|
+
parsers[PRIMARY] = ProfileParser.new(@attributes_hash)
|
|
43
|
+
|
|
44
|
+
# check if current version is not the base version, find the base version and add use it as a base parser
|
|
45
|
+
if(!is_base?(@version))# version is not standard != '2.4'
|
|
46
|
+
parsers[BASE]= get_base_parser()
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#useExVal can be passed from the client - future feature
|
|
50
|
+
return (@templatePath)? generate_message_from_template(parsers, templatePath, useExVal) : generate_message(parsers)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# factory method to build message using schema
|
|
54
|
+
def generate_message(parsers)
|
|
55
|
+
|
|
56
|
+
# get message structure from the schema file for message type and version
|
|
57
|
+
# use primary parser
|
|
58
|
+
structure = parsers[PRIMARY].get_message_definition()
|
|
59
|
+
|
|
60
|
+
# brake message structure into segments, handle groups of segments
|
|
61
|
+
structParser = StructureParser.new()
|
|
62
|
+
structParser.process_segments(structure)
|
|
63
|
+
|
|
64
|
+
# select segments to build, keep required and z segments, random pick optional segments
|
|
65
|
+
profile = structure.split('~')
|
|
66
|
+
segment_picker = SegmentPicker.new(profile, structParser.encodedSegments, @loadFactor)
|
|
67
|
+
segments = segment_picker.pick_segments_to_build()
|
|
68
|
+
|
|
69
|
+
# configure a segment generator
|
|
70
|
+
baseVersion = @std
|
|
71
|
+
segmentGenerator = SegmentGenerator.new(baseVersion, @event, parsers)
|
|
72
|
+
|
|
73
|
+
# msh segment configured by hand, due to many requirements that only apply for this segment
|
|
74
|
+
hl7Msg = HL7::Message.new
|
|
75
|
+
hl7Msg << segmentGenerator.init_msh
|
|
76
|
+
# hl7Msg << segmentGenerator.init_msh(is_base?(@version))
|
|
77
|
+
|
|
78
|
+
#iterate over selected segments and build the entire message
|
|
79
|
+
segments.each{ |segment|
|
|
80
|
+
segmentGenerator.generate(hl7Msg, segment, parsers)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return hl7Msg
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# factory method to build message using MWB templates
|
|
87
|
+
def generate_message_from_template(parsers, templatePath, useExVal)
|
|
88
|
+
|
|
89
|
+
hl7Msg = HL7::Message.new
|
|
90
|
+
|
|
91
|
+
templateGenerator = TemplateGenerator.new(templatePath, parsers)
|
|
92
|
+
|
|
93
|
+
return templateGenerator.generate(hl7Msg, useExVal)
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def is_base?(version)
|
|
99
|
+
isBase = @version_store.find { |s| s[:std] == @std }[:profiles].find { |p| p[:doc] == version }[:std]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Add parser for base version of schema
|
|
103
|
+
def get_base_parser()
|
|
104
|
+
# find version for base standard version - standard with 'std' attribute
|
|
105
|
+
v_base = @version_store.find { |s| s[:std] == @std }[:profiles].find { |p| p[:std]!=nil }[:doc]
|
|
106
|
+
v_base_hash = @attributes_hash.clone()
|
|
107
|
+
v_base_hash[:version] = v_base
|
|
108
|
+
ProfileParser.new(v_base_hash)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def self.to_arr(hl7Msg)
|
|
112
|
+
arr = []
|
|
113
|
+
hl7Msg.each{|it| arr << it.to_s.gsub("\r","\n")}
|
|
114
|
+
return arr
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Identify segment as a part of a group
|
|
118
|
+
def in_group?(groups, idx)
|
|
119
|
+
is_in_group = !groups.select { |group| group.cover?(idx) }.empty?
|
|
120
|
+
# p is_in_group
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
#find ranges of groups before segments collection containing groups can be flattened
|
|
124
|
+
def find_Groups(segments)
|
|
125
|
+
offset = 0
|
|
126
|
+
# groups = segments.map.with_index { |it, idx| (it.instance_of? Array) ? ( groupLn=it.size-1;((offset+idx) .. (idx + offset=offset + groupLn)) ) : nil }.compact
|
|
127
|
+
|
|
128
|
+
groups = segments.map.with_index { |it, idx|
|
|
129
|
+
# groups of sequential segments stored as arrays of segments
|
|
130
|
+
if (it.instance_of? Array)
|
|
131
|
+
# calculate group range based on offset that affected flattening arrays of segments
|
|
132
|
+
groupLen = it.size-1
|
|
133
|
+
groupStart = offset + idx
|
|
134
|
+
groupEnd = idx + offset = offset + groupLen # inline var assignment, just cause I can..
|
|
135
|
+
(groupStart..groupEnd) # group range
|
|
136
|
+
end
|
|
137
|
+
}
|
|
138
|
+
# get rid of nils
|
|
139
|
+
groups.compact()
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'ruby-hl7'
|
|
2
|
+
|
|
3
|
+
class MsgErrorHandler
|
|
4
|
+
|
|
5
|
+
ERR_HEAD = "ERROR <Ens>ErrGeneral:"
|
|
6
|
+
|
|
7
|
+
def handle(resp)
|
|
8
|
+
# segs = message
|
|
9
|
+
#segs = resp.split("\r")
|
|
10
|
+
resp.chomp!()
|
|
11
|
+
|
|
12
|
+
# msg = HL7::Message.new(segs)
|
|
13
|
+
# msg = HL7::Message.parse(message)
|
|
14
|
+
# seg = msg[:ERR]
|
|
15
|
+
if(! resp.include?(ERR_HEAD))
|
|
16
|
+
return nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# if there are errors, make response easy on the eye
|
|
20
|
+
resp.gsub!("\\X0D\\\\X0A\\", "\r")
|
|
21
|
+
resp.gsub!("\r+\r", "\r")
|
|
22
|
+
resp.gsub!(ERR_HEAD, "\r"+ERR_HEAD)
|
|
23
|
+
resp.gsub!("\r\r","\r") # remove empty lines
|
|
24
|
+
|
|
25
|
+
# get errors for toaster display
|
|
26
|
+
errors = resp.split(ERR_HEAD).reject {|e| e.empty?}
|
|
27
|
+
errors.delete_at(0) # remove part of the message before errors
|
|
28
|
+
|
|
29
|
+
return errors
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
require 'ox'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require_relative 'service/utils'
|
|
4
|
+
|
|
5
|
+
class ProfileParser
|
|
6
|
+
include Utils
|
|
7
|
+
|
|
8
|
+
#instance attributes
|
|
9
|
+
attr_reader :base,:xml;
|
|
10
|
+
alias_method :base?, :base;
|
|
11
|
+
|
|
12
|
+
# attr_reader :xml;
|
|
13
|
+
|
|
14
|
+
@@FILTER_ALL = {filter: '.*', group: 'All'}
|
|
15
|
+
FILTER_ADM = {filter: 'ADT_A|QBP_Q2|RSP_K2[1-4]', group: 'Admissions'}
|
|
16
|
+
FILTER_FM = {filter: 'DFT_P03|DFT_P11|DFT_X03', group: 'Financial Management'}
|
|
17
|
+
FILTER_GEN = {filter: 'OSR_Q06|OSQ_Q06|ORG_O20|OMG_O19', group: 'General'}
|
|
18
|
+
FILTER_LAB = {filter: 'ORL_O22|OML_O21|QRY_R02|OUL_R21|ORU_R01', group: 'Laboratory'}
|
|
19
|
+
FILTER_MSR = {filter: 'MFN_M01|MFN_X01|MFN_Y01', group: 'Master Files'}
|
|
20
|
+
FILTER_OBS = {filter: 'OMS_O05', group: 'Order'}
|
|
21
|
+
FILTER_PH = {filter: 'OMP_|ORP_|RDE_|RRE_|RDS_|RRD_|RGV_|RRG_|RAS_|RRA_', group: 'Pharmacy'}
|
|
22
|
+
|
|
23
|
+
# attr_accessor :std; :version; :event; :xml; :version_store;
|
|
24
|
+
# @@HL7_VERSIONS = {'2.4'=>'2.4/2.4-schema.xml', 'vaz2.4'=>'vaz2.4/vaz2.4-schema.xml'}
|
|
25
|
+
#class attribute
|
|
26
|
+
# @@segment_patern = /\[([^\[\]]*)\]/
|
|
27
|
+
@@segment_patern = /\[([^\[\]]*)\]|\{([^\[\]]*)\}/
|
|
28
|
+
|
|
29
|
+
# Child class has a wrapper TODO: Refactor
|
|
30
|
+
# def initialize(version=nil, event=nil)
|
|
31
|
+
def initialize(args)
|
|
32
|
+
args.each do |k,v|
|
|
33
|
+
instance_variable_set("@#{k}", v) unless v.nil?
|
|
34
|
+
end
|
|
35
|
+
# set to false if it has not been set already
|
|
36
|
+
# @base ||= false
|
|
37
|
+
|
|
38
|
+
profile, path = nil
|
|
39
|
+
# if(@version_store)
|
|
40
|
+
profile = @version_store.find{|v| v[:std] == @std}[:profiles].find{|p| p[:doc] == @version }[:path]
|
|
41
|
+
path = @version_store.detect{|v| v[:std] == @std}[:path]
|
|
42
|
+
# else
|
|
43
|
+
# # path = self.class.get_schema_location
|
|
44
|
+
# # profile = File.path(path+ @@HL7_VERSIONS[@version])
|
|
45
|
+
# end
|
|
46
|
+
|
|
47
|
+
@xml = Ox.parse(IO.read(profile))
|
|
48
|
+
|
|
49
|
+
# added = File.path(path+'added.xml')
|
|
50
|
+
begin
|
|
51
|
+
added = File.path(path+'/added/coded-tables.xml')
|
|
52
|
+
@added = Ox.parse(IO.read(added))
|
|
53
|
+
rescue => e
|
|
54
|
+
# puts e.message
|
|
55
|
+
$log.error ("#{self.class.to_s}:#{__method__.to_s}") { e.message }
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# set flag if this is base or custom schema
|
|
61
|
+
@base = (@xml.Export.Document.Category.attributes[:std] == '1')
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# instance methods
|
|
66
|
+
def self.get_schema_location
|
|
67
|
+
#properties_file = File.expand_path('../resources/properties.yml', __FILE__)
|
|
68
|
+
#yml = YAML.load_file properties_file
|
|
69
|
+
#path = yml['web.install.dir'] # set when run intall gem with argument, example: gem install 'c:/ez7Gen/ez7gen-web/config/resources/'
|
|
70
|
+
path = File.expand_path('../', __FILE__)
|
|
71
|
+
path = File.join(path, 'config/schema/')
|
|
72
|
+
#puts path + ' : self.get_schema_location'
|
|
73
|
+
# path = path<<'config/schema/'
|
|
74
|
+
# path = path<<'config/resources/'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.lookup_versions
|
|
78
|
+
path = self.get_schema_location
|
|
79
|
+
puts path + " self.lookup_versions"
|
|
80
|
+
names = Dir.glob("#{path}*").select {|f| File.directory? f}
|
|
81
|
+
versions = names.map{|it| { std: it.sub(path,''), path: it}}
|
|
82
|
+
# for each version
|
|
83
|
+
# look get list of .xml files, except added,own directory for added?
|
|
84
|
+
versions.each{|version|
|
|
85
|
+
profiles = []
|
|
86
|
+
|
|
87
|
+
Dir.glob("#{version[:path]}/**").select {|file| !File.directory? file}.each{|path|
|
|
88
|
+
xml = Ox.parse(IO.read(path))
|
|
89
|
+
# for each schema collect metadata
|
|
90
|
+
profile = xml.Export.Document.attributes
|
|
91
|
+
profile[:doc] = profile.delete(:name) # resolve collision with same keys
|
|
92
|
+
profile.merge!(xml.Export.Document.Category.attributes)
|
|
93
|
+
profile[:path] = path
|
|
94
|
+
profiles << profile
|
|
95
|
+
}
|
|
96
|
+
version[:profiles] = profiles
|
|
97
|
+
}
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.getExclusionFilterRule(std, version)
|
|
101
|
+
path = self.get_schema_location
|
|
102
|
+
rules_file = "#{path}#{std}/rules/#{version}.yml"
|
|
103
|
+
|
|
104
|
+
if File.exists? (rules_file)
|
|
105
|
+
yml = YAML.load_file rules_file
|
|
106
|
+
all = []
|
|
107
|
+
all += (yml['exclusion.errors'])?yml['exclusion.errors']:[]
|
|
108
|
+
all += (yml['exclusion.blacklist'])?yml['exclusion.blacklist']:[]
|
|
109
|
+
else
|
|
110
|
+
[]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
def self.getVersionUrlRule(std, version)
|
|
115
|
+
path = self.get_schema_location
|
|
116
|
+
rules_file = "#{path}#{std}/rules/#{version}.yml"
|
|
117
|
+
|
|
118
|
+
if File.exists? (rules_file)
|
|
119
|
+
yml = YAML.load_file rules_file
|
|
120
|
+
(yml['version.url'])?yml['version.url']:nil
|
|
121
|
+
else
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# find message structure by event type
|
|
128
|
+
def get_message_definition
|
|
129
|
+
msg_type = get_message_structure(@event)
|
|
130
|
+
# p msg_type
|
|
131
|
+
$log.info("#{self.class.to_s}:#{__method__.to_s}") { msg_type }
|
|
132
|
+
definition = @xml.Export.Document.Category.locate('MessageStructure').select{|it| it.attributes[:name] == msg_type }.first.attributes[:definition]
|
|
133
|
+
post_process(definition)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# helper method to handle corner cases
|
|
137
|
+
def post_process(definition)
|
|
138
|
+
|
|
139
|
+
if(@base && (@event == 'OSR_Q06'))
|
|
140
|
+
# 1.If the OSQ_O06 query is about the status of the general messages OMG_O19 General Clinical Order and OML_O21 Lab Order, which only have the OBR segment, then the OSR_O06 should only have the OBR segment in its Order Detail Segment < >.
|
|
141
|
+
# 2.If the OSQ_O06 query is about the status of the Pharmacy order messages (OMP_O09, RDE_O11) that do not have OBR segment, but have RXO segment, then the OSR_O06 Order Detail Segment < > should only contain RXO.
|
|
142
|
+
# definition.sub!(/<(.*?)>/,['OBR','RXO'].sample())
|
|
143
|
+
# puts definition
|
|
144
|
+
definition = definition.sub!(/<(.*?)>/,['OBR','RXO'].sample())
|
|
145
|
+
# puts definition
|
|
146
|
+
elsif(@base && (@event == 'ORL_O22'))
|
|
147
|
+
# work around for Ensemble issue where repeating group causes error in validation, remove repeating {} tag
|
|
148
|
+
# MSH~MSA~[~ERR~]~[~{~NTE~}~]~[~[~PID~{~[~SAC~[~{~OBX~}~]~]~[~{~ORC~[~OBR~[~{~SAC~}~]~]~}~]~}~]~]
|
|
149
|
+
# 'MSH~MSA~[~ERR~]~[~{~NTE~}~]~[~PID~{~[~SAC~[~{~OBX~}~]~]~[~{~ORC~[~OBR~[~{~SAC~}~]~]~}~]~}~]' #simplified
|
|
150
|
+
# 'MSH~MSA~[~ERR~]~[~{~NTE~}~]~[~PID~[~[~SAC~[~{~OBX~}~]~]~[~{~ORC~[~OBR~[~{~SAC~}~]~]~}~]~]~]' #changed
|
|
151
|
+
definition = 'MSH~MSA~[~ERR~]~[~{~NTE~}~]~[~PID~[~[~SAC~[~{~OBX~}~]~]~[~{~ORC~[~OBR~[~{~SAC~}~]~]~}~]~]~]'
|
|
152
|
+
# definition.sub('[~{~ORC~[~OBR~[~{~SAC~}~]~]~}~]', '[~ORC~[~OBR~[~{~SAC~}~]~]~]')
|
|
153
|
+
else
|
|
154
|
+
definition
|
|
155
|
+
end
|
|
156
|
+
return definition
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def get_message_structure(event)
|
|
160
|
+
msg_type = @xml.Export.Document.Category.locate('MessageType').select { |it| it.attributes[:name] == event }.first.attributes[:structure]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
#get hash of attributes for codeTable values
|
|
165
|
+
def get_code_table(tableName)
|
|
166
|
+
#exclude 361,362 sending/receiving app and facility
|
|
167
|
+
#if(tableName in ['72','88','132','264','269','471','9999']){
|
|
168
|
+
# println tableName
|
|
169
|
+
#}
|
|
170
|
+
|
|
171
|
+
#empty hash if no table name
|
|
172
|
+
return [] if blank?(tableName)
|
|
173
|
+
|
|
174
|
+
attributes = lookup_code_table(tableName, @xml)
|
|
175
|
+
|
|
176
|
+
if(blank?(attributes))||(attributes.size == 1 && attributes[0][:value] =='...')
|
|
177
|
+
attributes = lookup_code_table(tableName, @added)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Per Galina, code table values with special characters. Ensemble validation fails.
|
|
181
|
+
# Filter out codes which have html encoded characters - Ensemble has problem handling it.
|
|
182
|
+
# a bit of awkward logic - if either description or value has html encoded chars, remove the item
|
|
183
|
+
attributes.select!{|a| (has_html_encoded_ch?(a[:description]) || has_html_encoded_ch?(a[:value]))?false:true }
|
|
184
|
+
|
|
185
|
+
return attributes
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def lookup_code_table(tableName, path)
|
|
189
|
+
tbl = path.Export.Document.Category.locate('CodeTable').select { |it| it.attributes[:name] == tableName }
|
|
190
|
+
(!blank?(tbl)) ? tbl.first.locate('Enumerate').map { |it| it.attributes } : [Utils::DATA_LOOKUP_MIS]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def get_segment_structure(segment)
|
|
194
|
+
segmentName = get_segment_name(segment)
|
|
195
|
+
# $log.info (segment)
|
|
196
|
+
$log.info("#{self.class.to_s}:#{__method__.to_s}") { segment }
|
|
197
|
+
# node = export.Document.Category.SegmentStructure.find{ it.@name == segmentName}
|
|
198
|
+
# values = @xml.elements.collect("Export/Document/Category/SegmentStructure[@name ='#{segmentName}']/SegmentSubStructure"){|x| x.attributes}
|
|
199
|
+
@xml.Export.Document.Category.locate('SegmentStructure').select{|it| it.attributes[:name] == segmentName }.first.locate('SegmentSubStructure').map{|it| it.attributes}
|
|
200
|
+
#values.each {|it| puts it}
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def lookup_message_types(map=nil, exclusion=nil)
|
|
204
|
+
# match everything if no filter defined
|
|
205
|
+
map ||= @@FILTER_ALL
|
|
206
|
+
|
|
207
|
+
filter = map[:filter]
|
|
208
|
+
messageTypeColl = @xml.Export.Document.Category.locate('MessageType').select{|it| it.attributes[:name] =~/#{filter}/}.map!{|it| it.attributes[:name]}
|
|
209
|
+
if(!blank?(exclusion))
|
|
210
|
+
messageTypeColl = messageTypeColl - exclusion
|
|
211
|
+
end
|
|
212
|
+
messages = messageTypeColl.map{ |el|
|
|
213
|
+
event = (el.split('_')).last
|
|
214
|
+
{
|
|
215
|
+
name: el,
|
|
216
|
+
#chek if there is a match otherwise use the segment name
|
|
217
|
+
code: ((e = @xml.Export.Document.Category.locate('MessageEvent').select{|it| it.attributes[:name] == event}); e!=[] )? (e.first().attributes[:description]): el,
|
|
218
|
+
group: map[:group]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return messages
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def lookup_events(params)
|
|
225
|
+
|
|
226
|
+
#all events for version
|
|
227
|
+
events = @xml.Export.Document.Category.locate('MessageType').map!{|it| it.attributes[:name]}
|
|
228
|
+
|
|
229
|
+
#if there are exclusion rule, remove the exclusions
|
|
230
|
+
if(!blank?(params[:exclusions]))
|
|
231
|
+
events -= params[:exclusions]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
path = params[:templates_path]
|
|
235
|
+
|
|
236
|
+
templates = (!blank?(path))? get_templates(path) : []
|
|
237
|
+
|
|
238
|
+
# go over the events and build attributes of the array
|
|
239
|
+
events_with_attr = events.map{ |el|
|
|
240
|
+
build_event_attributes(el, templates, path)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
#events_with_attr
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# build all the details for event type including template information
|
|
247
|
+
def build_event_attributes(event, templates, path)
|
|
248
|
+
|
|
249
|
+
attr = {}
|
|
250
|
+
attr[:name] = event
|
|
251
|
+
#chek if there is a match otherwise use the segment name
|
|
252
|
+
attr[:code] = get_message_event_desc(event)
|
|
253
|
+
|
|
254
|
+
# check if this event has matching template files
|
|
255
|
+
# event_templates = templates.collect { |template| template =~/#{event}/ } unless blank?(templates)
|
|
256
|
+
# attr[:templates] = templates unless blank?(event_templates)
|
|
257
|
+
if (!blank?(templates))
|
|
258
|
+
# try to match templates to an event by name
|
|
259
|
+
event_templates = templates.select { |template| template =~/#{event}/i }
|
|
260
|
+
# if found set event attribute with template names
|
|
261
|
+
if(!blank?(event_templates))
|
|
262
|
+
|
|
263
|
+
# attr[:templates] = []
|
|
264
|
+
#update event code with template count
|
|
265
|
+
#code = "<b> +#{event_templates.size}" << (event_templates.size == 1) ? "TEMPLATE" : "TEMPLATES" << "</b>"
|
|
266
|
+
# attr[:code] += "<div><b><font color=#337ab7>#{event_templates.size} #{(event_templates.size == 1) ? 'TEMPLATE' : 'TEMPLATES'}</font></b></div>"
|
|
267
|
+
attr[:code] += "<small><b><font color=#337ab7> (#{event_templates.size}#{(event_templates.size == 1) ? 'TEMPLATE' : 'TEMPLATES'})</font></b></small>"
|
|
268
|
+
# if (event_templates.size == 1) then code.chop! end
|
|
269
|
+
# attr[:code] += "#{code}"
|
|
270
|
+
|
|
271
|
+
# if (event_templates.size == 1) then attr[:code].chop! end
|
|
272
|
+
|
|
273
|
+
attr[:templates] = event_templates.collect{ |tmpl|
|
|
274
|
+
desc = Ox.parse(IO.read("#{path}/#{tmpl}")).HL7v2xConformanceProfile.HL7v2xStaticDef.attributes[:EventDesc]
|
|
275
|
+
# desc = (!blank?(desc)) ? desc : "Custom #{event}"
|
|
276
|
+
desc = (!blank?(desc)) ? desc : tmpl.gsub('_',' ').sub('.xml','')
|
|
277
|
+
{:desc => desc.upcase , :file => tmpl}
|
|
278
|
+
}
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
return attr
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# # look up for message template files in a specified directory
|
|
288
|
+
def get_templates(path)
|
|
289
|
+
begin
|
|
290
|
+
Dir.entries(path).select {|f| f =~/.xml/i}.sort
|
|
291
|
+
# rescue => e
|
|
292
|
+
rescue
|
|
293
|
+
[] # handle case when dir is missing
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
#look up for event description
|
|
298
|
+
def get_message_event_desc(event)
|
|
299
|
+
desc = nil
|
|
300
|
+
tbl = @xml.Export.Document.Category.locate('CodeTable').select { |it| (it.attributes[:description] == 'Event type') }
|
|
301
|
+
|
|
302
|
+
if(!blank?(tbl))
|
|
303
|
+
# get event/message name ex: NO2 for ACK_NO2
|
|
304
|
+
event_name = (event.split('_')).last
|
|
305
|
+
for t in tbl # could be multiple tables, iterate
|
|
306
|
+
desc = (( e = t.locate('Enumerate').select{ |it| it.attributes[:value] == event_name }); e!=[])? (e.first().attributes[:description]) : nil
|
|
307
|
+
if(desc)then break end # brake after first match
|
|
308
|
+
end # for
|
|
309
|
+
end # if
|
|
310
|
+
|
|
311
|
+
return desc || event
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# helper method to look up messages for specific groups of messages
|
|
315
|
+
def lookup_message_groups (groups)
|
|
316
|
+
messages = []
|
|
317
|
+
groups.each{ |group| messages += lookup_message_types(group) }
|
|
318
|
+
return messages
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
end
|