hl7validator
provides a declarative way of validating HL7 messages. This package provides a simple DSL to describe
validation rules. A HL7 message will be checked against those rules.
This is a work in progress.
pip install hl7validator
You can also use source code to install the package into a venv:
$ git clone https://github.com/cezio/hl7-validator.git
$ cd hl7-validator
$ python -m venv venv
$ source venv/bin/activate
$ pip install .
You can validate a HL7 message in few lines, assuming you have proper rules ready:
from hl7validator.validator import Validator
rules = """
// we expect OML^O21 in MSH.9, but we need to look into subfields with python-hl7
MSH.9.1.1 must be "OML"
MSH.9.1.2 must be "O21"
HSH.12.1 must be one of "2.3", "2.4", "2.5"
"""
msg = test_msg = r'MSH|^~\\&|SrcSystem||TargetSystem|LabName|200705271331||OML^O21|12345|P|2.4\r'
validator = Validator(rules)
result = validator.validate(msg)
assert result.is_valid
Validation is also available as a CLI script: validate_hl7
. Incorrect message will result in non-zero return code from
the script:
$ validate_hl7 tests/resources/test.incorrect.rules tests/resources/test.message.hl7
Message is invalid:
* validation error for <FieldSelector sel=MSH.3.1> value FromSystem | <FieldValidationRule: <FieldSelector sel=MSH.3.1> <MustBe(expected=<ConstValue: SrcSystem>)> >
* validation error for <FieldSelector sel=MSH.3.1> value FromSystem | <FieldValidationRule: <FieldSelector sel=MSH.3.1> <MustBe(expected=<OneOfValues: ['SrcSystem', 'OtherSystem']>)> >
$ echo $?
1
Correct message will return zero as return code, and will print the Message is valid.
message, which can be suppressed
with -q
switch.
$ validate_hl7 tests/resources/test.correct.rules tests/resources/test.message.hl7
Message is valid.
$ echo $?
0
A HL7 message can be validated with a set of rules written in human-friendly form with a dedicated DSL. Each rule is one line.
You can import rules from other rule files. Imported rules will be merged with your current validator. Mind that the file is parsed from top to bottom, and importing rules will add them at specific state of current validator. It's advised to import rules at the beginning of rules file. You can refer other rules by using URL-like notation in two ways:
- by using
file://
protocol to point to a specific file from file system, for examplefile://validation/rules/olm_o21_base.rules
will import rules fromvalidation/rules/olm_o21_base.rules
relative path. - by using
pkg://
protocol to point to a rule available as a resource from another python package. For example,pkg://lab.medsystem.com/validation/rules/base.rules
will import rules fromlab.medsystem.com
package, fromvalidation/rules/base.rules
resource location within that package.
Structure check rules allow to check if the structure of the message matches specific scheme. Structure check is defined as a list of names of segments. A segment can be at a specific level of structure. Ones with no indentation are root-level, anc can be placed within the message anywhere. When a segment is indented, then it's considered as dependent on previous one on the higher level. Additionally, each segment can have cardinality specified, which describes a number of occurrences of a segment within the structure.
Cardinalities allowed:
0
- no occurrence1
- only one occurrence (the default)0..1
- at most one1..0
- at least one0..n
- any number
MSH
PID
PV1 1..n
DG1 1..n
NTE 0..n
A field value rule is a check on a specific field if the value of this field matches certain conditions. Field is
specified with accessor notation: AAA.1.1
(or AAA.1.1.1
if you need to access a subfield).
See accessor documentation in python-hl7 for details.
Once field selection is specified, value conditions can be specified. You can specify if a value should:
- be not empty
- be of a specific type (int, string)
- be of a specific value (constant value) or within a specific set of values (a list of allowed values)
- match a specific regexp
- match specific criteria, if it's not empty (
may be
) - be dependent on another field (current field will be checked if other field's check will be positive; syntax for dependent checks is similar to base checks
You can also put comments prepended with //
chars.
// importing rules from other files - importing will merge strucutre/field rules with current validator
import "file://path/to/rules.file"
// you can import from a file (with `file://` protocol) or from a package (with `pkg://` protocol)
import "pkg://package.name/path/to/a/resource/with/rules.file"
// below structure validation allows to specify groups of segments and cardinality of each seagment within the group:
// the default is `1`
// but you can also specify `0..1` (at most one), `1..n` (at least one), `0..n` (any number), or `0` (no segment).
MSH
PID
PV1 1..n
NTE 0..n
// this is a comment
// this value must not be empty
"MSH.3.1" must be not empty
// this value must be a proper string
"MSH.3.1" must be string
// this value should be a string if speficied (implies it should not be empty)
"MSH.3.2" may be string
// cannot be that value
"MSH.3.1" cannot be "AnySystem"
// must be a specific string value
"MSH.3.1" must be "FromSystem"
// list of allowed values
"MSH.3.1" must be one of "FromSystem", "OtherSystem"
// this is a regexp
"MSH.7.1" must match r"[0-9]{12}"
// dependency on another field
"NTE.2.1" must be "P" if "PID.2.1" is not empty
// various checks:
"NTE.2.1" must be "P" if "PID.2.1" is of value "ABC"
"NTE.2.1" must be "P" if "PID.2.1" matches r"ABC"
"NTE.2.1" must be "P" if "PID.2.1" is one of "ABC", "BCD", "CDE"
"NTE.2.2" must be "P" if "PID.2.1" is of type string
See the grammar file for details.