roqua-healthy 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.rubocop.yml +31 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +10 -0
- data/Gemfile +8 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +20 -0
- data/README.md +31 -0
- data/Rakefile +33 -0
- data/bin/get +8 -0
- data/bin/get_old_mirth_processing_result +6 -0
- data/bin/get_xml_for_patient +6 -0
- data/bin/parse_local_xml +9 -0
- data/healthy.gemspec +42 -0
- data/lib/roqua/healthy/a19/address_parser.rb +46 -0
- data/lib/roqua/healthy/a19/cdis_name_parser.rb +23 -0
- data/lib/roqua/healthy/a19/correct_patient_check.rb +18 -0
- data/lib/roqua/healthy/a19/fetcher.rb +61 -0
- data/lib/roqua/healthy/a19/name_parser.rb +52 -0
- data/lib/roqua/healthy/a19/response_parser.rb +27 -0
- data/lib/roqua/healthy/a19/response_validator.rb +45 -0
- data/lib/roqua/healthy/a19/transformer.rb +102 -0
- data/lib/roqua/healthy/a19.rb +25 -0
- data/lib/roqua/healthy/errors.rb +16 -0
- data/lib/roqua/healthy/message_cleaner.rb +44 -0
- data/lib/roqua/healthy/version.rb +6 -0
- data/lib/roqua/healthy.rb +14 -0
- data/lib/roqua-healthy.rb +1 -0
- data/spec/fixtures/cdis_gerda_geit.xml +181 -0
- data/spec/fixtures/cdis_jan_fictief.xml +177 -0
- data/spec/fixtures/cdis_piet_fictief.xml +177 -0
- data/spec/fixtures/comez_patient.xml +259 -0
- data/spec/fixtures/medo_patient.xml +101 -0
- data/spec/fixtures/oru-requests/spsy1218j.hl7 +82 -0
- data/spec/fixtures/oru-requests/spsy1218o.hl7 +76 -0
- data/spec/fixtures/oru-requests/spsy1218o2.hl7 +77 -0
- data/spec/fixtures/oru-requests/spsy411o.hl7 +73 -0
- data/spec/fixtures/oru-requests/spsy411o2.hl7 +71 -0
- data/spec/fixtures/oru-requests/spsyl.hl7 +71 -0
- data/spec/fixtures/oru-responses/xmcare_nack.hl7 +5 -0
- data/spec/fixtures/user_patient.xml +251 -0
- data/spec/fixtures/user_patient_with_gsm_and_email.xml +281 -0
- data/spec/fixtures/user_patient_with_maiden_name.xml +251 -0
- data/spec/fixtures/xmcare_impersonating_cdis.xml +245 -0
- data/spec/fixtures/xmcare_patient.xml +252 -0
- data/spec/fixtures/xmcare_patient_email_in_field_number_four.xml +245 -0
- data/spec/fixtures/xmcare_patient_not_found.xml +93 -0
- data/spec/fixtures/xmcare_patient_with_maiden_name.xml +252 -0
- data/spec/fixtures/xmcare_patient_without_birthdate.xml +250 -0
- data/spec/fixtures/xmcare_timeout_waiting_for_ack.xml +4 -0
- data/spec/healthy_spec.rb +7 -0
- data/spec/integration/cdis_spec.rb +72 -0
- data/spec/integration/comez_spec.rb +26 -0
- data/spec/integration/medo_spec.rb +26 -0
- data/spec/integration/mirth_spec.rb +17 -0
- data/spec/integration/user_spec.rb +72 -0
- data/spec/integration/xmcare_spec.rb +127 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/fixtures.rb +14 -0
- data/spec/unit/a19/address_parser_spec.rb +132 -0
- data/spec/unit/a19/correct_patient_check_spec.rb +22 -0
- data/spec/unit/a19/fetcher_spec.rb +54 -0
- data/spec/unit/a19_spec.rb +4 -0
- data/spec/unit/message_cleaner_spec.rb +47 -0
- metadata +361 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 55deed92d3c485a1be152d296ee2811252f48947
|
4
|
+
data.tar.gz: 1203252d41c3fb84053d04f0d01a3668fa8ed70f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 47599a271bec411480ccd2be7d7ec53e4e55e9baa04d82ccd6e40cfcdc40224910ac2a62ab26a690d03d4ac0b6dcc5243c547b70e6f4f583922a1c639d5ba680
|
7
|
+
data.tar.gz: da2432e8ba13859e503a8404e578c01e6995cd9ee878de3d00859140bf63f0c09ebc3ba7d5cba7d837d117634549d4db08fc14fda6d5bbcaa1c2d4bfe22dc2ff
|
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format progress
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`.
|
2
|
+
# The point is for the user to remove these configuration records
|
3
|
+
# one by one as the offences are removed from the code base.
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
Excludes:
|
7
|
+
- vendor/**
|
8
|
+
|
9
|
+
Documentation:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
LineLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Encoding:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
StringLiterals:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
SpaceInsideHashLiteralBraces:
|
22
|
+
EnforcedStyle: no_space
|
23
|
+
|
24
|
+
MethodLength:
|
25
|
+
Max: 20
|
26
|
+
|
27
|
+
FinalNewline:
|
28
|
+
Enabled: False
|
29
|
+
|
30
|
+
SignalException:
|
31
|
+
Enabled: False
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "healthy Documentation" --protected
|
data/ChangeLog.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
### 1.0.0 / 2014-01-22
|
2
|
+
|
3
|
+
* Released under the name 'roqua-healthy' to work around name collision on Rubygems.org.
|
4
|
+
This new name also makes it clearer that this gem is probably not very useful to others.
|
5
|
+
Note that everything is now nested under the Roqua::Healthy module instead of Healthy.
|
6
|
+
|
7
|
+
### 0.1.0 / 2013-09-24
|
8
|
+
|
9
|
+
* Initial release:
|
10
|
+
|
data/Gemfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
# Only add dependencies here if they're only used by CI or other services.
|
6
|
+
# Normal development dependencies should go in the gemspec. People shouldn't
|
7
|
+
# need to use bundler to develop on our gem.
|
8
|
+
gem "codeclimate-test-reporter", group: :test, require: nil
|
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
guard 'rspec', cmd: 'bundle exec rspec -f Fuubar' do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/healthy/(.+)\.rb$}) { |m| ["spec/unit/#{m[1]}_spec.rb", "spec/integration"] }
|
4
|
+
watch('spec/spec_helper.rb') { "spec" }
|
5
|
+
watch(%r{^spec/fixtures/([^_]+)_.*.xml}) { |m| "spec/integration/#{m[1]}_spec.rb" }
|
6
|
+
end
|
7
|
+
|
8
|
+
guard :rubocop do
|
9
|
+
watch(%r{.+\.rb$})
|
10
|
+
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
11
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Marten Veldthuis
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Healthy [![Code Climate](https://codeclimate.com/repos/524944dd56b10217490074e8/badges/5dd696b69c4614c83c2d/gpa.png)](https://codeclimate.com/repos/524944dd56b10217490074e8/feed) [![CircleCi](https://circleci.com/gh/roqua/healthy.png?circle-token=ece8f36798b00bc8659d5c76f720b22693d6600a)](https://circleci.com/gh/roqua/healthy)
|
2
|
+
|
3
|
+
## Patient details aka QRY\^A19
|
4
|
+
|
5
|
+
### Usage
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
Healthy::A19.fetch(patient_identifier)
|
9
|
+
```
|
10
|
+
|
11
|
+
### Adding integration tests
|
12
|
+
|
13
|
+
If you find any A19 response that Healthy currently does not handle correctly, please add a fixture and integration test for it.
|
14
|
+
|
15
|
+
* `curl --data "method=A19&application=healthy&patient_id=7767853" "http://10.20.11.100:60401"`
|
16
|
+
* Paste the resulting XML into a new file in `spec/fixtures`. Name this after the specific thing that is different, prefixed with the originating EPD. Don't name this after a specific organization, this repository is open-sourced and our customers might not want to be named publicly here.
|
17
|
+
* Please run it through an XML pretty printer like `xmllint --format` to get indented output.
|
18
|
+
* **<blink>Remove/sanitize/anonymize the XML file where needed.</blink>** We can't do this automatically with a script, because having a script normalize e.g. all names to "Voornaam Achternaam" would defeat the entire point of having multiple fixtures to show the different styles of names we can encounter.
|
19
|
+
* Add an integration spec example that uses it and checks all currently returned values.
|
20
|
+
|
21
|
+
### Manually trying out parsing of a given record
|
22
|
+
|
23
|
+
There are two helpers in `bin`: `get_xml_for_patient` and `parse_local_xml`. The first one takes a patient number and ip+port on the mirth machine, and gets the XML from there. The second parses a chunk of XML from either standard input or a given file.
|
24
|
+
|
25
|
+
These two commands are then chained together by `bin/get`:
|
26
|
+
|
27
|
+
`bin/get 7767853 10.20.11.100:60201`
|
28
|
+
|
29
|
+
## Copyright
|
30
|
+
|
31
|
+
Copyright (c) 2013 Marten Veldthuis. Publicly available under an MIT license. See [LICENSE.txt](https://github.com/roqua/healthy/blob/master/LICENSE.txt) for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
rescue LoadError => e
|
8
|
+
warn e.message
|
9
|
+
warn "Run `gem install bundler` to install Bundler."
|
10
|
+
exit -1
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Bundler.setup(:development)
|
15
|
+
rescue Bundler::BundlerError => e
|
16
|
+
warn e.message
|
17
|
+
warn "Run `bundle install` to install missing gems."
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake'
|
22
|
+
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
RSpec::Core::RakeTask.new
|
25
|
+
|
26
|
+
task :test => :spec
|
27
|
+
task :default => :spec
|
28
|
+
|
29
|
+
require "bundler/gem_tasks"
|
30
|
+
|
31
|
+
require 'yard'
|
32
|
+
YARD::Rake::YardocTask.new
|
33
|
+
task :doc => :yard
|
data/bin/get
ADDED
data/bin/parse_local_xml
ADDED
data/healthy.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path('../lib/roqua/healthy/version', __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "roqua-healthy"
|
7
|
+
gem.version = Roqua::Healthy::VERSION
|
8
|
+
gem.summary = %q{Arranges communication between Mirth and RoQua}
|
9
|
+
gem.description = %q{Receives queries from RoQua, sends them to Mirth, and translates Mirth's responses back into Rubyland.}
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.authors = ["Marten Veldthuis"]
|
12
|
+
gem.email = "marten@roqua.nl"
|
13
|
+
gem.homepage = "https://github.com/roqua/healthy"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_dependency 'activesupport', '~> 3.2'
|
21
|
+
gem.add_dependency 'addressable', '~> 2.3'
|
22
|
+
gem.add_dependency 'roqua-support', '~> 0.1.0'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'bundler', '~> 1.0'
|
25
|
+
gem.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
gem.add_development_dependency 'rspec', '~> 3.0.0.beta1'
|
27
|
+
gem.add_development_dependency 'yard', '~> 0.8'
|
28
|
+
|
29
|
+
# Required for the tests
|
30
|
+
gem.add_development_dependency 'webmock', '~> 1.13'
|
31
|
+
|
32
|
+
# Workflow and tools
|
33
|
+
gem.add_development_dependency 'guard', '~> 2.1'
|
34
|
+
gem.add_development_dependency 'guard-rspec', '~> 4.2.4'
|
35
|
+
gem.add_development_dependency 'listen', '~> 2.1'
|
36
|
+
gem.add_development_dependency 'guard-rubocop', '~> 1.0.1'
|
37
|
+
gem.add_development_dependency 'rubocop', '~> 0.15'
|
38
|
+
gem.add_development_dependency 'fuubar'
|
39
|
+
|
40
|
+
# Documentation generation
|
41
|
+
gem.add_development_dependency 'kramdown', '1.2'
|
42
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Roqua
|
2
|
+
module Healthy
|
3
|
+
module A19
|
4
|
+
class AddressParser
|
5
|
+
attr_reader :message
|
6
|
+
|
7
|
+
def initialize(message)
|
8
|
+
@message = message
|
9
|
+
end
|
10
|
+
|
11
|
+
def address_type
|
12
|
+
return nil unless record
|
13
|
+
record.fetch('PID.11.7')
|
14
|
+
end
|
15
|
+
|
16
|
+
def street
|
17
|
+
return nil unless record
|
18
|
+
record.fetch('PID.11.1').fetch('PID.11.1.1')
|
19
|
+
end
|
20
|
+
|
21
|
+
def city
|
22
|
+
return nil unless record
|
23
|
+
record.fetch('PID.11.3')
|
24
|
+
end
|
25
|
+
|
26
|
+
def zipcode
|
27
|
+
return nil unless record
|
28
|
+
record.fetch('PID.11.5')
|
29
|
+
end
|
30
|
+
|
31
|
+
def country
|
32
|
+
return nil unless record
|
33
|
+
record.fetch('PID.11.6')
|
34
|
+
end
|
35
|
+
|
36
|
+
def record
|
37
|
+
@record = nil
|
38
|
+
@record ||= message.fetch('PID').fetch('PID.11').find { |record| record.fetch('PID.11.7', :unknown_type_of_address_record) == 'M' }
|
39
|
+
@record ||= message.fetch('PID').fetch('PID.11').find { |record| record.fetch('PID.11.7', :unknown_type_of_address_record) == 'H' }
|
40
|
+
@record ||= message.fetch('PID').fetch('PID.11').find { |record| record.fetch('PID.11.7', :unknown_type_of_address_record) == 'L' }
|
41
|
+
@record
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'roqua/healthy/a19/name_parser'
|
2
|
+
|
3
|
+
module Roqua
|
4
|
+
module Healthy
|
5
|
+
module A19
|
6
|
+
# The CDIS EPD returns names in a format different from most other EPD vendors.
|
7
|
+
# This parser overrides some methods that are affected by the differences.
|
8
|
+
class CdisNameParser < NameParser
|
9
|
+
def firstname
|
10
|
+
names[:legal].fetch('PID.5.2')
|
11
|
+
end
|
12
|
+
|
13
|
+
def initials
|
14
|
+
names[:legal].fetch('PID.5.3')
|
15
|
+
end
|
16
|
+
|
17
|
+
def lastname
|
18
|
+
names[:legal].fetch('PID.5.1').fetch('PID.5.1.3')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Roqua
|
2
|
+
module Healthy
|
3
|
+
module A19
|
4
|
+
class CorrectPatientCheck
|
5
|
+
attr_reader :patient_id, :record
|
6
|
+
|
7
|
+
def initialize(patient_id, record)
|
8
|
+
@patient_id = patient_id
|
9
|
+
@record = record
|
10
|
+
end
|
11
|
+
|
12
|
+
def check
|
13
|
+
record[:identities].any? { |i| i[:ident] == patient_id }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
4
|
+
require 'roqua/healthy/a19/response_validator'
|
5
|
+
require 'roqua/healthy/a19/response_parser'
|
6
|
+
|
7
|
+
module Roqua
|
8
|
+
module Healthy
|
9
|
+
module A19
|
10
|
+
class Fetcher
|
11
|
+
attr_reader :patient_id
|
12
|
+
|
13
|
+
def initialize(patient_id)
|
14
|
+
@patient_id = patient_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch
|
18
|
+
response = mirth_response
|
19
|
+
parser = ResponseParser.new(response)
|
20
|
+
|
21
|
+
if ResponseValidator.new(response.code, parser, patient_id).validate
|
22
|
+
parser.fetch("HL7Message")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def mirth_response
|
27
|
+
Net::HTTP.start(remote_url.host, remote_url.port, use_ssl: use_ssl?) do |http|
|
28
|
+
request = Net::HTTP::Post.new(remote_url.path)
|
29
|
+
request.set_form_data(mirth_params)
|
30
|
+
http.request request
|
31
|
+
end
|
32
|
+
rescue ::Timeout::Error, Errno::ETIMEDOUT => error
|
33
|
+
raise ::Roqua::Healthy::Timeout, error.message
|
34
|
+
rescue Errno::EHOSTUNREACH => error
|
35
|
+
raise ::Roqua::Healthy::HostUnreachable, error.message
|
36
|
+
rescue Errno::ECONNREFUSED => error
|
37
|
+
raise ::Roqua::Healthy::ConnectionRefused, error.message
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def mirth_params
|
43
|
+
{'method' => 'A19', 'patient_id' => patient_id.to_s, 'application' => "healthy"}
|
44
|
+
end
|
45
|
+
|
46
|
+
def use_ssl?
|
47
|
+
remote_url.port == 443 || remote_url.scheme == 'https'
|
48
|
+
end
|
49
|
+
|
50
|
+
def remote_url
|
51
|
+
return @remote_url if @remote_url
|
52
|
+
|
53
|
+
url = Addressable::URI.parse(Healthy.a19_endpoint)
|
54
|
+
url.path = "/"
|
55
|
+
|
56
|
+
@remote_url = URI.parse(url.to_s)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Roqua
|
2
|
+
module Healthy
|
3
|
+
module A19
|
4
|
+
class NameParser
|
5
|
+
attr_reader :message
|
6
|
+
|
7
|
+
def initialize(message)
|
8
|
+
@message = message
|
9
|
+
end
|
10
|
+
|
11
|
+
def firstname
|
12
|
+
return unless names[:nick]
|
13
|
+
names[:nick].fetch('PID.5.2')
|
14
|
+
end
|
15
|
+
|
16
|
+
def initials
|
17
|
+
"#{names[:legal].fetch('PID.5.2')} #{names[:legal].fetch('PID.5.3')}".strip
|
18
|
+
end
|
19
|
+
|
20
|
+
def lastname
|
21
|
+
prefix = names[:legal].fetch('PID.5.1').fetch('PID.5.1.2')
|
22
|
+
lastname = names[:legal].fetch('PID.5.1').fetch('PID.5.1.3')
|
23
|
+
"#{prefix} #{lastname}".strip
|
24
|
+
end
|
25
|
+
|
26
|
+
def display_name
|
27
|
+
return unless names[:display]
|
28
|
+
names[:display].fetch('PID.5.1')
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def names
|
34
|
+
names = {}
|
35
|
+
message.fetch('PID').fetch('PID.5').each do |record|
|
36
|
+
case record.fetch('PID.5.7', :unknown_type_of_name_record)
|
37
|
+
when 'L'
|
38
|
+
names[:legal] = record
|
39
|
+
when 'D'
|
40
|
+
names[:display] = record
|
41
|
+
when 'N'
|
42
|
+
names[:nick] = record
|
43
|
+
else
|
44
|
+
# ignore record
|
45
|
+
end
|
46
|
+
end
|
47
|
+
names
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'active_support/core_ext/hash/conversions'
|
2
|
+
|
3
|
+
module Roqua
|
4
|
+
module Healthy
|
5
|
+
module A19
|
6
|
+
class ResponseParser
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch(root)
|
14
|
+
parsed_body[root] || {}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def parsed_body
|
20
|
+
@parsed_body ||= Hash.from_xml(response.body)
|
21
|
+
rescue REXML::ParseException => e
|
22
|
+
raise IllegalMirthResponse, e.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'roqua/healthy/a19/response_parser'
|
2
|
+
|
3
|
+
module Roqua
|
4
|
+
module Healthy
|
5
|
+
module A19
|
6
|
+
class ResponseValidator
|
7
|
+
attr_reader :response_code
|
8
|
+
attr_reader :parser
|
9
|
+
attr_reader :patient_id
|
10
|
+
|
11
|
+
def initialize(response_code, parser, patient_id)
|
12
|
+
@response_code = response_code
|
13
|
+
@parser = parser
|
14
|
+
@patient_id = patient_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate
|
18
|
+
case response_code
|
19
|
+
when '200'
|
20
|
+
validate_200
|
21
|
+
when '500'
|
22
|
+
validate_500
|
23
|
+
else
|
24
|
+
raise ::Roqua::Healthy::UnknownFailure, "Unexpected HTTP response code #{response_code}."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_200
|
29
|
+
failure = parser.fetch("HL7Message")
|
30
|
+
raise ::Roqua::Healthy::PatientNotFound if failure.key?("ERR") && failure.fetch("ERR").fetch("ERR.1").fetch("ERR.1.4").fetch("ERR.1.4.2") =~ /Patient \(@\) niet gevonden\(.*\)/
|
31
|
+
raise ::Roqua::Healthy::MirthErrors::WrongPatient if failure.key?('QRD') && failure.fetch("QRD").fetch("QRD.8").fetch("QRD.8.1") != patient_id
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_500
|
36
|
+
failure = parser.fetch("failure")
|
37
|
+
raise ::Roqua::Healthy::Timeout, failure["error"] if failure["error"] == "Timeout waiting for ACK"
|
38
|
+
raise ::Roqua::Healthy::Timeout, failure["error"] if failure["error"] == "Unable to connect to destination\tSocketTimeoutException\tconnect timed out"
|
39
|
+
raise ::Roqua::Healthy::ConnectionRefused, failure["error"] if failure["error"] == "Unable to connect to destination\tConnectException\tConnection refused"
|
40
|
+
raise ::Roqua::Healthy::UnknownFailure, failure["error"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'roqua/healthy/a19/name_parser'
|
2
|
+
require 'roqua/healthy/a19/cdis_name_parser'
|
3
|
+
require 'roqua/healthy/a19/address_parser'
|
4
|
+
|
5
|
+
module Roqua
|
6
|
+
module Healthy
|
7
|
+
module A19
|
8
|
+
class Transformer
|
9
|
+
attr_reader :message
|
10
|
+
|
11
|
+
def initialize(message)
|
12
|
+
@message = message
|
13
|
+
@message['PID']['PID.3'] = [@message.fetch('PID').fetch('PID.3')].flatten.compact
|
14
|
+
@message['PID']['PID.5'] = [@message.fetch('PID').fetch('PID.5')].flatten.compact
|
15
|
+
@message['PID']['PID.11'] = [@message.fetch('PID').fetch('PID.11')].flatten.compact
|
16
|
+
@message['PID']['PID.13'] = [@message.fetch('PID').fetch('PID.13')].flatten.compact
|
17
|
+
@message = MessageCleaner.new(@message).message
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_patient
|
21
|
+
{
|
22
|
+
status: status,
|
23
|
+
source: source,
|
24
|
+
identities: identities,
|
25
|
+
firstname: name.firstname,
|
26
|
+
initials: name.initials,
|
27
|
+
lastname: name.lastname,
|
28
|
+
display_name: name.display_name,
|
29
|
+
email: email,
|
30
|
+
address_type: address.address_type,
|
31
|
+
street: address.street,
|
32
|
+
city: address.city,
|
33
|
+
zipcode: address.zipcode,
|
34
|
+
country: address.country,
|
35
|
+
birthdate: birthdate,
|
36
|
+
gender: gender,
|
37
|
+
phone_cell: phone_cell
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def status
|
42
|
+
'SUCCESS'
|
43
|
+
end
|
44
|
+
|
45
|
+
def source
|
46
|
+
message.fetch('MSH').fetch('MSH.4').fetch('MSH.4.1')
|
47
|
+
end
|
48
|
+
|
49
|
+
def identities
|
50
|
+
message.fetch('PID').fetch('PID.3').map do |identity|
|
51
|
+
next if identity.fetch('PID.3.1').blank?
|
52
|
+
{ident: identity.fetch('PID.3.1'), authority: identity.fetch('PID.3.5')}
|
53
|
+
end.compact
|
54
|
+
end
|
55
|
+
|
56
|
+
def birthdate
|
57
|
+
birthdate_details = message.fetch('PID').fetch('PID.7')
|
58
|
+
birthdate_details.fetch('PID.7.1') if birthdate_details
|
59
|
+
end
|
60
|
+
|
61
|
+
def email
|
62
|
+
email_record = message.fetch('PID').fetch('PID.13').find do |record|
|
63
|
+
record.fetch('PID.13.2', :unknown_type_of_phone_record) == 'NET'
|
64
|
+
end
|
65
|
+
return nil unless email_record
|
66
|
+
|
67
|
+
email_address = email_record.fetch('PID.13.1', "")
|
68
|
+
email_address = email_record.fetch('PID.13.4', "") if email_address.blank?
|
69
|
+
email_address
|
70
|
+
end
|
71
|
+
|
72
|
+
def phone_cell
|
73
|
+
phone_cell_record = message.fetch('PID').fetch('PID.13').find do |record|
|
74
|
+
record.fetch('PID.13.2', :unknown_type_of_phone_record) == 'ORN'
|
75
|
+
end
|
76
|
+
return nil unless phone_cell_record
|
77
|
+
|
78
|
+
phone_cell_record.fetch('PID.13.1', "")
|
79
|
+
end
|
80
|
+
|
81
|
+
def gender
|
82
|
+
message.fetch('PID').fetch('PID.8').fetch('PID.8.1')
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def name
|
88
|
+
case source
|
89
|
+
when "UMCG", "IMPULSE"
|
90
|
+
CdisNameParser.new(message)
|
91
|
+
else
|
92
|
+
NameParser.new(message)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def address
|
97
|
+
AddressParser.new(message)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Roqua
|
2
|
+
module Healthy
|
3
|
+
module A19
|
4
|
+
extend ::Roqua::Logging
|
5
|
+
|
6
|
+
# Fetches a patient record given a `patient_id` and returns a hash containing
|
7
|
+
# the interesting information that was returned from an upstream `ADR^A19`
|
8
|
+
# response.
|
9
|
+
#
|
10
|
+
# @param patient_id [String] the patient identifier
|
11
|
+
# @return [Hash] the patient details.
|
12
|
+
def self.fetch(patient_id)
|
13
|
+
eventlog.lifecycle('roqua.hl7.a19', patient_id: patient_id) do
|
14
|
+
message = Fetcher.new(patient_id).fetch
|
15
|
+
patient = Transformer.new(message).to_patient
|
16
|
+
patient
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require_relative 'a19/fetcher'
|
24
|
+
require_relative 'a19/transformer'
|
25
|
+
require_relative 'a19/correct_patient_check'
|