yahl7 0.1.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +47 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  4. data/.github/dependabot.yml +7 -0
  5. data/CHANGELOG.md +64 -0
  6. data/CODE_OF_CONDUCT.md +74 -25
  7. data/CONTRIBUTING.md +47 -0
  8. data/README.md +19 -3
  9. data/lib/yahl7/v2/{segment_field_names.rb → alias_field_names.rb} +14 -7
  10. data/lib/yahl7/v2/alias_person_name.rb +65 -0
  11. data/lib/yahl7/v2/data_type/cnn.rb +29 -0
  12. data/lib/yahl7/v2/data_type/dt.rb +28 -0
  13. data/lib/yahl7/v2/data_type/ft.rb +26 -0
  14. data/lib/yahl7/v2/data_type/ndl.rb +30 -0
  15. data/lib/yahl7/v2/data_type/ts.rb +24 -0
  16. data/lib/yahl7/v2/data_type/xad.rb +31 -0
  17. data/lib/yahl7/v2/data_type/xpn.rb +40 -0
  18. data/lib/yahl7/v2/data_type/xtn.rb +29 -0
  19. data/lib/yahl7/v2/data_type.rb +20 -0
  20. data/lib/yahl7/v2/date_time.rb +1 -1
  21. data/lib/yahl7/v2/error/invalid_format_error.rb +12 -0
  22. data/lib/yahl7/v2/message/adt.rb +2 -0
  23. data/lib/yahl7/v2/message/oru.rb +1 -0
  24. data/lib/yahl7/v2/message.rb +10 -0
  25. data/lib/yahl7/v2/segment/al1.rb +21 -0
  26. data/lib/yahl7/v2/segment/dg1.rb +36 -0
  27. data/lib/yahl7/v2/segment/evn.rb +23 -0
  28. data/lib/yahl7/v2/segment/in1.rb +68 -0
  29. data/lib/yahl7/v2/segment/msh.rb +4 -2
  30. data/lib/yahl7/v2/segment/nte.rb +3 -2
  31. data/lib/yahl7/v2/segment/obr.rb +13 -12
  32. data/lib/yahl7/v2/segment/obx.rb +11 -5
  33. data/lib/yahl7/v2/segment/orc.rb +8 -7
  34. data/lib/yahl7/v2/segment/pd1.rb +36 -0
  35. data/lib/yahl7/v2/segment/pid.rb +11 -10
  36. data/lib/yahl7/v2/segment/pv1.rb +7 -6
  37. data/lib/yahl7/v2.rb +6 -1
  38. data/lib/yahl7/version.rb +1 -1
  39. metadata +23 -3
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YAHL7
4
+ module V2
5
+ # This module can be included into a class of a data type that includes the
6
+ # name components in order to add an `#assemble_name_family_first` method
7
+ # and an `#assemble_name_given_first` method.
8
+ module AliasPersonName
9
+ # This is the method the builds the "bare" name (i.e., without prefixes
10
+ # and/or suffixes) with the family name first.
11
+ def bare_name_family_first
12
+ buf = family_name.nil? || family_name == '' ? '' : "#{family_name}, "
13
+ buf += "#{given_name} " unless given_name.nil? || given_name == ''
14
+ buf += middle_name unless middle_name.nil? || middle_name == ''
15
+
16
+ buf.strip
17
+ end
18
+
19
+ # This is the method the builds the "bare" name (i.e., without prefixes
20
+ # and/or suffixes) with the given name first.
21
+ def bare_name_given_first
22
+ [given_name, middle_name, family_name]
23
+ .reject { |p| p.nil? || p == '' }
24
+ .join(' ')
25
+ end
26
+
27
+ # This is the method that builds the full name (i.e., with prefixes and/or
28
+ # suffixes) with the family name first.
29
+ def assemble_name_family_first
30
+ buf = prefix.nil? || prefix == '' ? '' : "#{prefix} "
31
+ buf += bare_name_family_first
32
+
33
+ s = suffixes
34
+ buf += ", #{s}" unless s == ''
35
+
36
+ buf
37
+ end
38
+
39
+ # We have two locations for suffix information on a name:
40
+ #
41
+ # 1. The suffix, which is meant to house information like Jr, II, etc.
42
+ # 2. The degree, which is meant to house information like MD, PhD, etc.
43
+ #
44
+ # This method is used to combine those into a single value to make name
45
+ # assembly easier.
46
+ def suffixes
47
+ [suffix, degree]
48
+ .reject { |p| p.nil? || p == '' }
49
+ .join(', ')
50
+ end
51
+
52
+ # This is the method that builds the full name (i.e., with prefixes and/or
53
+ # suffixes) with the given name first.
54
+ def assemble_name_given_first
55
+ buf = prefix.nil? || prefix == '' ? '' : "#{prefix} "
56
+ buf += bare_name_given_first
57
+
58
+ s = suffixes
59
+ buf += ", #{s}" unless s == ''
60
+
61
+ buf
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for a composite name and ID number.
9
+ class CNN < YAHL7::V2::DataType
10
+ include YAHL7::V2::AliasPersonName
11
+ include YAHL7::V2::AliasFieldNames
12
+
13
+ define_field_names({
14
+ id_number: 0,
15
+ family_name: 1,
16
+ given_name: 2,
17
+ middle_name: 3,
18
+ suffix: 4,
19
+ prefix: 5,
20
+ degree: 6,
21
+ source_table: 7,
22
+ assigning_authority_namespace_id: 8,
23
+ assigning_authority_universal_id: 9,
24
+ assigning_authority_universal_id_type: 10
25
+ })
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for a single date. This should be in the
9
+ # format of YYYY[MM[DD]]
10
+ class DT < YAHL7::V2::DataType
11
+ def date
12
+ @date ||= parse_date
13
+ end
14
+
15
+ private
16
+
17
+ def parse_date
18
+ case @value.length
19
+ when 4 then Date.parse("#{@value}0101")
20
+ when 6 then Date.parse("#{@value}01")
21
+ when 8 then Date.parse(@value)
22
+ else raise YAHL7::V2::Error::InvalidFormatError, 'Unknown date format'
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for formatted text
9
+ class FT < YAHL7::V2::DataType
10
+ def formatted
11
+ @formatted ||= parse_value
12
+ end
13
+
14
+ private
15
+
16
+ def parse_value
17
+ value.is_a?(Array) ? value.map { |v| parse_body(v) }.join("\n") : parse_body(value)
18
+ end
19
+
20
+ def parse_body(body)
21
+ YAHL7::V2::Formatter.format(body)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ require_relative 'ts'
6
+
7
+ module YAHL7
8
+ module V2
9
+ class DataType
10
+ # This is the HL7 data type for a name with a date and location.
11
+ class NDL < YAHL7::V2::DataType
12
+ include YAHL7::V2::AliasFieldNames
13
+
14
+ define_field_names({
15
+ name: { index: 0, class: YAHL7::V2::DataType::CNN },
16
+ start_datetime: { index: 1, class: YAHL7::V2::DataType::TS },
17
+ end_datetime: { index: 2, class: YAHL7::V2::DataType::TS },
18
+ point_of_care: 3,
19
+ room: 4,
20
+ bed: 5,
21
+ facility: 6,
22
+ location_status: 7,
23
+ patient_location_type: 8,
24
+ building: 9,
25
+ floor: 10
26
+ })
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for a timestamp with a format of
9
+ # YYYY[MM[DD[HH[MM[SS[.S[S[S[S]]]]]]]]][+/-ZZZZ]
10
+ class TS < YAHL7::V2::DataType
11
+ def timestamp
12
+ @timestamp ||= parse_timestamp
13
+ end
14
+
15
+ private
16
+
17
+ def parse_timestamp
18
+ timestamp = @value.is_a?(Array) ? @value.first : @value
19
+ YAHL7::V2::DateTime.parse(timestamp)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for an extended address.
9
+ class XAD < YAHL7::V2::DataType
10
+ include YAHL7::V2::AliasFieldNames
11
+
12
+ define_field_names({
13
+ street_address: 0,
14
+ other_designation: 1,
15
+ city: 2,
16
+ state: 3,
17
+ zip: 4,
18
+ country: 5,
19
+ address_type: 6,
20
+ other_geographic_designation: 7,
21
+ county_code: 8,
22
+ census_tract: 9,
23
+ address_representation_code: 10,
24
+ address_validity_range: 11,
25
+ effective_date: { index: 12, class: YAHL7::V2::DataType::TS },
26
+ expiration_date: { index: 13, class: YAHL7::V2::DataType::TS }
27
+ })
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for an extended person name. This is the data
9
+ # type used for patient names, etc.
10
+ class XPN < YAHL7::V2::DataType
11
+ include YAHL7::V2::AliasPersonName
12
+ include YAHL7::V2::AliasFieldNames
13
+
14
+ define_field_names({
15
+ family_name: 0,
16
+ given_name: 1,
17
+ middle_name: 2,
18
+ suffix: 3,
19
+ prefix: 4,
20
+ degree: 5,
21
+ name_type_code: 6,
22
+ name_representation_code: 7,
23
+ name_context: 8,
24
+ name_validity_range: 9,
25
+ name_assembly_order: 10,
26
+ effective_date: 11,
27
+ expiration_date: 12,
28
+ professional_suffix: 13
29
+ })
30
+
31
+ def full_name
32
+ case name_assembly_order&.downcase
33
+ when 'f' then assemble_name_family_first
34
+ else assemble_name_given_first
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+
5
+ module YAHL7
6
+ module V2
7
+ class DataType
8
+ # This is the HL7 data type for an extended telephone number.
9
+ class XTN < YAHL7::V2::DataType
10
+ include YAHL7::V2::AliasFieldNames
11
+
12
+ define_field_names({
13
+ telephone_number: 0,
14
+ telephone_use_code: 1,
15
+ telephone_equipment_type: 2,
16
+ email_address: 3,
17
+ country_code: 4,
18
+ area_city_code: 5,
19
+ local_number: 6,
20
+ extension: 7,
21
+ any_text: 8,
22
+ extension_prefix: 9,
23
+ speed_dial_code: 10,
24
+ unformatted_telephone_number: 11
25
+ })
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.join(__dir__, 'data_type', '*.rb')].sort.each { |f| require f }
4
+
5
+ module YAHL7
6
+ module V2
7
+ # This is the base data type class for custom HL7 data types.
8
+ class DataType
9
+ attr_accessor :value
10
+
11
+ def initialize(value)
12
+ @value = value
13
+ end
14
+
15
+ def [](index)
16
+ value[index]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -21,7 +21,7 @@ module YAHL7
21
21
  timezone = parts[1]
