Skip to content

Commit

Permalink
failing unit test for issue phatboyg#58 with condition logic in place
Browse files Browse the repository at this point in the history
fixed test issue

add more conditions to X12 maps

added conditional logic to several X12 maps

created more conditions in maps and created failing unit test for issue phatboyg#65

added new failing test for issue phatboyg#65

updated readme and added tests for issue phatboyg#65
  • Loading branch information
ahives committed Oct 8, 2018
1 parent 010ac95 commit 69cc503
Show file tree
Hide file tree
Showing 78 changed files with 1,855 additions and 235 deletions.
295 changes: 295 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

![Machete](machete_thin_outline_small.png)

[![Join the chat at https://gitter.im/PhatBoyG-Machete/Lobby](https://badges.gitter.im/Machete/Lobby.svg)](https://gitter.im/Machete/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Machete is a parser, object mapper, and query engine for processing sophisticated text.

> Machete will also include translation and formatting, but those features have yet to be developed.
Expand All @@ -27,3 +29,296 @@ Machete was also designed to support X12 messages, support will be coming soon.
Machete is written entirely in C# and was developed using JetBrains Rider. It supports the standard .NET framework, as well as .NET Standard, allowing it to be used on any .NET compatible platform.


## NuGet the Bits

If you are working in HL7, you need the following NuGet packages:<br/>
Machete.NET<br/>
Machete.HL7<br/>
Machete.HL7Schema
<br/><br/>
If you are working in X12, you need the following NuGet packaged:<br/>
Machete.NET<br/>
Machete.X12<br/>
Machete.X12Schema

## Registering Machete Components

Before using any Machete API the components must be registered. Below represents which components that must be registered.

<br/>

**Step 1:** Register the Machete components you need<br/><br/>
*If parsing and/or translating a message, register a schema and parser like so...*

**HL7**
```c#
var schema = Machete.Schema.Factory.CreateHL7<HL7Entity>(cfg => cfg.AddFromNamespaceContaining<MSH>());
var parser = Machete.Parser.Factory.CreateHL7(schema);
```
**X12**
```c#
var schema = Machete.Schema.Factory.CreateX12<X12Entity>(cfg => cfg.AddFromNamespaceContaining<ISA>());
var parser = Machete.Parser.Factory.CreateX12(schema);
```

Since registering the schema is expensive you would want to use the singleton pattern and register this once in your application. You can use your favorite DI container or do those do yourselfers, you can look at HL7MacheteTestHarness or X12MacheteTestHarness for examples.<br/><br/>

*If using the formatter to encode in-memory schema objects into other formats, register these formatters like so...*

**HL7**
```c#
var formatter = Machete.Formatter.Factory.CreateHL7(schema);
```
**X12**
```c#
var formatter = Machete.Formatter.Factory.CreateX12(schema);
```
<br/>

## Parsing Text Data

Parsing is the most fundamental operation that can be performed within Machete. Below are the various ways for the various specifications for which a parsing operation can be performed.

<br/>

**Step 1:** Initialize a parser

*If parsing a single message, then call the parser like so...*
```c#
var parse = parser.Parse(message);
```

<br/>

*If parsing a string of multiple messages, then call the streaming parser like so...*
```c#
using (var stream = new StringReader(message))
{
StreamText text = await new TextReaderStreamTextReader(stream, Environment.NewLine).Text;
var parse = await parser.ParseStream(text, new TextSpan(0, text.Length));

...
}
```
<br/>

*If parsing a file of a single message or multiple messages, then call the streaming parser like so...*
```c#
using (var stream = File.OpenRead(file))
{
StreamText text = await new StreamTextReader(stream).Text;
var parse = await parser.ParseStream(text, new TextSpan(0, text.Length));

...
}
```
<br/>

**Step 2:** Query the message

*If querying entities, then do the following...*

**HL7**
```c#
while (parse.HasResult)
{
while (parse.TryGetEntity(index, out HL7Segment segment))
{
...
}

result = await parse.NextAsync();
}
```
**X12**
```c#
while (parse.HasResult)
{
while (parse.TryGetEntity(index, out X12Segment segment))
{
...
}

result = await parse.NextAsync();
}
```

<br/>

*If querying using layout patterns, then do the following...*

**HL7**
```c#
using (var stream = File.OpenRead(file))
{
var text = await new StreamTextReader(stream).Text;
var parse = await parser.ParseStream(text, new TextSpan(0, text.Length));

if (!schema.TryGetLayout(out ILayoutParserFactory<ORM_O01, HL7Entity> layout))
return;

while (parse.HasResult)
{
IParser<HL7Entity, ORM_O01> query = parse.CreateQuery(layout);
Result<Cursor<HL7Entity>, ORM_O01> queryResult = parse.Query(query);

if (queryResult.HasResult)
{
...
}

result = await result.NextAsync();
}
}
```
**X12**
```c#
using (var stream = File.OpenRead(file))
{
var text = await new StreamTextReader(stream).Text;
var parse = await parser.ParseStream(text, new TextSpan(0, text.Length));

if (!schema.TryGetLayout(out ILayoutParserFactory<HCPA835, X12Entity> layout))
return;

while (parse.HasResult)
{
IParser<X12Entity, HCPA835> query = parse.CreateQuery(layout);
Result<Cursor<X12Entity>, HCPA835> queryResult = parse.Query(query);

if (queryResult.HasResult)
{
...
}

result = await result.NextAsync();
}
}
```

<br/>

*If querying using LINQ, then do the following...*

**LINQ to HL7**
```c#
var query = parse.Query(q =>
{
var obxQuery = from obx in q.Select<OBX>()
from nte in q.Select<NTE>().ZeroOrMore()
select new
{
OBX = obx,
NTE = nte
};

var obrQuery = from obr in q.Select<OBR>()
from dg1 in q.Select<DG1>().Optional()
from obx in obxQuery.Optional()
select new
{
OBR = obr,
DG1 = dg1,
OBX = obx
};

var testQuery = from orc in q.Select<ORC>()
from obr in obrQuery.ZeroOrMore()
select new
{
ORC = orc,
OBR = obr
};

return from msh in q.Select<MSH>()
from nte in q.Select<NTE>().ZeroOrMore()
from skip in q.Except<HL7Segment, ORC>().ZeroOrMore()
from tests in testQuery.ZeroOrMore()
select new
{
MSH = msh,
Notes = nte,
Tests = tests
};
});
```

**LINQ to X12**
```c#
var query = schema.CreateQuery(q =>
from isa in q.Select<ISA>()
from skip in q.Except<X12Segment, NM1>().ZeroOrMore()
from nm1 in q.Select<NM1>()
select nm1);
```

<br/>

**Step 3:** Accessing data

```c#
string placerGroupNumber = result.Result.Tests[0].ORC.PlacerGroupNumber.Select(x => x.EntityIdentifier).ValueOrDefault();
```

Note: Use the *Select* and *ValueOrDefault* methods to prevent exceptions from bubbling up to the caller when attempting to access fields.

## Translating Parsed Data

If you need to change the parsed data, then Machete provides a separate API from the parsing API since the message being parsed is immutable. Below are the steps necessary to translate the data.

<br/>

**Step 1:** Define a translate
```c#
public class MessageTranslation :
HL7Translation<HL7Entity>
{
public MessageTranslation()
{
Translate<MSH>(x => x.Using<ReplaceSendingApplication>());
}
}

public class ReplaceSendingApplication :
HL7SegmentTranslation<MSH, HL7Entity>
{
public ReplaceSendingApplication()
{
Copy(x => x.ReceivingApplication, x => x.SendingApplication);

Set(x => x.CreationDateTime, x => DateTimeOffset.UtcNow);
}
}
```

<br/>

**Step 2:** Get a translator from the schema based on the translate defined in step 1
```c#
var translator = schema.GetTranslator<MessageTranslation>();
```

<br/>

**Step 3:** Translate the parsed data using the translator created in step 2


```c#
var translateResult = await translator.Translate(parse);
```

## Formatting Objects

It is common to use in-memory objects within algorithms but those objects need to encoded before they can be transmitted with other systems. In Machete, this is how you would use the default formatter to output a text string representation of in-memory objects.
```c#
using (var memoryStream = new MemoryStream())
{
var formatted = await formatter.FormatAsync(memoryStream, input);

string text = Encoding.UTF8.GetString(memoryStream.ToArray());

...
}
```

<br/>
11 changes: 11 additions & 0 deletions src/Machete.HL7.Tests/QueryTests/AdvancedQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ select new
};
});

foreach (var orc in result.Result.Tests)
{
if (orc.ORC.PlacerGroupNumber.Select(x => x.EntityIdentifier).IsEqualTo("X0934N"))
{
string placerGroupNumber = orc.ORC
.PlacerGroupNumber
.Select(x => x.EntityIdentifier)
.ValueOrDefault();
}
}

Assert.That(result.HasResult, Is.True);
Assert.AreEqual(3, result.Result.Tests.Count);
Assert.AreEqual(4, result.Result.Notes.Count);
Expand Down

0 comments on commit 69cc503

Please sign in to comment.