Wednesday, February 9, 2011

Translating Everest rc1 Details to Acknowledgement Error Codes


Everest has always included the IResultDetail interface for reporting errors back to developers that serialize or parse HL7v3 structures. More recently in Everest rc1, more implementations of this class have been added to make error reporting more granular. One of the things we can do with this data is create the transport acknowledgement details back to other systems so they can understand the errors as well.

First, let's take a look at the IResultDetail classes that come with Everest:


As you can see, the built in result detail classes all inherit from the ResultDetail. A quick review of each of the classes (from: the Tech Exchange Library)

ClassDescription
MandatoryElementMissingResultDetailIdentifies that an element marked as mandatory is missing from the structure
VocabularyIssueResultDetailIdentifies that an issue has been detected with the vocabulary values specified in a structure
RequiredElementsMissingResultDetailIdentifies that an element marked as required is missing from the structure and no null flavor was specified
NotImplementedResultDetailIdentifies that an element is not recognized by the formatter and its data is lost.
InsufficientRepetitionsResultDetailIdentifies that an element does not meet the minimum number of repetitions
FixedValueMisMatchResultDetailIdentifies that the value of an element does not match its fixed value. The fixed value is taken in place of the original value.


So, how can we use these to create acknowledgement details? Well, simple, we just have to iterate through each result and create the matching code.

Let's write a simple program that reads in a file and creates a response. First, create a new console project with an appropriate CA message assembly and the R1 Formatter, and ITS1 Formatter.




Next, create we start to fill out Program.cs, let's create a function called ValidateMessage that reads a message and returns the validation errors:

static IResultDetail[] ValidateMessage(string file) {

First, we have to setup the formatter, so do that. I'm using an XML ITS1 formatter with DataTypes R1:

MARC.Everest.Formatters.XML.ITS1.Formatter fmtr = new MARC.Everest.Formatters.XML.ITS1.Formatter();
fmtr.GraphAides.Add(typeof(MARC.Everest.Formatters.XML.Datatypes.R1.Formatter));

Next, we'll open the file for read (standard .net stuff)

// Open the file
FileStream fs = null;
try
{
    fs = File.OpenRead(file);

Then we need to get the formatter to parse the object from the file, this will force a validation and will create a basic sanity check. Whenever you call ParseObject from a formatter, if the object returned is null, then the stream content was not HL7v3 content.

IGraphable obj = fmtr.ParseObject(fs);
if (obj == null)
    return new IResultDetail[] {
          new  ResultDetail(
                 ResultDetailType.Error,
                 "Bad Message",
                 (string)null
           )
    };
Next, we return the validation or Details array of the object:

return fmtr.Details;

Finish up our function:

   }
   finally
   {
 
      if (fs != null) fs.Close();

    }
}And the ValidateMessage function is complete. The next function we'll create will turn the array of IResultDetail instances to HL7v3 acknowledgement details.

static IEnumerable<AcknowledgementDetail> CreateAcks(IResultDetail[] dtls)
{


The fact that we can create this function is due to Everest's new combining functionality whereby all AcknowledgementDetail classes are combined into one class type (instead of 10). To create the Acks, we need an IEnumerable structure that we can populate. We'll iterate over the dtls parameter and create a new AcknowledgementDetail for each IResultDetail we're passed:

List<AcknowledgementDetail> retVal = new List<AcknowledgementDetail>();
 foreach(var dtl in dtls)
{
     AcknowledgementDetail ackDtl = new
     AcknowledgementDetail();

 

The first thing we need to do is populate the type code, since the IResultDetail class isn't used by the generated assembly, we need to do a manual translate:

switch(dtl.Type)
{
    case ResultDetailType.Error:
        ackDtl.TypeCode = AcknowledgementDetailType.Error;
        break;
    case ResultDetailType.Warning:
        ackDtl.TypeCode = AcknowledgementDetailType.Warning;
        break;
    case ResultDetailType.Information:
        ackDtl.TypeCode = AcknowledgementDetailType.Information;
        break;
}

Next, we want to translate the type of IResultDetail to a code, here are some examples of how I would do this:

if (dtl is InsufficientRepetionsResultDetail)
    ackDtl.Code = AcknowledgementDetailCode.InsufficientRepetitions;
else if (dtl is MandatoryElementMissingResultDetail)
    ackDtl.Code = AcknowledgementDetailCode.MandatoryElementWithNullValue;
else if (dtl is NotImplementedElementResultDetail)
    ackDtl.Code = AcknowledgementDetailCode.SyntaxError;

 
There would be an if-statement for each IResultDetail you'd want to translate. Next, I just fill in the location and text:

ackDtl.Text = dtl.Message;
ackDtl.Location = new
SET<ST>((ST)dtl.Location);


Then we add it to our return value and finish up the function:

     retVal.Add(ackDtl);
    }

  return retVal;

}


Now all we have to do is fill out our Main function to load the message and create a response. I'm going to use MCCI_IN000002CA just to illustrate.


static void Main(string[] args)
{



MCCI_IN000002CA ret = new MCCI_IN000002CA(

    Guid.NewGuid(),

    DateTime.Now,

    ResponseMode.Immediate,

    MCCI_IN000002CA.GetInteractionId(),
   
MCCI_IN000002CA.GetProfileId(),
   
ProcessingID.Production,
   
AcknowledgementCondition.Never
);ret.Acknowledgement = new Acknowledgement(
    AcknowledgementType.ApplicationAcknowledgementAccept,    new TargetMessage());
ret.Acknowledgement.AcknowledgementDetail.AddRange(
    CreateAcks(ValidateMessage("C:\\test.xml"))
);


// Format to the screen

MARC.Everest.Formatters.XML.ITS1.Formatter fmtr = new MARC.Everest.Formatters.XML.ITS1.Formatter();
fmtr.GraphAides.Add(typeof(MARC.Everest.Formatters.XML.Datatypes.R1.Formatter));
fmtr.ValidateConformance = false; // Just a sample, I know this message isn't correct
fmtr.GraphObject(Console.OpenStandardOutput(), ret);

 }


And voila, you should get a proper AcknowledgementDetail message.

No comments:

Post a Comment