Repository: nifi
Updated Branches:
  refs/heads/master 4a4d60e6a -> 4179ce664


NIFI-2090 This closes #669. Added options for segment names, parse fields in 
ExtractHL7Attributes


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/4179ce66
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/4179ce66
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/4179ce66

Branch: refs/heads/master
Commit: 4179ce6644beba27fdc9e7586fcecd792594654a
Parents: 4a4d60e
Author: Joey Frazee <[email protected]>
Authored: Wed Jun 22 23:44:40 2016 -0500
Committer: joewitt <[email protected]>
Committed: Fri Jul 22 10:11:03 2016 -0400

----------------------------------------------------------------------
 .../processors/hl7/ExtractHL7Attributes.java    | 252 +++++++++++++++----
 .../hl7/TestExtractHL7Attributes.java           | 213 +++++++++++++++-
 2 files changed, 402 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/4179ce66/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
 
b/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
index 6c35637..ac5b18e 100644
--- 
a/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
+++ 
b/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/main/java/org/apache/nifi/processors/hl7/ExtractHL7Attributes.java
@@ -20,11 +20,14 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.text.WordUtils;
 
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
@@ -46,11 +49,14 @@ import org.apache.nifi.stream.io.StreamUtils;
 import ca.uhn.hl7v2.DefaultHapiContext;
 import ca.uhn.hl7v2.HL7Exception;
 import ca.uhn.hl7v2.HapiContext;
+import ca.uhn.hl7v2.model.Composite;
 import ca.uhn.hl7v2.model.Group;
 import ca.uhn.hl7v2.model.Message;
 import ca.uhn.hl7v2.model.Segment;
 import ca.uhn.hl7v2.model.Structure;
 import ca.uhn.hl7v2.model.Type;
+import ca.uhn.hl7v2.model.Visitable;
+import ca.uhn.hl7v2.parser.CanonicalModelClassFactory;
 import ca.uhn.hl7v2.parser.DefaultEscaping;
 import ca.uhn.hl7v2.parser.EncodingCharacters;
 import ca.uhn.hl7v2.parser.Escaping;
@@ -68,8 +74,13 @@ import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
         + "a value of \"2.1\" and an attribute named \"OBX_11.3\" with a value 
