Skip to content

HL7 v2 parser, envelope mapping, LIMS push wiring (PR-7)#37

Open
ramonski wants to merge 1 commit into
2.xfrom
pr-7-hl7-parser
Open

HL7 v2 parser, envelope mapping, LIMS push wiring (PR-7)#37
ramonski wants to merge 1 commit into
2.xfrom
pr-7-hl7-parser

Conversation

@ramonski
Copy link
Copy Markdown
Contributor

Turn raw HL7 bytes into the existing Envelope so the HL7 server
serves LIMS pushes the same way the ASTM server does. Closes the
parser gap from PR-6; instrument-specific routing (sample ID,
keyword mapping, OBR-4 filtering) lands in the next PR.

Summary

  • transports/hl7/parser.pyparse(raw) -> Envelope. Wraps
    the hl7 package for defensive
    escape-sequence and encoding handling. Segment mapping:

    HL7 segment Envelope bucket
    MSH H
    PID P
    OBR O
    OBX R
    NTE C

    Raw text lands in metadata.hl7.

  • core/envelope.py

    • Metadata gains an hl7 field.
    • astm and lis2a are soft-defaulted to "" so HL7 envelopes
      validate without faking ASTM representations.
    • ENVELOPE_VERSION bumped to 1.1 (additive, backwards
      compatible — consumers that pin to 1.0 keep working).
    • serialize_envelope learns the "hl7" format.
  • core/output.pyDiskCaptureHandler gains a payload=
    callable so callers pick which Metadata field gets persisted.
    ASTM retains the default (metadata.astm); HL7 server passes a
    lambda for metadata.hl7 and ext=".hl7".

  • cli/hl7_server.py — parses HL7 via the new parser, runs
    the shared Pipeline. New flags --url / --consumer / --message-format / --retries / --delay so the HL7 server can
    push to SENAITE the same way the ASTM server does. Without
    --url the server stays capture-only.

  • tests/data/envelopes/*.json — regenerated for
    envelope_version 1.1. This is the expected snapshot churn on a
    schema bump (called out in the refactor plan §6 / Risks).

Test plan

  • python -m pytest src/senaite/astm/tests/340 passed, 1 skipped.
  • flake8 --config ci_flake8.cfg src/senaite/astm — clean.
  • New test_hl7_parser.py (25 tests):
    • Per-fixture envelope shape: fresh blood (OBS), liquid QC
      (LQC, ranges populated), proficiency (PRF, ranges
      suppressed), flagged-with-NTE.
    • Special OBX values (LL, ---) parse without crashing.
    • Serializer integration: "hl7" mode returns raw text;
      "json" mode includes metadata.hl7 with astm/lis2a as
      empty strings.
    • Robustness: accepts bytes and str; normalises \n to \r;
      raises ValueError on input without an MSH header.
  • All existing ASTM tests still pass against the regenerated
    snapshots.

Smoke-test

pip install -e .

# Terminal 1
senaite-hl7-server -p 2575 -o /tmp/hl7-captures -v

# Terminal 2
senaite-hl7-simulator -a 127.0.0.1 -p 2575 \
  -i src/senaite/astm/tests/data/hl7/hemoscreen_fresh_blood.hl7

A .hl7 file lands in /tmp/hl7-captures/. With --url http://user:pass@host/senaite on the server, the parsed envelope
also gets pushed to SENAITE.

Out of scope (PR-8)

  • Instrument resolution by MSH-3 == "HemoScreen".
  • OBR-4 routing — only OBS should reach the LIMS pipeline;
    LQC and PRF are debug-logged and dropped on the push side
    (still captured to disk).
  • Keyword mapping. The downstream LIMS importer already maps
    #_ABS and %_PERC, which matches HemoScreen's
    parameter names verbatim — no conversion table needed on the
    senaite.astm side.
  • Sample ID extraction from PID-2 for the SENAITE adapter.

Turn raw HL7 bytes into the existing Envelope so the HL7 server
serves LIMS pushes the same way the ASTM server does. Closes the
parser gap; instrument-specific routing (sample ID, keyword
mapping, OBR-4 filtering) lands in the next PR.

- transports/hl7/parser.py: parse(raw) -> Envelope. Wraps the hl7
  PyPI package for defensive escape-sequence handling. Segment
  mapping MSH->H, PID->P, OBR->O, OBX->R, NTE->C. Raw text lands
  in metadata.hl7.
- core/envelope.py: Metadata gains hl7 field; astm and lis2a soft-
  default to "" so HL7 envelopes validate without faking ASTM
  representations. ENVELOPE_VERSION bumped to 1.1 (additive,
  backwards compatible). serialize_envelope learns the "hl7"
  format.
- core/output.py: DiskCaptureHandler gains a payload= callable so
  callers can pick which Metadata field gets persisted. ASTM
  retains the default (metadata.astm); HL7 server passes a lambda
  for metadata.hl7 and ext=".hl7".
- cli/hl7_server.py: parses HL7 via the new parser, runs the
  shared Pipeline. New flags --url / --consumer / --message-format
  / --retries / --delay so the HL7 server can push to SENAITE the
  same way the ASTM server does. Without --url the server stays
  capture-only.
- tests/data/envelopes/*.json: regenerated for envelope_version 1.1.
- tests/test_envelope_schema.py: assertion that astm/lis2a are
  required is replaced with one verifying they soft-default; HL7
  envelopes need that.
- tests/test_hl7_parser.py (new, 25 tests): per-fixture envelope
  shape checks for fresh blood (OBS), liquid QC (LQC, ranges
  populated), proficiency (PRF, ranges suppressed), and the
  flagged variant with NTE notes. Plus serializer integration
  and robustness (bytes/str, LF normalization, malformed input).

Suite: 340 passed, 1 skipped. flake8 clean.

Next: PR-8 — HemoScreen instrument adapter. MSH-3 == "HemoScreen"
resolution; sample ID from PID-2; OBR-4 == "OBS" routing (LQC/PRF
captured-but-not-pushed); keyword mapping via the existing
# -> _ABS / % -> _PERC convention.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant