Skip to content

Commit 1dfde0e

Browse files
committed
Initial commit
1 parent d223af7 commit 1dfde0e

File tree

1,216 files changed

+28104
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,216 files changed

+28104
-1
lines changed

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
same "printed page" as the copyright notice for easier
187187
identification within third-party archives.
188188

189-
Copyright [yyyy] [name of copyright owner]
189+
Copyright 2018 HCA Healthcare
190190

191191
Licensed under the Apache License, Version 2.0 (the "License");
192192
you may not use this file except in compliance with the License.

README.md

+65
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,67 @@
11
# elixir-hl7
22
An Elixir library for working with HL7 v2.x healthcare data
3+
4+
Elixir-HL7 provides parsers and data-structures for working with healthcare data that conforms to the HL7 v2.x standards. Elixir-HL7 includes data structures for every HL7 Segment and Datatype defined in HL7 versions 2.1, 2.2, 2.3, 2.3.1, 2.4, 2.5, and 2.5.1. Elixir-HL7 also provides basic support for handling MLLP streams.
5+
6+
You can learn more about HL7 here:
7+
* The official HL& website ([hl7.org](http://www.hl7.org/index.cfm))
8+
* Wikipedia's [HL7 article](https://en.wikipedia.org/wiki/Health_Level_7)
9+
10+
## Status
11+
12+
This project is at v0.0.1 for a reason. The API and internals will likely quite a bit between now and v1.0. Also, be aware of the details of the license (Apache 2.0).
13+
14+
## Design goals
15+
16+
### Completeness
17+
18+
The library should contain every segment and datatype defined in the HL7 standard
19+
for each supported HL7 version. We used the XSD files found at [hl7.org](http://www.hl7.org/implement/standards/product_brief.cfm?product_id=214) to build our initial set.
20+
21+
### Speed
22+
23+
For basic reads we are aiming at tens of thousands of messages per second on a typical developer laptop.
24+
We will include more meaningful benchmarks soon.
25+
26+
### Non-strict evaluation
27+
28+
Non-standard, company-specific data is commonly stored in segments beginning with the letter "Z" such as ZPM (commonly referred to as "Z segments"). Elixir-HL7 stores Z-Segment data as lists of lists in ZSegment structs.
29+
30+
Data for other unknown segments is stored similarly to Z-Segments but in UnknownSegment structs.
31+
32+
Data that overflows the list of fields defined in the specs for segments and datatypes are preserved.
33+
34+
### Flexibility
35+
36+
To access data by ordinal positions or nested named structures
37+
38+
### Lossless data round-trips
39+
40+
[raw hl7 string] <-> [lists of lists] <-> [data structures]
41+
42+
## Getting started
43+
44+
The Hl7.Example module provides sample data you can use to explore the API.
45+
46+
```
47+
iex> raw_hl7 = Hl7.Examples.wikipedia_sample_hl7
48+
49+
"MSH|^~&|MegaReg|XYZHospC|SuperOE|XYZImgCtr|20060529090131-0500||ADT^A01^ADT_A01|01052901|P|2.5\rEVN||200605290901||||200605290900\rPID|||56782445^^^UAReg^PI||KLEINSAMPLE^BARRY^Q^JR||19620910|M||2028-9^^HL70005^RA99113^^XYZ|260 GOODWIN CREST DRIVE^^BIRMINGHAM^AL^35209^^M~NICKELL’S PICKLES^10000 W 100TH AVE^BIRMINGHAM^AL^35200^^O|||||||0105I30001^^^99DEF^AN\rPV1||I|W^389^1^UABH^^^^3||||12345^MORGAN^REX^J^^^MD^0010^UAMC^L||67890^GRAINGER^LUCY^X^^^MD^0010^UAMC^L|MED|||||A0||13579^POTTER^SHERMAN^T^^^MD^0010^UAMC^L|||||||||||||||||||||||||||200605290900\rOBX|1|NM|^Body Height||1.80|m^Meter^ISO+|||||F\rOBX|2|NM|^Body Weight||79|kg^Kilogram^ISO+|||||F\rAL1|1||^ASPIRIN\rDG1|1||786.50^CHEST PAIN, UNSPECIFIED^I9|||A\r"
50+
```
51+
52+
We can take that sample message and create an `Hl7.Message` like so
53+
54+
```
55+
iex> raw_hl7 |> Hl7.Message.new() |> Hl7.Message.make_structs()
56+
```
57+
58+
# Roadmap
59+
- [ ] Module docs and function docs
60+
- [ ] Support adding custom Z-Segments
61+
- [ ] Once it's a bit less wobbly, publish to hex.pm
62+
- [ ] Property-based tests and StreamData generators for HL7 segments and datatypes
63+
- [ ] Support for higher HL7 versions (e.g. 2.8)
64+
65+
# License
66+
67+
Elixir-HL7 source code is released under Apache 2 License. Check LICENSE file for more information.

VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.0.1

bump_version.exs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#! /usr/bin/env elixir
2+
3+
new_version =
4+
"VERSION"
5+
|> File.read!()
6+
|> String.trim()
7+
|> Version.parse!()
8+
|> (fn ver -> "#{ver.major}.#{ver.minor}.#{ver.patch + 1}" end).()
9+
10+
File.write("VERSION", new_version)
11+
IO.puts("Version bumped to #{new_version}")

config/config.exs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file is responsible for configuring your application
2+
# and its dependencies with the aid of the Mix.Config module.
3+
use Mix.Config
4+
5+
# This configuration is loaded before any dependency and is restricted
6+
# to this project. If another project depends on this project, this
7+
# file won't be loaded nor affect the parent project. For this reason,
8+
# if you want to provide default values for your application for
9+
# 3rd-party users, it should be done in your "mix.exs" file.
10+
11+
# You can configure your application as:
12+
#
13+
# config :hl7, key: :value
14+
#
15+
# and access this configuration in your application as:
16+
#
17+
# Application.get_env(:hl7, :key)
18+
#
19+
# You can also configure a 3rd-party app:
20+
#
21+
# config :logger, level: :info
22+
#
23+
24+
# It is also possible to import configuration files, relative to this
25+
# directory. For example, you can emulate configuration per environment
26+
# by uncommenting the line below and defining dev.exs, test.exs and such.
27+
# Configuration from the imported file will override the ones defined
28+
# here (which is why it is important to import them last).
29+
#
30+
# import_config "#{Mix.env}.exs"

lib/hl7.ex

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
defmodule Hl7 do
2+
@moduledoc """
3+
Functions for reading raw HL7 messages, splitting them into segments, and
4+
parsing them into HL7.Segments structures
5+
"""
6+
require Logger
7+
8+
def open_hl7_file_stream(file_path) do
9+
file_ref = File.open!(file_path, [:read])
10+
first_three = IO.binread(file_ref, 3)
11+
File.close(file_ref)
12+
13+
case first_three do
14+
<<"MSH">> ->
15+
File.stream!(file_path)
16+
17+
<<0x0B, "M", "S">> ->
18+
file_path
19+
|> File.stream!([], 32768)
20+
|> Hl7.MllpStream.raw_to_messages()
21+
end
22+
end
23+
24+
def get_separators(<<"MSH", _::binary()>> = raw_message) do
25+
Hl7.Separators.new(raw_message)
26+
end
27+
end

lib/hl7/2.1/datatypes/ce.ex

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule Hl7.V2_1.DataTypes.Ce do
2+
@moduledoc """
3+
The "CE" (CE) data type
4+
"""
5+
6+
use Hl7.DataType,
7+
fields: [
8+
identifier: nil,
9+
text: nil,
10+
name_of_coding_system: nil,
11+
alternate_identifier: nil,
12+
alternate_text: nil,
13+
name_of_alternate_coding_system: nil
14+
]
15+
end

lib/hl7/2.1/segments.ex

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
defmodule Hl7.V2_1.Segments.ZSegment do
2+
@moduledoc """
3+
HL7 segment data structure for handling non-standard Z-Segments
4+
"""
5+
use Hl7.Segment, fields: [segment: nil, values: nil], undefined_struct: true
6+
end
7+
8+
defmodule Hl7.V2_1.Segments.UnknownSegment do
9+
@moduledoc """
10+
HL7 segment data structure for handling unknown segment types
11+
"""
12+
use Hl7.Segment, fields: [segment: nil, values: nil], undefined_struct: true
13+
end
14+
15+
defmodule Hl7.V2_1.Segments do
16+
alias Hl7.V2_1.Segments
17+
18+
def parse(nested_lists), do: parse(nested_lists |> unlist, nested_lists)
19+
def parse("ACC", nested_lists), do: Segments.ACC.new(nested_lists)
20+
def parse("ADD", nested_lists), do: Segments.ADD.new(nested_lists)
21+
def parse("BHS", nested_lists), do: Segments.BHS.new(nested_lists)
22+
def parse("BLG", nested_lists), do: Segments.BLG.new(nested_lists)
23+
def parse("BTS", nested_lists), do: Segments.BTS.new(nested_lists)
24+
def parse("DG1", nested_lists), do: Segments.DG1.new(nested_lists)
25+
def parse("DSC", nested_lists), do: Segments.DSC.new(nested_lists)
26+
def parse("DSP", nested_lists), do: Segments.DSP.new(nested_lists)
27+
def parse("ERR", nested_lists), do: Segments.ERR.new(nested_lists)
28+
def parse("EVN", nested_lists), do: Segments.EVN.new(nested_lists)
29+
def parse("FHS", nested_lists), do: Segments.FHS.new(nested_lists)
30+
def parse("FT1", nested_lists), do: Segments.FT1.new(nested_lists)
31+
def parse("FTS", nested_lists), do: Segments.FTS.new(nested_lists)
32+
def parse("GT1", nested_lists), do: Segments.GT1.new(nested_lists)
33+
def parse("IN1", nested_lists), do: Segments.IN1.new(nested_lists)
34+
def parse("MRG", nested_lists), do: Segments.MRG.new(nested_lists)
35+
def parse("MSA", nested_lists), do: Segments.MSA.new(nested_lists)
36+
def parse("MSH", nested_lists), do: Segments.MSH.new(nested_lists)
37+
def parse("NCK", nested_lists), do: Segments.NCK.new(nested_lists)
38+
def parse("NK1", nested_lists), do: Segments.NK1.new(nested_lists)
39+
def parse("NPU", nested_lists), do: Segments.NPU.new(nested_lists)
40+
def parse("NSC", nested_lists), do: Segments.NSC.new(nested_lists)
41+
def parse("NST", nested_lists), do: Segments.NST.new(nested_lists)
42+
def parse("NTE", nested_lists), do: Segments.NTE.new(nested_lists)
43+
def parse("OBR", nested_lists), do: Segments.OBR.new(nested_lists)
44+
def parse("OBX", nested_lists), do: Segments.OBX.new(nested_lists)
45+
def parse("ORC", nested_lists), do: Segments.ORC.new(nested_lists)
46+
def parse("ORO", nested_lists), do: Segments.ORO.new(nested_lists)
47+
def parse("PID", nested_lists), do: Segments.PID.new(nested_lists)
48+
def parse("PR1", nested_lists), do: Segments.PR1.new(nested_lists)
49+
def parse("PV1", nested_lists), do: Segments.PV1.new(nested_lists)
50+
def parse("QRD", nested_lists), do: Segments.QRD.new(nested_lists)
51+
def parse("QRF", nested_lists), do: Segments.QRF.new(nested_lists)
52+
def parse("RX1", nested_lists), do: Segments.RX1.new(nested_lists)
53+
def parse("UB1", nested_lists), do: Segments.UB1.new(nested_lists)
54+
def parse("URD", nested_lists), do: Segments.URD.new(nested_lists)
55+
def parse("URS", nested_lists), do: Segments.URS.new(nested_lists)
56+
def parse(<<"Z", _::binary>>, nested_lists), do: Segments.ZSegment.new(nested_lists)
57+
def parse(_, nested_lists), do: Segments.UnknownSegment.new(nested_lists)
58+
59+
def unlist([h | _]) do
60+
unlist(h)
61+
end
62+
63+
def unlist(v) do
64+
v
65+
end
66+
end

lib/hl7/2.1/segments/acc.ex

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule Hl7.V2_1.Segments.ACC do
2+
@moduledoc """
3+
HL7 segment data structure for "ACC"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
accident_date_time: nil,
12+
accident_code: nil,
13+
accident_location: nil
14+
]
15+
end

lib/hl7/2.1/segments/add.ex

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
defmodule Hl7.V2_1.Segments.ADD do
2+
@moduledoc """
3+
HL7 segment data structure for "ADD"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
addendum_continuation_pointer: nil
12+
]
13+
end

lib/hl7/2.1/segments/bhs.ex

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
defmodule Hl7.V2_1.Segments.BHS do
2+
@moduledoc """
3+
HL7 segment data structure for "BHS"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
batch_field_separator: nil,
12+
batch_encoding_characters: nil,
13+
batch_sending_application: nil,
14+
batch_sending_facility: nil,
15+
batch_receiving_application: nil,
16+
batch_receiving_facility: nil,
17+
batch_creation_date_time: nil,
18+
batch_security: nil,
19+
batch_name_id_type: nil,
20+
batch_comment: nil,
21+
batch_control_id: nil,
22+
reference_batch_control_id: nil
23+
]
24+
end

lib/hl7/2.1/segments/blg.ex

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule Hl7.V2_1.Segments.BLG do
2+
@moduledoc """
3+
HL7 segment data structure for "BLG"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
when_to_charge: nil,
12+
charge_type: nil,
13+
account_id: nil
14+
]
15+
end

lib/hl7/2.1/segments/bts.ex

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
defmodule Hl7.V2_1.Segments.BTS do
2+
@moduledoc """
3+
HL7 segment data structure for "BTS"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
batch_message_count: nil,
12+
batch_comment: nil,
13+
batch_totals: nil
14+
]
15+
end

lib/hl7/2.1/segments/dg1.ex

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
defmodule Hl7.V2_1.Segments.DG1 do
2+
@moduledoc """
3+
HL7 segment data structure for "DG1"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
set_id_diagnosis: nil,
12+
diagnosis_coding_method: nil,
13+
diagnosis_code: nil,
14+
diagnosis_description: nil,
15+
diagnosis_date_time: nil,
16+
diagnosis_drg_type: nil,
17+
major_diagnostic_category: nil,
18+
diagnostic_related_group: nil,
19+
drg_approval_indicator: nil,
20+
drg_grouper_review_code: nil,
21+
outlier_type: nil,
22+
outlier_days: nil,
23+
outlier_cost: nil,
24+
grouper_version_and_type: nil
25+
]
26+
end

lib/hl7/2.1/segments/dsc.ex

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
defmodule Hl7.V2_1.Segments.DSC do
2+
@moduledoc """
3+
HL7 segment data structure for "DSC"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
continuation_pointer: nil
12+
]
13+
end

lib/hl7/2.1/segments/dsp.ex

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule Hl7.V2_1.Segments.DSP do
2+
@moduledoc """
3+
HL7 segment data structure for "DSP"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
set_id_display_data: nil,
12+
display_level: nil,
13+
data_line: nil,
14+
logical_break_point: nil,
15+
result_id: nil
16+
]
17+
end

lib/hl7/2.1/segments/err.ex

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
defmodule Hl7.V2_1.Segments.ERR do
2+
@moduledoc """
3+
HL7 segment data structure for "ERR"
4+
"""
5+
6+
require Logger
7+
8+
use Hl7.Segment,
9+
fields: [
10+
segment: nil,
11+
error_code_and_location: nil
12+
]
13+
end

0 commit comments

Comments
 (0)