of \"93000^CPT4\".")
 public class ExtractHL7Attributes extends AbstractProcessor {
 
+    private static final EncodingCharacters HL7_ENCODING = 
EncodingCharacters.defaultInstance();
+
+    private static final Escaping HL7_ESCAPING = new DefaultEscaping();
+
     public static final PropertyDescriptor CHARACTER_SET = new 
PropertyDescriptor.Builder()
             .name("Character Encoding")
+            .displayName("Character Encoding")
             .description("The Character Encoding that is used to encode the 
HL7 data")
             .required(true)
             .expressionLanguageSupported(true)
@@ -77,6 +88,46 @@ public class ExtractHL7Attributes extends AbstractProcessor {
             .defaultValue("UTF-8")
             .build();
 
+    public static final PropertyDescriptor USE_SEGMENT_NAMES = new 
PropertyDescriptor.Builder()
+            .name("use-segment-names")
+            .displayName("Use Segment Names")
+            .description("Whether or not to use HL7 segment names in 
attributes")
+            .required(true)
+            .allowableValues("true", "false")
+            .defaultValue("false")
+            .addValidator(StandardValidators.BOOLEAN_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor PARSE_SEGMENT_FIELDS = new 
PropertyDescriptor.Builder()
+            .name("parse-segment-fields")
+            .displayName("Parse Segment Fields")
+            .description("Whether or not to parse HL7 segment fields into 
attributes")
+            .required(true)
+            .allowableValues("true", "false")
+            .defaultValue("false")
+            .addValidator(StandardValidators.BOOLEAN_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor SKIP_VALIDATION = new 
PropertyDescriptor.Builder()
+            .name("skip-validation")
+            .displayName("Skip Validation")
+            .description("Whether or not to validate HL7 message values")
+            .required(true)
+            .allowableValues("true", "false")
+            .defaultValue("true")
+            .addValidator(StandardValidators.BOOLEAN_VALIDATOR)
+            .build();
+
+    public static final PropertyDescriptor HL7_INPUT_VERSION = new 
PropertyDescriptor.Builder()
+            .name("hl7-input-version")
+            .displayName("HL7 Input Version")
+            .description("The HL7 version to use for parsing and validation")
+            .required(true)
+            .allowableValues("autodetect", "2.2", "2.3", "2.3.1", "2.4", 
"2.5", "2.5.1", "2.6")
+            .defaultValue("autodetect")
+            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+            .build();
+
     public static final Relationship REL_SUCCESS = new Relationship.Builder()
             .name("success")
             .description("A FlowFile is routed to this relationship if it is 
properly parsed as HL7 and its attributes extracted")
@@ -91,6 +142,10 @@ public class ExtractHL7Attributes extends AbstractProcessor 
{
     protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
         final List<PropertyDescriptor> properties = new ArrayList<>();
         properties.add(CHARACTER_SET);
+        properties.add(USE_SEGMENT_NAMES);
+        properties.add(PARSE_SEGMENT_FIELDS);
+        properties.add(SKIP_VALIDATION);
+        properties.add(HL7_INPUT_VERSION);
         return properties;
     }
 
@@ -110,6 +165,10 @@ public class ExtractHL7Attributes extends 
AbstractProcessor {
         }
 
         final Charset charset = 
Charset.forName(context.getProperty(CHARACTER_SET).evaluateAttributeExpressions(flowFile).getValue());
+        final Boolean useSegmentNames = 
context.getProperty(USE_SEGMENT_NAMES).asBoolean();
+        final Boolean parseSegmentFields = 
context.getProperty(PARSE_SEGMENT_FIELDS).asBoolean();
+        final Boolean skipValidation = 
context.getProperty(SKIP_VALIDATION).asBoolean();
+        final String inputVersion = 
context.getProperty(HL7_INPUT_VERSION).getValue();
 
         final byte[] buffer = new byte[(int) flowFile.getSize()];
         session.read(flowFile, new InputStreamCallback() {
@@ -121,89 +180,172 @@ public class ExtractHL7Attributes extends 
AbstractProcessor {
 
         @SuppressWarnings("resource")
         final HapiContext hapiContext = new DefaultHapiContext();
-        hapiContext.setValidationContext((ValidationContext) 
ValidationContextFactory.noValidation());
+        if (!inputVersion.equals("autodetect")) {
+            hapiContext.setModelClassFactory(new 
CanonicalModelClassFactory(inputVersion));
+        }
+        if (skipValidation) {
+            hapiContext.setValidationContext((ValidationContext) 
ValidationContextFactory.noValidation());
+        }
 
         final PipeParser parser = hapiContext.getPipeParser();
         final String hl7Text = new String(buffer, charset);
-        final Message message;
         try {
-            message = parser.parse(hl7Text);
-            final Group group = message.getParent();
-
-            final Map<String, String> attributes = new HashMap<>();
-            extractAttributes(group, attributes);
+            final Message message = parser.parse(hl7Text);
+            final Map<String, String> attributes = getAttributes(message, 
useSegmentNames, parseSegmentFields);
             flowFile = session.putAllAttributes(flowFile, attributes);
-            getLogger().info("Successfully extracted {} attributes for {}; 
routing to success", new Object[]{attributes.size(), flowFile});
             getLogger().debug("Added the following attributes for {}: {}", new 
Object[]{flowFile, attributes});
-            session.transfer(flowFile, REL_SUCCESS);
         } catch (final HL7Exception e) {
             getLogger().error("Failed to extract attributes from {} due to 
{}", new Object[]{flowFile, e});
             session.transfer(flowFile, REL_FAILURE);
             return;
         }
+
+        session.transfer(flowFile, REL_SUCCESS);
     }
 
-    private void extractAttributes(final Group group, final Map<String, 
String> attributes) throws HL7Exception {
-        extractAttributes(group, attributes, new HashMap<String, Integer>());
+    public static Map<String, String> getAttributes(final Group group, final 
boolean useNames, final boolean parseFields) throws HL7Exception {
+        final Map<String, String> attributes = new TreeMap<>();
+        if (!isEmpty(group)) {
+            for (final Map.Entry<String, Segment> segmentEntry : 
getAllSegments(group).entrySet()) {
+                final String segmentKey = segmentEntry.getKey();
+                final Segment segment = segmentEntry.getValue();
+                final Map<String, Type> fields = getAllFields(segmentKey, 
segment, useNames);
+                for (final Map.Entry<String, Type> fieldEntry : 
fields.entrySet()) {
+                    final String fieldKey = fieldEntry.getKey();
+                    final Type field = fieldEntry.getValue();
+                    // These maybe should used the escaped values, but that 
would
+                    // change the existing non-broken behavior of the processor
+                    if (parseFields && (field instanceof Composite) && 
!isTimestamp(field)) {
+                        for (final Map.Entry<String, Type> componentEntry : 
getAllComponents(fieldKey, field).entrySet()) {
+                            final String componentKey = 
componentEntry.getKey();
+                            final Type component = componentEntry.getValue();
+                            final String componentValue = 
HL7_ESCAPING.unescape(component.encode(), HL7_ENCODING);
+                            if (!StringUtils.isEmpty(componentValue)) {
+                                attributes.put(componentKey, componentValue);
+                            }
+                        }
+                    } else {
+                        final String fieldValue = 
HL7_ESCAPING.unescape(field.encode(), HL7_ENCODING);
+                        if (!StringUtils.isEmpty(fieldValue)) {
+                            attributes.put(fieldKey, fieldValue);
+                        }
+                    }
+
+                }
+            }
+        }
+        return attributes;
     }
 
-    private void extractAttributes(final Group group, final Map<String, 
String> attributes, final Map<String, Integer> segmentCounts) throws 
HL7Exception {
-        if (group.isEmpty()) {
-            return;
+    private static Map<String, Segment> getAllSegments(final Group group) 
throws HL7Exception {
+        final Map<String, Segment> segments = new TreeMap<>();
+        addSegments(group, segments);
+        return segments;
+    }
+
+    private static void addSegments(final Group group, final Map<String, 
Segment> segments) throws HL7Exception {
+        if (!isEmpty(group)) {
+            for (final String name : group.getNames()) {
+                for (final Structure structure : group.getAll(name)) {
+                    if (group.isGroup(name) && structure instanceof Group) {
+                        addSegments((Group) structure, segments);
+                    } else if (structure instanceof Segment) {
+                        addSegments((Segment) structure, segments);
+                    }
+                }
+            }
         }
+    }
 
-        final String[] structureNames = group.getNames();
-        for (final String structName : structureNames) {
-            final Structure[] subStructures = group.getAll(structName);
+    private static void addSegments(final Segment segment, final Map<String, 
Segment> segments) throws HL7Exception {
+        if (!isEmpty(segment)) {
+            final StringBuilder sb = new 
StringBuilder().append(segment.getName());
+            if (isRepeating(segment)) {
+                final Type field = segment.getField(1, 0);
+                if (!isEmpty(field)) {
+                    final String fieldValue = field.encode();
+                    final int segmentIndex = StringUtils.isEmpty(fieldValue) ? 
1 : Integer.parseInt(fieldValue);
+                    sb.append("_").append(segmentIndex);
+                }
+            }
+            final String segmentKey = sb.toString();
+            segments.put(segmentKey, segment);
+        }
+    }
 
-            if (group.isGroup(structName)) {
-                for (final Structure subStructure : subStructures) {
-                    final Group subGroup = (Group) subStructure;
-                    extractAttributes(subGroup, attributes, segmentCounts);
+    private static Map<String, Type> getAllFields(final String segmentKey, 
final Segment segment, final boolean useNames) throws HL7Exception {
+        final Map<String, Type> fields = new TreeMap<>();
+        final String[] segmentNames = segment.getNames();
+        for (int i = 1; i <= segment.numFields(); i++) {
+            final Type field = segment.getField(i, 0);
+            if (!isEmpty(field)) {
+                final String fieldName;
+                if (useNames) {
+                    fieldName = 
WordUtils.capitalize(segmentNames[i-1]).replaceAll("\\W+", "");
+                } else {
+                    fieldName = String.valueOf(i);
                 }
-            } else {
-                for (final Structure structure : subStructures) {
-                    final Segment segment = (Segment) structure;
-
-                    final String segmentName = segment.getName();
-                    Integer segmentNum = segmentCounts.get(segmentName);
-                    if (segmentNum == null) {
-                        segmentNum = 1;
-                        segmentCounts.put(segmentName, 1);
-                    } else {
-                        segmentNum++;
-                        segmentCounts.put(segmentName, segmentNum);
-                    }
 
-                    final boolean segmentRepeating = 
segment.getParent().isRepeating(segment.getName());
-                    final boolean parentRepeating = 
(segment.getParent().getParent() != segment.getParent() && 
segment.getParent().getParent().isRepeating(segment.getParent().getName()));
-                    final boolean useSegmentIndex = segmentRepeating || 
parentRepeating;
+                final String fieldKey = new StringBuilder()
+                    .append(segmentKey)
+                    .append(".")
+                    .append(fieldName)
+                    .toString();
 
-                    final Map<String, String> attributeMap = 
getAttributes(segment, useSegmentIndex ? segmentNum : null);
-                    attributes.putAll(attributeMap);
+                fields.put(fieldKey, field);
+            }
+        }
+        return fields;
+    }
+
+    private static Map<String, Type> getAllComponents(final String fieldKey, 
final Type field) throws HL7Exception {
+        final Map<String, Type> components = new TreeMap<>();
+        if (!isEmpty(field) && (field instanceof Composite)) {
+            final Type[] types = ((Composite) field).getComponents();
+            for (int i = 0; i < types.length; i++) {
+                final Type type = types[i];
+                if (!isEmpty(type)) {
+                    String fieldName = field.getName();
+                    if (fieldName.equals("CM_MSG")) {
+                        fieldName = "CM";
+                    }
+                    final String typeKey = new StringBuilder()
+                        .append(fieldKey)
+                        .append(".")
+                        .append(fieldName)
+                        .append(".")
+                        .append(i+1)
+                        .toString();
+                    components.put(typeKey, type);
                 }
             }
         }
+        return components;
     }
 
-    private Map<String, String> getAttributes(final Segment segment, final 
Integer segmentNum) throws HL7Exception {
-        final Map<String, String> attributes = new HashMap<>();
+    private static boolean isTimestamp(final Type field) throws HL7Exception {
+        if (isEmpty(field)) {
+            return false;
+        }
+        final String fieldName = field.getName();
+        return (fieldName.equals("TS") || fieldName.equals("DT") || 
fieldName.equals("TM"));
+    }
 
-        final EncodingCharacters encoding = 
EncodingCharacters.defaultInstance();
-        final Escaping escaping = new DefaultEscaping();
-        for (int i = 1; i <= segment.numFields(); i++) {
-            final String fieldName = segment.getName() + (segmentNum == null ? 
"" : "_" + segmentNum) + "." + i;
-            final Type[] types = segment.getField(i);
-            for (final Type type : types) {
-                // This maybe should used the escaped values, but that would
-                // change the existing non-broken behavior of the processor
-                final String escapedTypeValue = type.encode();
-                final String unescapedTypeValue = 
escaping.unescape(escapedTypeValue, encoding);
-                attributes.put(fieldName, unescapedTypeValue);
-            }
+    private static boolean isRepeating(final Segment segment) throws 
HL7Exception {
+        if (isEmpty(segment)) {
+            return false;
         }
 
-        return attributes;
+        final Group parent = segment.getParent();
+        final Group grandparent = parent.getParent();
+        if (parent == grandparent) {
+            return false;
+        }
+
+        return grandparent.isRepeating(parent.getName());
     }
 
+    private static boolean isEmpty(final Visitable visitable) throws 
HL7Exception {
+        return (visitable == null || visitable.isEmpty());
+    }
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/4179ce66/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
 
b/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
index e8ecc1b..cec8551 100644
--- 
a/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
+++ 
b/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/src/test/java/org/apache/nifi/processors/hl7/TestExtractHL7Attributes.java
@@ -31,6 +31,13 @@ import java.util.TreeMap;
 
 public class TestExtractHL7Attributes {
 
+    public static final String TEST_INPUT_RECORD =
+        
"MSH|^~\\&|XXXXXXXX||HealthProvider||||ORU^R01|Q1111111111111111111|P|2.3|\r\n" 
+
+        
"PID|||12345^^^XYZ^MR||SMITH^JOHN||19700100|M||||||||||111111111111|123456789|\r\n"
 +
+        "PD1||||1234567890^LAST^FIRST^M^^^^^NPI|\r\n" +
+        "OBR|1|341856649^HNAM_ORDERID|000000000000000000|648088^Basic 
Metabolic 
Panel|||20150101000000|||||||||1620^Johnson^Corey^A||||||20150101000000|||F|||||||||||20150101000000|\r\n"
 +
+        "OBX|1|NM|GLU^Glucose 
Lvl|59|mg/dL|65-99^65^99|L|||F|||20150102000000|\r\n";
+
     @BeforeClass
     public static void setup() {
         System.setProperty("org.slf4j.simpleLogger.log.org.apache.nifi", 
"DEBUG");
@@ -38,13 +45,6 @@ public class TestExtractHL7Attributes {
 
     @Test
     public void testExtract() throws IOException {
-        final String inputRecord =
-            
"MSH|^~\\&|XXXXXXXX||HealthProvider||||ORU^R01|Q1111111111111111111|P|2.3|\r\n" 
+
-            
"PID|||12345^^^XYZ^MR||SMITH^JOHN||19700100|M||||||||||111111111111|123456789|\r\n"
 +
-            "PD1||||1234567890^LAST^FIRST^M^^^^^NPI|\r\n" +
-            "OBR|1|341856649^HNAM_ORDERID|000000000000000000|648088^Basic 
Metabolic 
Panel|||20150101000000|||||||||1620^Johnson^Corey^A||||||20150101000000|||F|||||||||||20150101000000|\r\n"
 +
-            "OBX|1|NM|GLU^Glucose 
Lvl|59|mg/dL|65-99^65^99|L|||F|||20150102000000|\r\n";
-
         final SortedMap<String, String> expectedAttributes = new TreeMap<>();
         // MSH.1 and MSH.2 could be escaped, but it's not clear which is right
         expectedAttributes.put("MSH.1", "|");
@@ -82,7 +82,94 @@ public class TestExtractHL7Attributes {
         expectedAttributes.put("PID.19", "123456789");
 
         final TestRunner runner = 
TestRunners.newTestRunner(ExtractHL7Attributes.class);
-        runner.enqueue(inputRecord.getBytes(StandardCharsets.UTF_8));
+        runner.enqueue(TEST_INPUT_RECORD.getBytes(StandardCharsets.UTF_8));
+
+        runner.run();
+        runner.assertAllFlowFilesTransferred(ExtractHL7Attributes.REL_SUCCESS, 
1);
+
+        final MockFlowFile out = 
runner.getFlowFilesForRelationship(ExtractHL7Attributes.REL_SUCCESS).get(0);
+        final SortedMap<String, String> sortedAttrs = new 
TreeMap<>(out.getAttributes());
+
+        for (final Map.Entry<String, String> entry : 
expectedAttributes.entrySet()) {
+            final String key = entry.getKey();
+            final String expected = entry.getValue();
+            final String actual = sortedAttrs.get(key);
+            Assert.assertEquals(key + " segment values do not match", 
expected, actual);
+        }
+
+        int mshSegmentCount = 0;
+        int obrSegmentCount = 0;
+        int obxSegmentCount = 0;
+        int pd1SegmentCount = 0;
+        int pidSegmentCount = 0;
+
+        for (final Map.Entry<String, String> entry : sortedAttrs.entrySet()) {
+            final String entryKey = entry.getKey();
+            if (entryKey.startsWith("MSH")) {
+                mshSegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("OBR")) {
+                obrSegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("OBX")) {
+                obxSegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("PD1")) {
+                pd1SegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("PID")) {
+                pidSegmentCount++;
+                continue;
+            }
+        }
+
+        Assert.assertEquals("Did not have the proper number of MSH segments", 
8, mshSegmentCount);
+        Assert.assertEquals("Did not have the proper number of OBR segments", 
9, obrSegmentCount);
+        Assert.assertEquals("Did not have the proper number of OBX segments", 
9, obxSegmentCount);
+        Assert.assertEquals("Did not have the proper number of PD1 segments", 
1, pd1SegmentCount);
+        Assert.assertEquals("Did not have the proper number of PID segments", 
6, pidSegmentCount);
+    }
+
+    @Test
+    public void testExtractWithSegmentNames() throws IOException {
+        final SortedMap<String, String> expectedAttributes = new TreeMap<>();
+        expectedAttributes.put("MSH.FieldSeparator", "|");
+        expectedAttributes.put("MSH.EncodingCharacters", "^~\\&");
+        expectedAttributes.put("MSH.SendingApplication", "XXXXXXXX");
+        expectedAttributes.put("MSH.ReceivingApplication", "HealthProvider");
+        expectedAttributes.put("MSH.MessageType", "ORU^R01");
+        expectedAttributes.put("MSH.MessageControlID", "Q1111111111111111111");
+        expectedAttributes.put("MSH.ProcessingID", "P");
+        expectedAttributes.put("MSH.VersionID", "2.3");
+        expectedAttributes.put("OBR_1.SetIDObservationRequest", "1");
+        expectedAttributes.put("OBR_1.PlacerOrderNumber", 
"341856649^HNAM_ORDERID");
+        expectedAttributes.put("OBR_1.FillerOrderNumber", 
"000000000000000000");
+        expectedAttributes.put("OBR_1.UniversalServiceIdentifier", 
"648088^Basic Metabolic Panel");
+        expectedAttributes.put("OBR_1.ObservationDateTime", "20150101000000");
+        expectedAttributes.put("OBR_1.OrderingProvider", 
"1620^Johnson^Corey^A");
+        expectedAttributes.put("OBR_1.ResultsRptStatusChngDateTime", 
"20150101000000");
+        expectedAttributes.put("OBR_1.ResultStatus", "F");
+        expectedAttributes.put("OBR_1.ScheduledDateTime", "20150101000000");
+        expectedAttributes.put("OBX_1.SetIDOBX", "1");
+        expectedAttributes.put("OBX_1.ValueType", "NM");
+        expectedAttributes.put("OBX_1.ObservationIdentifier", "GLU^Glucose 
Lvl");
+        expectedAttributes.put("OBX_1.ObservationSubID", "59");
+        expectedAttributes.put("OBX_1.ObservationValue", "mg/dL");
+        expectedAttributes.put("OBX_1.Units", "65-99^65^99");
+        expectedAttributes.put("OBX_1.ReferencesRange", "L");
+        expectedAttributes.put("OBX_1.NatureOfAbnormalTest", "F");
+        expectedAttributes.put("OBX_1.UserDefinedAccessChecks", 
"20150102000000");
+        expectedAttributes.put("PD1.PatientPrimaryCareProviderNameIDNo", 
"1234567890^LAST^FIRST^M^^^^^NPI");
+        expectedAttributes.put("PID.PatientIDInternalID", "12345^^^XYZ^MR");
+        expectedAttributes.put("PID.PatientName", "SMITH^JOHN");
+        expectedAttributes.put("PID.DateOfBirth", "19700100");
+        expectedAttributes.put("PID.Sex", "M");
+        expectedAttributes.put("PID.PatientAccountNumber", "111111111111");
+        expectedAttributes.put("PID.SSNNumberPatient", "123456789");
+
+        final TestRunner runner = 
TestRunners.newTestRunner(ExtractHL7Attributes.class);
+        runner.setProperty(ExtractHL7Attributes.USE_SEGMENT_NAMES, "true");
+        runner.enqueue(TEST_INPUT_RECORD.getBytes(StandardCharsets.UTF_8));
 
         runner.run();
         runner.assertAllFlowFilesTransferred(ExtractHL7Attributes.REL_SUCCESS, 
1);
@@ -100,6 +187,7 @@ public class TestExtractHL7Attributes {
         int mshSegmentCount = 0;
         int obrSegmentCount = 0;
         int obxSegmentCount = 0;
+        int pd1SegmentCount = 0;
         int pidSegmentCount = 0;
 
         for (final Map.Entry<String, String> entry : sortedAttrs.entrySet()) {
@@ -113,6 +201,9 @@ public class TestExtractHL7Attributes {
             } else if (entryKey.startsWith("OBX")) {
                 obxSegmentCount++;
                 continue;
+            } else if (entryKey.startsWith("PD1")) {
+                pd1SegmentCount++;
+                continue;
             } else if (entryKey.startsWith("PID")) {
                 pidSegmentCount++;
                 continue;
@@ -122,6 +213,112 @@ public class TestExtractHL7Attributes {
         Assert.assertEquals("Did not have the proper number of MSH segments", 
8, mshSegmentCount);
         Assert.assertEquals("Did not have the proper number of OBR segments", 
9, obrSegmentCount);
         Assert.assertEquals("Did not have the proper number of OBX segments", 
9, obxSegmentCount);
+        Assert.assertEquals("Did not have the proper number of PD1 segments", 
1, pd1SegmentCount);
         Assert.assertEquals("Did not have the proper number of PID segments", 
6, pidSegmentCount);
     }
+
+    @Test
+    public void testExtractWithSegmentNamesAndFields() throws IOException {
+        final SortedMap<String, String> expectedAttributes = new TreeMap<>();
+        expectedAttributes.put("MSH.FieldSeparator", "|");
+        expectedAttributes.put("MSH.EncodingCharacters", "^~\\&");
+        expectedAttributes.put("MSH.SendingApplication.HD.1", "XXXXXXXX");
+        expectedAttributes.put("MSH.ReceivingApplication.HD.1", 
"HealthProvider");
+        expectedAttributes.put("MSH.MessageType.CM.1", "ORU");
+        expectedAttributes.put("MSH.MessageType.CM.2", "R01");
+        expectedAttributes.put("MSH.MessageControlID", "Q1111111111111111111");
+        expectedAttributes.put("MSH.ProcessingID.PT.1", "P");
+        expectedAttributes.put("MSH.VersionID", "2.3");
+        expectedAttributes.put("OBR_1.SetIDObservationRequest", "1");
+        expectedAttributes.put("OBR_1.PlacerOrderNumber.EI.1", "341856649");
+        expectedAttributes.put("OBR_1.PlacerOrderNumber.EI.2", "HNAM_ORDERID");
+        expectedAttributes.put("OBR_1.FillerOrderNumber.EI.1", 
"000000000000000000");
+        expectedAttributes.put("OBR_1.UniversalServiceIdentifier.CE.1", 
"648088");
+        expectedAttributes.put("OBR_1.UniversalServiceIdentifier.CE.2", "Basic 
Metabolic Panel");
+        expectedAttributes.put("OBR_1.ObservationDateTime", "20150101000000");
+        expectedAttributes.put("OBR_1.OrderingProvider.XCN.1", "1620");
+        expectedAttributes.put("OBR_1.OrderingProvider.XCN.2", "Johnson");
+        expectedAttributes.put("OBR_1.OrderingProvider.XCN.3", "Corey");
+        expectedAttributes.put("OBR_1.OrderingProvider.XCN.4", "A");
+        expectedAttributes.put("OBR_1.ResultsRptStatusChngDateTime", 
"20150101000000");
+        expectedAttributes.put("OBR_1.ResultStatus", "F");
+        expectedAttributes.put("OBR_1.ScheduledDateTime", "20150101000000");
+        expectedAttributes.put("OBX_1.SetIDOBX", "1");
+        expectedAttributes.put("OBX_1.ValueType", "NM");
+        expectedAttributes.put("OBX_1.ObservationIdentifier.CE.1", "GLU");
+        expectedAttributes.put("OBX_1.ObservationIdentifier.CE.2", "Glucose 
Lvl");
+        expectedAttributes.put("OBX_1.ObservationSubID", "59");
+        expectedAttributes.put("OBX_1.ObservationValue", "mg/dL");
+        expectedAttributes.put("OBX_1.Units.CE.1", "65-99");
+        expectedAttributes.put("OBX_1.Units.CE.3", "65");
+        expectedAttributes.put("OBX_1.Units.CE.3", "99");
+        expectedAttributes.put("OBX_1.ReferencesRange", "L");
+        expectedAttributes.put("OBX_1.NatureOfAbnormalTest", "F");
+        expectedAttributes.put("OBX_1.UserDefinedAccessChecks", 
"20150102000000");
+        expectedAttributes.put("PD1.PatientPrimaryCareProviderNameIDNo.XCN.1", 
"1234567890");
+        expectedAttributes.put("PD1.PatientPrimaryCareProviderNameIDNo.XCN.2", 
"LAST");
+        expectedAttributes.put("PD1.PatientPrimaryCareProviderNameIDNo.XCN.3", 
"FIRST");
+        expectedAttributes.put("PD1.PatientPrimaryCareProviderNameIDNo.XCN.4", 
"M");
+        expectedAttributes.put("PD1.PatientPrimaryCareProviderNameIDNo.XCN.9", 
"NPI");
+        expectedAttributes.put("PID.PatientIDInternalID.CX.1", "12345");
+        expectedAttributes.put("PID.PatientIDInternalID.CX.4", "XYZ");
+        expectedAttributes.put("PID.PatientIDInternalID.CX.5", "MR");
+        expectedAttributes.put("PID.PatientName.XPN.1", "SMITH");
+        expectedAttributes.put("PID.PatientName.XPN.2", "JOHN");
+        expectedAttributes.put("PID.DateOfBirth", "19700100");
+        expectedAttributes.put("PID.Sex", "M");
+        expectedAttributes.put("PID.PatientAccountNumber.CX.1", 
"111111111111");
+        expectedAttributes.put("PID.SSNNumberPatient", "123456789");
+
+        final TestRunner runner = 
TestRunners.newTestRunner(ExtractHL7Attributes.class);
+        runner.setProperty(ExtractHL7Attributes.USE_SEGMENT_NAMES, "true");
+        runner.setProperty(ExtractHL7Attributes.PARSE_SEGMENT_FIELDS, "true");
+        runner.enqueue(TEST_INPUT_RECORD.getBytes(StandardCharsets.UTF_8));
+
+        runner.run();
+        runner.assertAllFlowFilesTransferred(ExtractHL7Attributes.REL_SUCCESS, 
1);
+
+        final MockFlowFile out = 
runner.getFlowFilesForRelationship(ExtractHL7Attributes.REL_SUCCESS).get(0);
+        final SortedMap<String, String> sortedAttrs = new 
TreeMap<>(out.getAttributes());
+
+        for (final Map.Entry<String, String> entry : 
expectedAttributes.entrySet()) {
+            final String key = entry.getKey();
+            final String expected = entry.getValue();
+            final String actual = sortedAttrs.get(key);
+            Assert.assertEquals(key + " segment values do not match", 
expected, actual);
+        }
+
+        int mshSegmentCount = 0;
+        int obrSegmentCount = 0;
+        int obxSegmentCount = 0;
+        int pd1SegmentCount = 0;
+        int pidSegmentCount = 0;
+
+        for (final Map.Entry<String, String> entry : sortedAttrs.entrySet()) {
+            final String entryKey = entry.getKey();
+            if (entryKey.startsWith("MSH")) {
+                mshSegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("OBR")) {
+                obrSegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("OBX")) {
+                obxSegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("PD1")) {
+                pd1SegmentCount++;
+                continue;
+            } else if (entryKey.startsWith("PID")) {
+                pidSegmentCount++;
+                continue;
+            }
+        }
+
+        Assert.assertEquals("Did not have the proper number of MSH segments", 
9, mshSegmentCount);
+        Assert.assertEquals("Did not have the proper number of OBR segments", 
14, obrSegmentCount);
+        Assert.assertEquals("Did not have the proper number of OBX segments", 
12, obxSegmentCount);
+        Assert.assertEquals("Did not have the proper number of PD1 segments", 
5, pd1SegmentCount);
+        Assert.assertEquals("Did not have the proper number of PID segments", 
9, pidSegmentCount);
+    }
+
 }

Reply via email to