roqua-healthy 1.0.0
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/.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 [](https://codeclimate.com/repos/524944dd56b10217490074e8/feed) [](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'
|