22
22
 
23
23
  format = timestamp_format(timestamp)
24
- raise 'Unknown timestamp format' if format.nil?
24
+ raise YAHL7::V2::Error::InvalidFormatError, 'Unknown timestamp format' if format.nil?
25
25
 
26
26
  format += '%z' unless timezone.nil?
27
27
  ::DateTime.strptime(value, format)
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YAHL7
4
+ module V2
5
+ module Error
6
+ # This error is raised when the input for a given data type is implausible
7
+ # or otherwise invalid.
8
+ class InvalidFormatError < StandardError
9
+ end
10
+ end
11
+ end
12
+ end
@@ -3,6 +3,8 @@
3
3
  module YAHL7
4
4
  module V2
5
5
  class Message
6
+ # ADT messages contain admit, discharge, and transfer information about
7
+ # patients.
6
8
  class ADT < YAHL7::V2::Message
7
9
  end
8
10
  end
@@ -3,6 +3,7 @@
3
3
  module YAHL7
4
4
  module V2
5
5
  class Message
6
+ # ORU messages contain unsolicited observation results about a patient.
6
7
  class ORU < YAHL7::V2::Message
7
8
  end
8
9
  end
@@ -17,6 +17,10 @@ module YAHL7
17
17
  .map { |s| Segment.parse(s, parse_options) }
18
18
  end
19
19
 
20
+ def to_s
21
+ @to_s ||= segments.join(parse_options.segment_sep)
22
+ end
23
+
20
24
  def [](index)
21
25
  case index
22
26
  when Integer then segments[index]
@@ -41,9 +45,15 @@ module YAHL7
41
45
  # information, as this method simply finds the type code and then hands it
42
46
  # off to `get_message_class` to find the class to use.
43
47
  def self.message_type(body, parse_options = nil)
48
+ return self if body.length < 8
49
+
44
50
  parse_options ||= ParseOptions.from_body(body)
45
51
  head = body.split(parse_options.segment_sep)[0]
52
+ return self if head.nil?
53
+
46
54
  type = head.split(parse_options.repetition_sep)[8]
55
+ return self if type.nil?
56
+
47
57
  code = type.split(parse_options.component_sep)[0]
48
58
  get_message_class(code)
49
59
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YAHL7
4
+ module V2
5
+ class Segment
6
+ # AL1 segments contain patient allergy information.
7
+ class AL1 < YAHL7::V2::Segment
8
+ include YAHL7::V2::AliasFieldNames
9
+
10
+ define_field_names({
11
+ set_id: 1,
12
+ allergen_type_code: 2,
13
+ allergen_code: 3,
14
+ allergen_severity_code: 4,
15
+ allergen_reaction_code: 5,
16
+ identification_date: { index: 6, class: YAHL7::V2::DataType::DT }
17
+ })
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YAHL7
4
+ module V2
5
+ class Segment
6
+ # DG1 segments contain patient diagnosis information.
7
+ class DG1 < YAHL7::V2::Segment
8
+ include YAHL7::V2::AliasFieldNames
9
+
10
+ define_field_names({
11
+ set_id: 1,
12
+ diagnosis_code_method: 2,
13
+ diagnosis_code: 3,
14
+ diagnosis_description: 4,
15
+ diagnosis_datetime: { index: 5, class: YAHL7::V2::DataType::TS },
16
+ diagnosis_type: 6,
17
+ major_diagnosis_category: 7,
18
+ diagnostic_related_group: 8,
19
+ drg_approval_indicator: 9,
20
+ drg_grouper_review_code: 10,
21
+ outlier_type: 11,
22
+ outlier_days: 12,
23
+ outlier_cost: 13,
24
+ grouper_version_and_type: 14,
25
+ diagnostic_priority: 15,
26
+ diagnosing_clinician: 16,
27
+ diagnosis_classification: 17,
28
+ confidential_indicator: 18,
29
+ attestation_datetime: { index: 19, class: YAHL7::V2::DataType::TS },
30
+ diagnosis_identifier: 20,
31
+ diagnosis_action_code: 21
32
+ })
33
+ end
34
+ end
35
+ end
36
+ end