v0.3.4: field-name aliases, dot/dash syntax, ops (=, !=, ~, !~), new formats
Field path improvements (hl7-field.sh + every tool that uses it):
- Accept both `.` and `-` as separators:
PID.3 == PID-3
PV1.3.4 == PV1-3.4 == PV1-3-4 == PV1.3-4
- Field-name aliases (case-insensitive):
mrn → PID.3
account / account_number → PID.18
name / patient_name → PID.5
dob / birthdate → PID.7
ssn → PID.19
visit / encounter / csn → PV1.19
attending → PV1.7
event → MSH.9.2
control_id / msgid → MSH.10
...and ~40 more covering MSH/PID/PV1/EVN/NK1/GT1/IN1/OBR/OBX/DG1/ORC
- Aliases also accept component/subcomponent suffixes:
name.2 → PID.5.2
mrn.1 → PID.3.1
Filter operators (nc-msgs.sh --field):
PATH=VALUE exact equality
PATH!=VALUE not equal
PATH~VALUE contains (case-insensitive)
PATH!~VALUE does not contain (case-insensitive)
PATH=NULL /= null / empty / absent
PATH!=NULL present (any non-empty rep)
PATH=* wildcard — any non-empty value
Multiple --field flags AND; for OR, run two queries.
New output formats for nc-msgs.sh:
text (default) segments per line + metadata header per message
oneline one message per line, segments joined with a ⏎ marker
fields each non-empty field on its own line: "SEG.N: value"
mp alias for fields (matches v1 `mp` semantic)
labeled fields with friendly aliases: "MSH.9 (msg_type): ADT^A08"
raw, json, count — unchanged
MANUAL.md updated with the full operator + format reference.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b9415f3b57
commit
8ffdeb4f5d
42
MANUAL.md
42
MANUAL.md
@ -155,18 +155,50 @@ Smat databases are **SQLite 3**. Reads via native `sqlite3 -ascii` — no Clover
|
||||
# Count messages in a thread's smat
|
||||
lib/nc-msgs.sh ADTto_3m --format count
|
||||
|
||||
# Recent 5 messages (text format with metadata + parsed segments)
|
||||
# Recent 5 messages (text format = segments per line, with metadata header)
|
||||
lib/nc-msgs.sh ADTto_3m --limit 5 --format text
|
||||
|
||||
# OUTPUT FORMATS:
|
||||
# text (default) segments per line + metadata header per message
|
||||
# oneline one message per line; segments separated by ⏎ marker
|
||||
# fields each non-empty field on its own line: "SEG.N: value"
|
||||
# mp alias for fields (v1 `mp`-style)
|
||||
# labeled fields with alias names where known: "MSH.9 (msg_type): ADT^A08"
|
||||
# raw raw bytes; messages separated by 0x1c (FS) — for piping
|
||||
# json structured JSON
|
||||
# count just the count
|
||||
lib/nc-msgs.sh ADTto_3m --limit 3 --format oneline
|
||||
lib/nc-msgs.sh ADTto_3m --limit 1 --format fields
|
||||
lib/nc-msgs.sh ADTto_3m --limit 1 --format labeled # adds friendly aliases
|
||||
|
||||
# Time range — supports human expressions
|
||||
lib/nc-msgs.sh ADTto_3m --after "3 days ago" --format count
|
||||
lib/nc-msgs.sh ADTto_3m --after "2026-05-20" --before "2026-05-26 12:00:00"
|
||||
|
||||
# Filter by HL7 field — find messages where PID.3 equals an MRN
|
||||
lib/nc-msgs.sh ADTto_3m --field PID.3=5720501458 --limit 20 --format text
|
||||
# Filter operators (paths accept either . or - separators; same field name aliases everywhere):
|
||||
# PATH=VALUE exact equality (any repetition)
|
||||
# PATH!=VALUE not equal (no repetition matches)
|
||||
# PATH~VALUE contains, case-insensitive
|
||||
# PATH!~VALUE does not contain, case-insensitive
|
||||
# PATH=NULL empty / absent / "" — any of those
|
||||
# PATH= same as =NULL
|
||||
# PATH=* wildcard — any non-empty value
|
||||
# PATH!=NULL present (any non-empty repetition)
|
||||
# Multiple --field flags AND together. For OR, run two queries.
|
||||
|
||||
# Account-number search at PID.18
|
||||
lib/nc-msgs.sh ADTto_3m --field PID.18=623000286 --format text
|
||||
# Examples using field-name ALIASES (case-insensitive; auto-translates to SEG.N)
|
||||
lib/nc-msgs.sh ADTto_3m --field 'mrn=5720501458' --format count
|
||||
lib/nc-msgs.sh ADTto_3m --field 'account_number=623000286' --format text
|
||||
lib/nc-msgs.sh ADTto_3m --field 'event=A08' --format count
|
||||
lib/nc-msgs.sh ADTto_3m --field 'visit=*' --format count # any non-empty
|
||||
lib/nc-msgs.sh ADTto_3m --field 'ssn=NULL' --format count # missing SSN
|
||||
lib/nc-msgs.sh ADTto_3m --field 'name~smith' --format text # contains
|
||||
lib/nc-msgs.sh ADTto_3m --field 'name!~test' --format count # production-looking
|
||||
lib/nc-msgs.sh ADTto_3m --field 'event=A08' --field 'visit=*' # AND of both
|
||||
# Component access: name.2 (PID.5 component 2 = given name)
|
||||
lib/nc-msgs.sh ADTto_3m --field 'name.2=SALLY' --format count
|
||||
# Dash syntax (cheat-sheet style):
|
||||
lib/nc-msgs.sh ADTto_3m --field 'PV1-3.4=100200' --format count
|
||||
|
||||
# JSON output for piping
|
||||
lib/nc-msgs.sh ADTto_3m --field PID.3=5720501458 --format json | jq
|
||||
|
||||
2
larry.sh
2
larry.sh
@ -32,7 +32,7 @@ set -o pipefail
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Config
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
LARRY_VERSION="0.3.3"
|
||||
LARRY_VERSION="0.3.4"
|
||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||
LARRY_UPDATE_URL="${LARRY_UPDATE_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main/larry.sh}"
|
||||
LARRY_AGENTS_URL="${LARRY_AGENTS_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main/agents}"
|
||||
|
||||
@ -2,10 +2,13 @@
|
||||
# hl7-field.sh — extract a specific field from an HL7 v2 message. Native v3.
|
||||
#
|
||||
# Field path: SEG[.FIELD[.COMPONENT[.SUBCOMPONENT]]]
|
||||
# Both `.` and `-` are accepted as separators (cheat-sheet flexibility):
|
||||
# PID — return the whole PID segment
|
||||
# PID.3 — return PID field 3
|
||||
# PID-3 — same as PID.3
|
||||
# PID.3.1 — return PID field 3, component 1
|
||||
# PID.3.1.1 — return PID field 3, component 1, subcomponent 1
|
||||
# PID-3.1 — same as PID.3.1
|
||||
# PV1-3-4 — PV1 segment, field 3, component 4
|
||||
# MSH.10 — special: MSH numbering accounts for the encoding chars
|
||||
# (MSH.1 = field separator char, MSH.2 = encoding chars,
|
||||
# MSH.3+ = subsequent fields).
|
||||
@ -37,7 +40,93 @@ fi
|
||||
[ -n "$MSG" ] || { echo "hl7-field: empty message" >&2; exit 3; }
|
||||
|
||||
# Parse path: SEG, optional .FIELD, .COMPONENT, .SUBCOMPONENT
|
||||
IFS='.' read -r SEG FNUM CNUM SCNUM <<< "$PATH_SPEC"
|
||||
# Accept both `.` and `-` as separators (PID.3.1 == PID-3.1 == PID-3-1 == PID.3-1).
|
||||
# Normalize first separator-after-segment to `.` then split.
|
||||
NORMALIZED=$(printf '%s' "$PATH_SPEC" | sed 's/[.\-]/./g')
|
||||
|
||||
# Resolve common HL7 field-name aliases. The first token may be an alias like
|
||||
# MRN, NAME, ACCOUNT, VISIT, etc. — translate to its SEG.FIELD form, preserving
|
||||
# any component/subcomponent suffix the user passed.
|
||||
# MRN → PID.3
|
||||
# NAME.2 → PID.5.2
|
||||
# account_no.1 → PID.18.1
|
||||
resolve_hl7_alias() {
|
||||
local norm; norm=$(printf '%s' "$1" | tr '[:lower:]' '[:upper:]' | tr ' ' '_')
|
||||
case "$norm" in
|
||||
MRN|PATIENT_ID|PT_ID|PATIENTID) echo "PID.3" ;;
|
||||
ALT_ID|ALTID|ALT_PATIENT_ID|ALT_PT_ID) echo "PID.4" ;;
|
||||
NAME|PATIENT_NAME|PT_NAME) echo "PID.5" ;;
|
||||
MAIDEN|MOTHER_MAIDEN) echo "PID.6" ;;
|
||||
DOB|BIRTHDATE|BIRTH_DATE|BIRTHDAY) echo "PID.7" ;;
|
||||
SEX|GENDER) echo "PID.8" ;;
|
||||
ALIAS) echo "PID.9" ;;
|
||||
ADDR|ADDRESS|PT_ADDRESS) echo "PID.11" ;;
|
||||
PHONE|HOME_PHONE) echo "PID.13" ;;
|
||||
WORK_PHONE|BUSINESS_PHONE) echo "PID.14" ;;
|
||||
ACCT|ACCOUNT|ACCOUNT_NUMBER|ACCOUNTNUM|ACCT_NUM|ACCOUNT_NO) echo "PID.18" ;;
|
||||
SSN) echo "PID.19" ;;
|
||||
LIC|LICENSE|DRIVER_LICENSE|DL) echo "PID.20" ;;
|
||||
DOD|DEATH_DATE|DATE_OF_DEATH) echo "PID.29" ;;
|
||||
PATIENT_CLASS|PT_CLASS) echo "PV1.2" ;;
|
||||
LOCATION|ASSIGNED_LOCATION|PT_LOCATION|BED_LOCATION) echo "PV1.3" ;;
|
||||
ATTENDING|ATTENDING_DR|ATTENDING_DOCTOR|ATTENDING_PROVIDER) echo "PV1.7" ;;
|
||||
REFERRING|REFERRING_DR|REFERRING_DOCTOR|REFERRING_PROVIDER) echo "PV1.8" ;;
|
||||
CONSULTING|CONSULTING_DR|CONSULTING_DOCTOR) echo "PV1.9" ;;
|
||||
ADMITTING|ADMITTING_DR|ADMITTING_DOCTOR|ADMITTING_PROVIDER) echo "PV1.17" ;;
|
||||
PT_TYPE|PATIENT_TYPE) echo "PV1.18" ;;
|
||||
VISIT|VISIT_NUMBER|VISIT_NO|ENCOUNTER|CSN|ENC|ENC_NUM) echo "PV1.19" ;;
|
||||
ALT_VISIT|ALT_VISIT_ID) echo "PV1.50" ;;
|
||||
EVENT_DT|EVN_DATE) echo "EVN.2" ;;
|
||||
REASON_FOR_EVENT|EVN_REASON) echo "EVN.4" ;;
|
||||
OPERATOR|EVN_OPERATOR|RESPONSIBLE_OPERATOR) echo "EVN.5" ;;
|
||||
CONTROL_ID|MSG_CONTROL_ID|MSG_CTL_ID|CTLID|MSGID|MESSAGE_ID) echo "MSH.10" ;;
|
||||
MSG_TYPE|MESSAGE_TYPE) echo "MSH.9" ;;
|
||||
EVENT|EVENT_CODE|TRIGGER_EVENT|TRIGGER) echo "MSH.9.2" ;;
|
||||
TIMESTAMP|MSG_TIMESTAMP|MSG_TIME|SENT_TIME) echo "MSH.7" ;;
|
||||
SENDING_APP|SENDING_APPLICATION) echo "MSH.3" ;;
|
||||
SENDING_FACILITY|SENDING_FAC) echo "MSH.4" ;;
|
||||
RECEIVING_APP|RECEIVING_APPLICATION) echo "MSH.5" ;;
|
||||
RECEIVING_FACILITY|RECEIVING_FAC) echo "MSH.6" ;;
|
||||
PROCESSING_ID|PROC_ID) echo "MSH.11" ;;
|
||||
VERSION|HL7_VERSION|VER) echo "MSH.12" ;;
|
||||
NK|NK_NAME|NEXT_OF_KIN) echo "NK1.2" ;;
|
||||
NK_RELATIONSHIP|RELATIONSHIP) echo "NK1.3" ;;
|
||||
NK_ADDRESS) echo "NK1.4" ;;
|
||||
NK_PHONE) echo "NK1.5" ;;
|
||||
GUARANTOR|GUARANTOR_NAME|GT_NAME) echo "GT1.4" ;;
|
||||
GUARANTOR_ADDRESS|GT_ADDRESS) echo "GT1.5" ;;
|
||||
GUARANTOR_PHONE|GT_PHONE) echo "GT1.6" ;;
|
||||
GUARANTOR_SSN|GT_SSN) echo "GT1.12" ;;
|
||||
INSURANCE|INSURANCE_PLAN|INS_PLAN) echo "IN1.2" ;;
|
||||
INSURED_NAME|POLICY_HOLDER|INSURED) echo "IN1.16" ;;
|
||||
INSURED_DOB) echo "IN1.17" ;;
|
||||
POLICY|POLICY_NUMBER|INS_POLICY) echo "IN1.36" ;;
|
||||
DIAGNOSIS|DX|DIAGNOSIS_CODE) echo "DG1.3" ;;
|
||||
DIAGNOSIS_DESC|DX_DESC|DIAGNOSIS_DESCRIPTION) echo "DG1.4" ;;
|
||||
PLACER|PLACER_ORDER|ORDER_NUMBER|ORDER_NO) echo "OBR.2" ;;
|
||||
FILLER|FILLER_ORDER) echo "OBR.3" ;;
|
||||
TEST_CODE|UNIVERSAL_SERVICE_ID|SERVICE_CODE) echo "OBR.4" ;;
|
||||
SPECIMEN|SPECIMEN_SOURCE) echo "OBR.15" ;;
|
||||
ORDERING|ORDERING_PROVIDER|ORDERING_DR) echo "OBR.16" ;;
|
||||
OBS_VALUE|RESULT_VALUE|OBX_VALUE) echo "OBX.5" ;;
|
||||
OBS_STATUS|RESULT_STATUS|OBX_STATUS) echo "OBX.11" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Split the normalized path. If the first token is an alias, replace it.
|
||||
IFS='.' read -ra _parts <<< "$NORMALIZED"
|
||||
_first="${_parts[0]:-}"
|
||||
_aliased=$(resolve_hl7_alias "$_first")
|
||||
if [ -n "$_aliased" ]; then
|
||||
# Replace first token with alias expansion; keep remaining components
|
||||
if [ ${#_parts[@]} -gt 1 ]; then
|
||||
NORMALIZED="${_aliased}.$(IFS=. ; echo "${_parts[*]:1}")"
|
||||
else
|
||||
NORMALIZED="$_aliased"
|
||||
fi
|
||||
fi
|
||||
IFS='.' read -r SEG FNUM CNUM SCNUM <<< "$NORMALIZED"
|
||||
[ -n "$SEG" ] || { echo "hl7-field: bad path: $PATH_SPEC" >&2; exit 2; }
|
||||
|
||||
# Detect encoding characters from MSH
|
||||
|
||||
198
lib/nc-msgs.sh
198
lib/nc-msgs.sh
@ -67,7 +67,7 @@ while [ $# -gt 0 ]; do
|
||||
done
|
||||
|
||||
[ -n "$THREAD" ] || die "usage: nc-msgs.sh <thread> [...flags]"
|
||||
case "$FORMAT" in text|json|count|raw) ;; *) die "bad --format: $FORMAT" ;; esac
|
||||
case "$FORMAT" in text|json|count|raw|oneline|fields|mp|labeled) ;; *) die "bad --format: $FORMAT" ;; esac
|
||||
command -v sqlite3 >/dev/null 2>&1 || die "sqlite3 not on PATH (universally available on Cloverleaf hosts; install via your distro otherwise)"
|
||||
|
||||
# Locate smatdb
|
||||
@ -144,14 +144,22 @@ if [ -n "$TYPE" ]; then
|
||||
WHERE="$WHERE AND Type = '$ESC_TYPE'"
|
||||
fi
|
||||
|
||||
# Coarse LIKE pre-filter for any --field VALUEs (substring presence)
|
||||
# This is just an SQL fast-path; the precise field match happens via hl7-field.sh below.
|
||||
# Coarse LIKE pre-filter — only safe for positive exact/contains operators.
|
||||
# Negation ops, null/wildcard, and absences require post-filter.
|
||||
prefilter_op_path=""; prefilter_op_kind=""; prefilter_op_val=""
|
||||
for filt in "${FILTERS[@]}"; do
|
||||
# Skip negation, null, wildcard — those need every message to be inspected
|
||||
if [[ "$filt" == *"!="* ]] || [[ "$filt" == *"!~"* ]]; then continue; fi
|
||||
# Extract value portion regardless of op
|
||||
if [[ "$filt" == *"~"* ]] && [[ "$filt" != *"!~"* ]]; then
|
||||
val="${filt#*~}"
|
||||
elif [[ "$filt" == *"="* ]]; then
|
||||
val="${filt#*=}"
|
||||
if [ -n "$val" ] && [ "$val" != "$filt" ]; then
|
||||
else continue; fi
|
||||
norm_val=$(printf '%s' "$val" | tr '[:upper:]' '[:lower:]')
|
||||
if [ "$norm_val" = "null" ] || [ -z "$val" ] || [ "$val" = "*" ]; then continue; fi
|
||||
ESC_VAL=$(printf '%s' "$val" | sed "s/'/''/g")
|
||||
WHERE="$WHERE AND MessageContent LIKE '%${ESC_VAL}%'"
|
||||
fi
|
||||
done
|
||||
|
||||
SMATDB=$(locate_smatdb)
|
||||
@ -184,22 +192,100 @@ awk -v RS=$'\x1e' -v FS=$'\x1f' -v outdir="$TMP_OUT" '
|
||||
MSG_COUNT=$(ls "$TMP_OUT"/msg_*.bin 2>/dev/null | wc -l | tr -d ' ')
|
||||
KEPT=0
|
||||
|
||||
# Apply --field filters precisely via hl7-field.sh
|
||||
# Parse a single filter expression: returns path / op / expected via globals.
|
||||
# Supported operators (longest-first match):
|
||||
# !~ does not contain (case-insensitive substring)
|
||||
# != not equal
|
||||
# ~ contains (case-insensitive substring)
|
||||
# = exact equal (or NULL keyword, empty, or * wildcard — see match_filters)
|
||||
parse_filter() {
|
||||
local filt="$1"
|
||||
FP_OP=""; FP_PATH=""; FP_EXPECTED=""
|
||||
if [[ "$filt" == *"!~"* ]]; then
|
||||
FP_PATH="${filt%%!~*}"; FP_EXPECTED="${filt#*!~}"; FP_OP="!~"
|
||||
elif [[ "$filt" == *"!="* ]]; then
|
||||
FP_PATH="${filt%%!=*}"; FP_EXPECTED="${filt#*!=}"; FP_OP="!="
|
||||
elif [[ "$filt" == *"~"* ]]; then
|
||||
FP_PATH="${filt%%~*}"; FP_EXPECTED="${filt#*~}"; FP_OP="~"
|
||||
elif [[ "$filt" == *"="* ]]; then
|
||||
FP_PATH="${filt%%=*}"; FP_EXPECTED="${filt#*=}"; FP_OP="="
|
||||
fi
|
||||
}
|
||||
|
||||
# Returns 0 if the (op, expected) check matches the actual field value(s).
|
||||
field_matches() {
|
||||
local actual="$1" op="$2" expected="$3"
|
||||
local expected_lc; expected_lc=$(printf '%s' "$expected" | tr '[:upper:]' '[:lower:]')
|
||||
local actual_lc; actual_lc=$(printf '%s' "$actual" | tr '[:upper:]' '[:lower:]')
|
||||
local is_null=0
|
||||
if [ -z "$expected" ] || [ "$expected_lc" = "null" ]; then is_null=1; fi
|
||||
|
||||
case "$op" in
|
||||
"=")
|
||||
if [ "$is_null" = "1" ]; then
|
||||
# Null match: actual empty or all-empty reps
|
||||
[ -z "$actual" ] && return 0
|
||||
while IFS= read -r rep; do
|
||||
[ -n "$rep" ] && [ "$rep" != "\"\"" ] && return 1
|
||||
done <<< "$actual"
|
||||
return 0
|
||||
elif [ "$expected" = "*" ]; then
|
||||
# Wildcard — any non-empty rep
|
||||
while IFS= read -r rep; do
|
||||
[ -n "$rep" ] && [ "$rep" != "\"\"" ] && return 0
|
||||
done <<< "$actual"
|
||||
return 1
|
||||
fi
|
||||
while IFS= read -r rep; do
|
||||
[ "$rep" = "$expected" ] && return 0
|
||||
done <<< "$actual"
|
||||
return 1
|
||||
;;
|
||||
"!=")
|
||||
if [ "$is_null" = "1" ]; then
|
||||
# Not-null: at least one rep is non-empty
|
||||
while IFS= read -r rep; do
|
||||
[ -n "$rep" ] && [ "$rep" != "\"\"" ] && return 0
|
||||
done <<< "$actual"
|
||||
return 1
|
||||
elif [ "$expected" = "*" ]; then
|
||||
# Not-any-value = null
|
||||
[ -z "$actual" ] && return 0
|
||||
while IFS= read -r rep; do
|
||||
[ -n "$rep" ] && [ "$rep" != "\"\"" ] && return 1
|
||||
done <<< "$actual"
|
||||
return 0
|
||||
fi
|
||||
# Not-equal: NO repetition equals expected
|
||||
while IFS= read -r rep; do
|
||||
[ "$rep" = "$expected" ] && return 1
|
||||
done <<< "$actual"
|
||||
return 0
|
||||
;;
|
||||
"~")
|
||||
# Contains, case-insensitive
|
||||
[ "$is_null" = "1" ] && return 1 # contains-nothing is meaningless
|
||||
[[ "$actual_lc" == *"$expected_lc"* ]] && return 0
|
||||
return 1
|
||||
;;
|
||||
"!~")
|
||||
# Does not contain, case-insensitive
|
||||
[ "$is_null" = "1" ] && return 0 # always passes "doesn't contain (nothing)"
|
||||
[[ "$actual_lc" == *"$expected_lc"* ]] && return 1
|
||||
return 0
|
||||
;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Apply all --field filters; AND semantics.
|
||||
match_filters() {
|
||||
local msg_file="$1"
|
||||
for filt in "${FILTERS[@]}"; do
|
||||
path="${filt%%=*}"
|
||||
expected="${filt#*=}"
|
||||
[ "$path" = "$expected" ] && continue # skip if "=" missing
|
||||
# exact match: any repetition equal to expected
|
||||
actual=$("$HL7F" "$path" "$msg_file" 2>/dev/null)
|
||||
matched=0
|
||||
if [ -n "$actual" ]; then
|
||||
while IFS= read -r rep; do
|
||||
[ "$rep" = "$expected" ] && { matched=1; break; }
|
||||
done <<< "$actual"
|
||||
fi
|
||||
[ "$matched" = "1" ] || return 1
|
||||
parse_filter "$filt"
|
||||
[ -z "$FP_OP" ] && continue
|
||||
local actual; actual=$("$HL7F" "$FP_PATH" "$msg_file" 2>/dev/null)
|
||||
field_matches "$actual" "$FP_OP" "$FP_EXPECTED" || return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
@ -225,7 +311,7 @@ case "$FORMAT" in
|
||||
fi
|
||||
done
|
||||
;;
|
||||
text)
|
||||
text|oneline|fields|mp|labeled)
|
||||
i=0
|
||||
for f in "$TMP_OUT"/msg_*.bin; do
|
||||
i=$((i+1))
|
||||
@ -236,14 +322,86 @@ case "$FORMAT" in
|
||||
typ=$(printf '%s' "$meta" | awk -F'\t' '{print $2}')
|
||||
src=$(printf '%s' "$meta" | awk -F'\t' '{print $3}')
|
||||
dst=$(printf '%s' "$meta" | awk -F'\t' '{print $4}')
|
||||
# Render time
|
||||
if [ "$tm" -gt 100000000000 ] 2>/dev/null; then
|
||||
tm_h=$(date -r $((tm/1000)) 2>/dev/null || date -d "@$((tm/1000))" 2>/dev/null || echo "$tm")
|
||||
else
|
||||
tm_h="$tm"
|
||||
fi
|
||||
printf '===== msg %d time=%s type=%s src=%s dst=%s =====\n' "$KEPT" "$tm_h" "$typ" "$src" "$dst"
|
||||
case "$FORMAT" in
|
||||
text)
|
||||
tr '\r' '\n' < "$f"
|
||||
;;
|
||||
oneline)
|
||||
# Compact: single line, segments separated by visible '⏎' marker
|
||||
tr '\r' '\037' < "$f" | sed 's/\x1f/ ⏎ /g'
|
||||
printf '\n'
|
||||
;;
|
||||
fields|mp)
|
||||
# Each field on its own line: SEG.N: value (skips empty)
|
||||
tr '\r' '\n' < "$f" | awk -F'|' '
|
||||
NF > 0 {
|
||||
seg = substr($1, 1, 3)
|
||||
if (seg == "") next
|
||||
is_msh = (seg == "MSH")
|
||||
for (k=2; k<=NF; k++) {
|
||||
val = $k
|
||||
if (val == "" || val == "\"\"") continue
|
||||
fnum = is_msh ? k : (k - 1)
|
||||
printf "%s.%d: %s\n", seg, fnum, val
|
||||
}
|
||||
}'
|
||||
;;
|
||||
labeled)
|
||||
# Same as fields but adds the friendly alias when known.
|
||||
tr '\r' '\n' < "$f" | awk -F'|' '
|
||||
BEGIN {
|
||||
# Reverse alias lookup table (alias for SEG.N → label)
|
||||
a["PID.3"]="mrn"; a["PID.4"]="alt_id"
|
||||
a["PID.5"]="name"; a["PID.6"]="mothers_maiden"
|
||||
a["PID.7"]="dob"; a["PID.8"]="sex"
|
||||
a["PID.11"]="address"; a["PID.13"]="phone"
|
||||
a["PID.14"]="work_phone"; a["PID.18"]="account"
|
||||
a["PID.19"]="ssn"; a["PID.20"]="license"
|
||||
a["PID.29"]="dod"
|
||||
a["PV1.2"]="patient_class"; a["PV1.3"]="location"
|
||||
a["PV1.7"]="attending"; a["PV1.8"]="referring"
|
||||
a["PV1.9"]="consulting"; a["PV1.17"]="admitting"
|
||||
a["PV1.18"]="patient_type"; a["PV1.19"]="visit"
|
||||
a["PV1.50"]="alt_visit"
|
||||
a["MSH.3"]="sending_app"; a["MSH.4"]="sending_facility"
|
||||
a["MSH.5"]="receiving_app"; a["MSH.6"]="receiving_facility"
|
||||
a["MSH.7"]="timestamp"; a["MSH.9"]="msg_type"
|
||||
a["MSH.10"]="control_id"; a["MSH.11"]="processing_id"
|
||||
a["MSH.12"]="hl7_version"
|
||||
a["EVN.1"]="trigger_event"; a["EVN.2"]="event_dt"
|
||||
a["EVN.4"]="evn_reason"; a["EVN.5"]="operator"
|
||||
a["NK1.2"]="next_of_kin"; a["NK1.3"]="relationship"
|
||||
a["NK1.4"]="nk_address"; a["NK1.5"]="nk_phone"
|
||||
a["GT1.4"]="guarantor"; a["GT1.5"]="gt_address"
|
||||
a["GT1.6"]="gt_phone"; a["GT1.12"]="gt_ssn"
|
||||
a["IN1.2"]="insurance"; a["IN1.16"]="insured"
|
||||
a["IN1.17"]="insured_dob"; a["IN1.36"]="policy"
|
||||
a["DG1.3"]="diagnosis"; a["DG1.4"]="dx_desc"
|
||||
a["OBR.2"]="placer_order"; a["OBR.3"]="filler_order"
|
||||
a["OBR.4"]="test_code"; a["OBR.16"]="ordering"
|
||||
a["OBX.5"]="value"; a["OBX.11"]="status"
|
||||
}
|
||||
NF > 0 {
|
||||
seg = substr($1, 1, 3)
|
||||
if (seg == "") next
|
||||
is_msh = (seg == "MSH")
|
||||
for (k=2; k<=NF; k++) {
|
||||
val = $k
|
||||
if (val == "" || val == "\"\"") continue
|
||||
fnum = is_msh ? k : (k - 1)
|
||||
key = seg "." fnum
|
||||
if (key in a) printf "%s (%s): %s\n", key, a[key], val
|
||||
else printf "%s: %s\n", key, val
|
||||
}
|
||||
}'
|
||||
;;
|
||||
esac
|
||||
printf '\n'
|
||||
fi
|
||||
done
|
||||
|
||||
Loading…
Reference in New Issue
Block a user