roqua-healthy 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.document +3 -0
  3. data/.gitignore +8 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +31 -0
  6. data/.yardopts +1 -0
  7. data/ChangeLog.md +10 -0
  8. data/Gemfile +8 -0
  9. data/Guardfile +11 -0
  10. data/LICENSE.txt +20 -0
  11. data/README.md +31 -0
  12. data/Rakefile +33 -0
  13. data/bin/get +8 -0
  14. data/bin/get_old_mirth_processing_result +6 -0
  15. data/bin/get_xml_for_patient +6 -0
  16. data/bin/parse_local_xml +9 -0
  17. data/healthy.gemspec +42 -0
  18. data/lib/roqua/healthy/a19/address_parser.rb +46 -0
  19. data/lib/roqua/healthy/a19/cdis_name_parser.rb +23 -0
  20. data/lib/roqua/healthy/a19/correct_patient_check.rb +18 -0
  21. data/lib/roqua/healthy/a19/fetcher.rb +61 -0
  22. data/lib/roqua/healthy/a19/name_parser.rb +52 -0
  23. data/lib/roqua/healthy/a19/response_parser.rb +27 -0
  24. data/lib/roqua/healthy/a19/response_validator.rb +45 -0
  25. data/lib/roqua/healthy/a19/transformer.rb +102 -0
  26. data/lib/roqua/healthy/a19.rb +25 -0
  27. data/lib/roqua/healthy/errors.rb +16 -0
  28. data/lib/roqua/healthy/message_cleaner.rb +44 -0
  29. data/lib/roqua/healthy/version.rb +6 -0
  30. data/lib/roqua/healthy.rb +14 -0
  31. data/lib/roqua-healthy.rb +1 -0
  32. data/spec/fixtures/cdis_gerda_geit.xml +181 -0
  33. data/spec/fixtures/cdis_jan_fictief.xml +177 -0
  34. data/spec/fixtures/cdis_piet_fictief.xml +177 -0
  35. data/spec/fixtures/comez_patient.xml +259 -0
  36. data/spec/fixtures/medo_patient.xml +101 -0
  37. data/spec/fixtures/oru-requests/spsy1218j.hl7 +82 -0
  38. data/spec/fixtures/oru-requests/spsy1218o.hl7 +76 -0
  39. data/spec/fixtures/oru-requests/spsy1218o2.hl7 +77 -0
  40. data/spec/fixtures/oru-requests/spsy411o.hl7 +73 -0
  41. data/spec/fixtures/oru-requests/spsy411o2.hl7 +71 -0
  42. data/spec/fixtures/oru-requests/spsyl.hl7 +71 -0
  43. data/spec/fixtures/oru-responses/xmcare_nack.hl7 +5 -0
  44. data/spec/fixtures/user_patient.xml +251 -0
  45. data/spec/fixtures/user_patient_with_gsm_and_email.xml +281 -0
  46. data/spec/fixtures/user_patient_with_maiden_name.xml +251 -0
  47. data/spec/fixtures/xmcare_impersonating_cdis.xml +245 -0
  48. data/spec/fixtures/xmcare_patient.xml +252 -0
  49. data/spec/fixtures/xmcare_patient_email_in_field_number_four.xml +245 -0
  50. data/spec/fixtures/xmcare_patient_not_found.xml +93 -0
  51. data/spec/fixtures/xmcare_patient_with_maiden_name.xml +252 -0
  52. data/spec/fixtures/xmcare_patient_without_birthdate.xml +250 -0
  53. data/spec/fixtures/xmcare_timeout_waiting_for_ack.xml +4 -0
  54. data/spec/healthy_spec.rb +7 -0
  55. data/spec/integration/cdis_spec.rb +72 -0
  56. data/spec/integration/comez_spec.rb +26 -0
  57. data/spec/integration/medo_spec.rb +26 -0
  58. data/spec/integration/mirth_spec.rb +17 -0
  59. data/spec/integration/user_spec.rb +72 -0
  60. data/spec/integration/xmcare_spec.rb +127 -0
  61. data/spec/spec_helper.rb +29 -0
  62. data/spec/support/fixtures.rb +14 -0
  63. data/spec/unit/a19/address_parser_spec.rb +132 -0
  64. data/spec/unit/a19/correct_patient_check_spec.rb +22 -0
  65. data/spec/unit/a19/fetcher_spec.rb +54 -0
  66. data/spec/unit/a19_spec.rb +4 -0
  67. data/spec/unit/message_cleaner_spec.rb +47 -0
  68. 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
@@ -0,0 +1,3 @@
1
+ -
2
+ ChangeLog.md
3
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ .idea
2
+ .yardoc
3
+ Gemfile.lock
4
+ doc/
5
+ pkg/
6
+ tmp/
7
+ vendor/cache/*.gem
8
+ tmp/rspec_guard_result
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless ARGV[0] && ARGV[1]
4
+ puts "Usage: get PATIENT_ID IP:PORT"
5
+ exit 1
6
+ end
7
+
8
+ exec "bin/get_xml_for_patient #{ARGV[0]} '#{ARGV[1]}' | ruby -Ilib bin/parse_local_xml"
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ COMMAND="curl --data \"method=A19&patient_id=$1\" \"http://$2\""
4
+ XML=$(ssh x3mirth "$COMMAND")
5
+
6
+ echo $XML | xmllint --format -
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ COMMAND="curl --data \"method=A19&application=healthy&patient_id=$1\" \"http://$2\""
4
+ XML=$(ssh x3mirth "$COMMAND")
5
+
6
+ echo $XML | xmllint --format -
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'healthy'
4
+ require 'ap'
5
+
6
+ body = ARGF.read
7
+ message = Hash.from_xml(body).fetch("HL7Message") { Hash.new }
8
+
9
+ ap Healthy::A19::Transformer.new(message).to_patient
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'