diff --git a/docs/Filters-and-Tags.md b/docs/Filters-and-Tags.md index 0a50017b0..319a46015 100644 --- a/docs/Filters-and-Tags.md +++ b/docs/Filters-and-Tags.md @@ -16,6 +16,7 @@ If these filters do not meet your needs, you can also write your own filters. | get_related_segment_list | Given a segment and related segment name, returns the collection of related named segments | `{% assign result = hl7v2Data \| get_related_segment_list: parentSegment, 'childSegmentName' -%}` | | get_parent_segment | Given a child segment name and overall message index, returns the first matched parent segment | `{% assign result = hl7v2Data \| get_parent_segment: 'childSegmentName', 3, 'parentSegmentName' -%}` | | has_segments | Checks if HL7 v2 message has segments | `{% assign result = hl7v2Data \| has_segments: 'segment1\|segment2\|...' -%}` | +| split_data_by_segments | Given an HL7 v2 message and segment name(s) as the separator(s), returns the message list split by separator(s).
Note: Each segment separator will be retained as the first segment of each message in the list, while the segments before the first separator (which may be empty) will be retained as the first message in the list without any separator. | `{% assign result = hl7v2Data \| split_data_by_segments: 'segment1\|segment2\|...' -%}` | ### C-CDA specific filters | Filter | Description | Syntax | diff --git a/release.yml b/release.yml index 623aba57c..44c88edea 100644 --- a/release.yml +++ b/release.yml @@ -17,7 +17,7 @@ variables: functionalTests: "**/*FunctionalTests/*.csproj" buildConfiguration: 'Release' major: 4 - minor: 4 + minor: 5 patch: 0 buildnum: $[counter(format('{0}.{1}.{2}',variables['major'],variables['minor'], variables['patch']), 1)] version: $(major).$(minor).$(patch).$(buildnum) diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/SegmentFiltersTests.cs b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/SegmentFiltersTests.cs index d0ad6cadb..2652fdbf0 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/SegmentFiltersTests.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Filters/SegmentFiltersTests.cs @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------------------------- using System; +using System.Collections.Generic; using System.Linq; using Microsoft.Health.Fhir.Liquid.Converter.Models.Hl7v2; using Microsoft.Health.Fhir.Liquid.Converter.Parsers; @@ -116,6 +117,65 @@ public void GivenAnHl7v2Data_WhenHasSegments_CorrectResultShouldBeReturned() Assert.Throws(() => Filters.HasSegments(new Hl7v2Data(), null)); } + [Fact] + public void GivenAnHl7v2Data_WhenSplitDataBySegments_CorrectResultShouldBeReturned() + { + List splitDataByOrc = Filters.SplitDataBySegments(TestData, "ORC"); + Assert.Equal(4, splitDataByOrc.Count); + Assert.Equal(3, splitDataByOrc[0].Meta.Count); + Assert.Equal(7, splitDataByOrc[1].Meta.Count); + Assert.Equal(2, splitDataByOrc[2].Meta.Count); + Assert.Equal(2, splitDataByOrc[3].Meta.Count); + Assert.Equal(3, splitDataByOrc[0].Data.Count); + Assert.Equal(7, splitDataByOrc[1].Data.Count); + Assert.Equal(2, splitDataByOrc[2].Data.Count); + Assert.Equal(2, splitDataByOrc[3].Data.Count); + Assert.Equal("MSH", splitDataByOrc[0].Meta[0]); + Assert.Equal("ORC", splitDataByOrc[1].Meta[0]); + string expectedOrcValue = @"ORC|RE|4422^NIST-AA-IZ-2|13696^NIST-AA-IZ-2|||||||7824^Jackson^Lily^Suzanne^^^^^NIST-PI-1^L^^^PRN||654^Thomas^Wilma^Elizabeth^^^^^NIST-PI-1^L^^^MD|||||NISTEHRFAC^NISTEHRFacility^HL70362|"; + Assert.Equal(expectedOrcValue, splitDataByOrc[1].Data[0].Value); + + List splitDataByMsh = Filters.SplitDataBySegments(TestData, "MSH"); + Assert.Equal(2, splitDataByMsh.Count); + Assert.Empty(splitDataByMsh[0].Meta); + Assert.Empty(splitDataByMsh[0].Data); + Assert.Equal(14, splitDataByMsh[1].Meta.Count); + Assert.Equal(14, splitDataByMsh[1].Data.Count); + + List splitData = Filters.SplitDataBySegments(TestData, "PID|OBX"); + Assert.Equal(6, splitData.Count); + Assert.Single(splitData[0].Meta); + Assert.Equal(5, splitData[1].Meta.Count); + Assert.Single(splitData[2].Meta); + Assert.Single(splitData[3].Meta); + Assert.Single(splitData[4].Meta); + Assert.Equal(5, splitData[5].Meta.Count); + Assert.Single(splitData[0].Data); + Assert.Equal(5, splitData[1].Data.Count); + Assert.Single(splitData[2].Data); + Assert.Single(splitData[3].Data); + Assert.Single(splitData[4].Data); + Assert.Equal(5, splitData[5].Data.Count); + Assert.Equal("MSH", splitData[0].Meta[0]); + Assert.Equal("PID", splitData[1].Meta[0]); + Assert.Equal("OBX", splitData[2].Meta[0]); + Assert.Equal(@"OBX|1|CE|30963-3^Vaccine Funding Source^LN|1|PHC70^Private^CDCPHINVS||||||F|||20150624", splitData[2].Data[0].Value); + + splitData = Filters.SplitDataBySegments(TestData, "PID|||OBX|KKK|"); + Assert.Equal(6, splitData.Count); + Assert.Equal("MSH", splitData[0].Meta[0]); + Assert.Equal("PID", splitData[1].Meta[0]); + Assert.Equal("OBX", splitData[2].Meta[0]); + + // If separators are not in the data or empty, a data list containing only the original data will be returned. + Assert.StrictEqual(TestData, Filters.SplitDataBySegments(TestData, string.Empty)[0]); + Assert.StrictEqual(TestData, Filters.SplitDataBySegments(TestData, "PV1")[0]); + + // Hl7v2Data and separators could not be null. If one of them is null, NullReferenceException will be thrown. + Assert.Throws(() => Filters.SplitDataBySegments(null, "ORC")); + Assert.Throws(() => Filters.SplitDataBySegments(TestData, null)); + } + private static Hl7v2Data LoadTestData() { var parser = new Hl7v2DataParser(); diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Utilities/Hl7v2DataUtilityTests.cs b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Utilities/Hl7v2DataUtilityTests.cs new file mode 100644 index 000000000..9769179c6 --- /dev/null +++ b/src/Microsoft.Health.Fhir.Liquid.Converter.UnitTests/Utilities/Hl7v2DataUtilityTests.cs @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------------------------------------------- + +using System; +using Microsoft.Health.Fhir.Liquid.Converter.Utilities; +using Xunit; + +namespace Microsoft.Health.Fhir.Liquid.Converter.UnitTests.Utilities +{ + public class Hl7v2DataUtilityTests + { + [Fact] + public void GivenValidMessage_WhenSplitMessageToSegments_CorrectResultShouldBeReturned() + { + string[] testMessage = + { + "\r\nMSH|^~\\&|test\r\nPID|test\r\n\r\nPD1|test\r\nPD1|test\r\n", + "\rMSH|^~\\&|test\rPID|test\r\rPD1|test\rPD1|test\r", + "\nMSH|^~\\&|test\nPID|test\n\nPD1|test\nPD1|test\n", + "\nMSH|^~\\&|test\r\nPID|test\r\r\nPD1|test\r\n\nPD1|test\r\n", + }; + + foreach (string message in testMessage) + { + string[] segments = Hl7v2DataUtility.SplitMessageToSegments(message); + Assert.Equal(4, segments.Length); + Assert.Equal("MSH|^~\\&|test", segments[0]); + Assert.Equal("PID|test", segments[1]); + Assert.Equal("PD1|test", segments[2]); + Assert.Equal("PD1|test", segments[3]); + } + + Assert.Empty(Hl7v2DataUtility.SplitMessageToSegments(string.Empty)); + + // message could not be null + Assert.Throws(() => Hl7v2DataUtility.SplitMessageToSegments(null)); + } + } +} diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/SegmentFilters.cs b/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/SegmentFilters.cs index 8782c03a2..4966b72b5 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/SegmentFilters.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter/Filters/SegmentFilters.cs @@ -128,5 +128,37 @@ private static Dictionary> GetSegmentListsInternal(Hl return result; } + + public static List SplitDataBySegments(Hl7v2Data hl7v2Data, string segmentIdSeparators) + { + var results = new List(); + var result = new Hl7v2Data(); + var segmentIds = new HashSet(segmentIdSeparators.Split(@"|", StringSplitOptions.RemoveEmptyEntries)); + + if (segmentIdSeparators == string.Empty || !segmentIds.Intersect(hl7v2Data.Meta).Any()) + { + results.Add(hl7v2Data); + return results; + } + + for (var i = 0; i < hl7v2Data.Meta.Count; ++i) + { + if (segmentIds.Contains(hl7v2Data.Meta[i])) + { + results.Add(result); + result = new Hl7v2Data(); + } + + result.Meta.Add(hl7v2Data.Meta[i]); + result.Data.Add(hl7v2Data.Data[i]); + } + + if (result.Meta.Count > 0) + { + results.Add(result); + } + + return results; + } } } diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/Hl7v2DataParser.cs b/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/Hl7v2DataParser.cs index 5d74ef34a..69f423bb7 100644 --- a/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/Hl7v2DataParser.cs +++ b/src/Microsoft.Health.Fhir.Liquid.Converter/Parsers/Hl7v2DataParser.cs @@ -10,6 +10,7 @@ using Microsoft.Health.Fhir.Liquid.Converter.InputProcessors; using Microsoft.Health.Fhir.Liquid.Converter.Models; using Microsoft.Health.Fhir.Liquid.Converter.Models.Hl7v2; +using Microsoft.Health.Fhir.Liquid.Converter.Utilities; using Microsoft.Health.Fhir.Liquid.Converter.Validators; namespace Microsoft.Health.Fhir.Liquid.Converter.Parsers @@ -17,7 +18,6 @@ namespace Microsoft.Health.Fhir.Liquid.Converter.Parsers public class Hl7v2DataParser : IDataParser { private static readonly Hl7v2DataValidator Validator = new Hl7v2DataValidator(); - private static readonly string[] SegmentSeparators = { "\r\n", "\r", "\n" }; public object Parse(string message) { @@ -30,7 +30,7 @@ public object Parse(string message) { var result = new Hl7v2Data(message); - var segments = message.Split(SegmentSeparators, StringSplitOptions.RemoveEmptyEntries); + var segments = Hl7v2DataUtility.SplitMessageToSegments(message); Validator.ValidateMessageHeader(segments[0]); var encodingCharacters = ParseHl7v2EncodingCharacters(segments[0]); result.EncodingCharacters = encodingCharacters; diff --git a/src/Microsoft.Health.Fhir.Liquid.Converter/Utilities/Hl7v2DataUtility.cs b/src/Microsoft.Health.Fhir.Liquid.Converter/Utilities/Hl7v2DataUtility.cs new file mode 100644 index 000000000..8996922a6 --- /dev/null +++ b/src/Microsoft.Health.Fhir.Liquid.Converter/Utilities/Hl7v2DataUtility.cs @@ -0,0 +1,22 @@ +// ------------------------------------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Health.Fhir.Liquid.Converter.Utilities +{ + public static class Hl7v2DataUtility + { + private static readonly string[] SegmentSeparators = { "\r\n", "\r", "\n" }; + + public static string[] SplitMessageToSegments(string message) + { + var segments = message.Split(SegmentSeparators, StringSplitOptions.RemoveEmptyEntries); + return segments; + } + } +}