Mirth - basic HL7 to HL7 transformation question - mirth

new to Mirth, not new to engines... finding it a bit challenging to do a basic source to destination HL7v2 transformation.
I've set up my Channel to read from a file as the source, and spit out the destination to a file as well. My output template is ${message.encodedData}. The channel seems to be reading the source correctly, and generating an output. But what I'm struggling with is how cumbersome this is.
I'm playing with an HL7 SIU message, my source has a lot more fields than the destination wants to receive, just need a simple way to map the few fields that are required.
I inserted the source system message template into the Destination Transformer Inbound Message Templates, then I'm doing the following which seems to work:
//MSH Segment
if (msg['MSH'][0]){
var MSH1 = msg['MSH']['MSH.1'];
var MSH2 = msg['MSH']['MSH.2'];
var MSH7 = msg['MSH']['MSH.7'];
var MSH9 = msg['MSH']['MSH.9'];
msg['MSH'] = '';
msg['MSH']['MSH.1']=MSH1;
msg['MSH']['MSH.2']=MSH2;
msg['MSH']['MSH.7']=MSH7;
msg['MSH']['MSH.9']=MSH9;
}
Rinse and repeat for the segments that I need, seems very painful to me.
On a second destination, I'm trying to leverage the Inbound and Outbound Message Template. Inserted the source system template as above, inserted the destination system template in Outbound Message Template.
My Javascript for that one looks something like this:
//MSH Segment
if (msg['MSH'][0]){
tmp['MSH'] = "";
tmp['MSH']['MSH.1'] = msg['MSH']['MSH.1'];
tmp['MSH']['MSH.2'] = msg['MSH']['MSH.2'];
tmp['MSH']['MSH.7'] = msg['MSH']['MSH.7'];
tmp['MSH']['MSH.9'] = msg['MSH']['MSH.9'];
}
It's cleaner, but doesn't seem to work properly, in some messages, my source doesn't have a PV1 segment, but the output contains the sample PV1 segment in the Output Message Template. Do I need to have an initial statement that is tmp = "";
There has to be a easier way to accomplish what I'm trying here, any advise is appreciated!
M

Eventually figured out a different route. Removed the outbound template entirely and built the outbound message from scratch. Here's a snapshot of what it looks like.
var output = <HL7Message/>;
//MSH Segment
createSegment('MSH',output);
output.MSH['MSH.1'] = msg['MSH']['MSH.1'];
output.MSH['MSH.2'] = msg['MSH']['MSH.2'];
output.MSH['MSH.7'] = msg['MSH']['MSH.7'];
output.MSH['MSH.9'] = msg['MSH']['MSH.9'];
//SCH Segment
if (msg['SCH'][0]){
createSegment('SCH',output);
output.SCH['SCH.1'] = msg['SCH']['SCH.1'];
output.SCH['SCH.2'] = msg['SCH']['SCH.2'];
output.SCH['SCH.6'] = msg['SCH']['SCH.6'];
output.SCH['SCH.7'] = msg['SCH']['SCH.7'];
output.SCH['SCH.8'] = msg['SCH']['SCH.8'];
output.SCH['SCH.11'] = msg['SCH']['SCH.11'];
output.SCH['SCH.12'] = msg['SCH']['SCH.12'];
output.SCH['SCH.16'] = msg['SCH']['SCH.16'];
output.SCH['SCH.25'] = msg['SCH']['SCH.25'];
}
var message = SerializerFactory.getSerializer('HL7V2').fromXML(output);
channelMap.put('outmsg',message);
And then in my destination, I use ${outmsg} for the Template.

Related

Email Pdf attachments to Google sheet

I am looking for something that allows me from a mail PDF attachment to get a data in a google sheet.
We all often get PDF attachments in our email and it will be great if we get the entire data in a google sheet.
DO let me know if there is anything like this
Explanation:
Your question is very broad and it is impossible to give a specific answer because that answer would depend on the pdf but also on the data you want to fetch from that, besides all the other details you skipped to mention.
Here I will provide a general code snippet which you can use to get the pdf from the gmail attachments and then convert it to text (string). For this text you can use some regular expressions (which have to be very specific on your use case) to get the desired information and then put it in your sheet.
Code snippet:
The main code will this one. You should only modify this code:
function myFunction() {
const queryString = "label:unread from example#gmail.com" // use your email query
const threads = GmailApp.search(queryString);
const threadsMessages = GmailApp.getMessagesForThreads(threads);
const ss = SpreadsheetApp.getActive();
for (thr = 0, thrs = threads.length; thr < thrs; ++thr) {
let messages = threads[thr].getMessages();
for (msg = 0, msgs = messages.length; msg < msgs; ++msg) {
let attachments = messages[msg].getAttachments();
for (att = 0, atts = attachments.length; att < atts; ++att) {
let attachment_Name = attachments[att].getName();
let filetext = pdfToText( attachments[att], {keepTextfile: false} );
Logger.log(filetext)
// do something with filetext
// build some regular expression that fetches the desired data from filetext
// put this data to the sheet
}}}
}
and pdfToText is a function implemented by Mogsdad which you can find here. Just copy paste the code snippet provided in that link together with myFunction I posted in this answer. Also you have some options which you can use that are very well explained in the link I provided. Important thing to note, to use this library you need to enable the Drive API from the resources.
This will get you started and if you face any issues down the road which you can't find the solution for, you should create a new post here with the specific details of the problem.

BizTalk Orch/SMTP - Microsoft.XLANGs.BaseType.Content must be a message property of

This is related to my question from 2017: How Set Attachment Name to Show Properly in Outlook
This time, I have an orchestration with the following in a construct shape:
attachmentName = System.IO.Path.GetFileName(
msg_Ledger6002_File_XmlDoc
(FILE.ReceivedFileName));
msg_Email.BodyPart = new ABC.Ledger6002.Component.RawString("See attached email.");
msg_Email.AttachmentPart = msg_Ledger6002_File_XmlDoc;
// attachmentName is set earlier in orch so we could write it to EventLog for debugging
msg_Email.AttachmentPart(MIME.FileName) = attachmentName;
//msg_Email.AttachmentPart(MIME.ContentDescription) = "body";
//msg_Email.AttachmentPart(MIME.ContentTransferEncoding) = "base64";
// These are working
msg_Email(SMTP.Subject) = "Ledger6002 File";
msg_Email(SMTP.SMTPTo) = msg_Config_Email.smtpToEmail;
msg_Email(SMTP.From) = msg_Config_Email.smtpFromEmail;
msg_Email(SMTP.SMTPAuthenticate) = 0; // do not authenticate to SMTP server
// trying these two new parms below
msg_Email(SMTP.EmailBodyTextCharset) = "UTF-8";
msg_Email(Microsoft.XLANGs.BaseTypes.ContentType) = "text/plain";
The last line above gives me an expression shape not valid, and when I mouse over it it says identifies the error as:
Microsoft.XLANGs.BaseType.Content must be a message property of
msg_email.
Before I added the two lines under the comment "trying these two new parms below", I am getting an email with the desired attachment. The problem is that it is just called "body" and when I do "save as" from Outlook, it wants to call it "body" instead of the name of the file I dropped.
I'm using a "specify later" statically configured SendPort with PassThru for the Pipeline. I have tried a pipeline with MimeEncoder, but that caused the attachment to appear in the body. I would like to move to a dynamic pipeline, but so far I've got the static one working except for the name assigned to the file attachment.
To fix the error: "Microsoft.XLANGs.BaseType.Content must be a message property of msg_email.", I just needed to include the part name (the message is associated with a multipart message type, and I didn't include the partname):
Wrong:
msg_Email(Microsoft.XLANGs.BaseTypes.ContentType) = "text/plain";
Right:
msg_Email.BodyPart (Microsoft.XLANGs.BaseTypes.ContentType) = "text/plain";
I'm still working on getting the email with attachment to come out correctly, but so far this post https://social.msdn.microsoft.com/Forums/en-US/988b0d91-1e5a-4f73-b30d-417d6ea9fa75/attachment-name-in-outlook-is-ok-see-on-exchange-always-named-body?forum=biztalkgeneral seems to be the best explanation.

Apache Tika - Parsing and extracting only metadata without reading content

Is there a way to configure the Apache Tikka so that it only extracts the metadata properties from the file and does not access the content of the file. ? We need a way to do this so as to avoid reading the entire content in larger files.
The code to extract we are using is as follows:
var tikaConfig = TikaConfig.getDefaultConfig();
var metadata = new Metadata();
AutoDetectParser parser = new AutoDetectParser(tikaConfig);
BodyContentHandler handler = new BodyContentHandler();
using (TikaInputStream stream = TikaInputStream.get(new File(filename), metadata))
{
parser.parse(stream, handler, metadata, new ParseContext());
Array metadataKeys = metadata.names();
Array.Sort(metadataKeys);
}
With the above code sample, when we try to extract the metadata even the content is being read. We would need a way to avoid the same.

How to edit pasted content using the Open XML SDK

I have a custom template in which I'd like to control (as best I can) the types of content that can exist in a document. To that end, I disable controls, and I also intercept pastes to remove some of those content types, e.g. charts. I am aware that this content can also be drag-and-dropped, so I also check for it later, but I'd prefer to stop or warn the user as soon as possible.
I have tried a few strategies:
RTF manipulation
Open XML manipulation
RTF manipulation is so far working fairly well, but I'd really prefer to use Open XML as I expect it to be more useful in the future. I just can't get it working.
Open XML Manipulation
The wonderfully-undocumented (as far as I can tell) "Embed Source" appears to contain a compound document object, which I can use to modify the copied content using the Open XML SDK. But I have been unable to put the modified content back into an object that lets it be pasted correctly.
The modification part seems to work fine. I can see, if I save the modified content to a temporary .docx file, that the changes are being made correctly. It's the return to the clipboard that seems to be giving me trouble.
I have tried assigning just the Embed Source object back to the clipboard (so that the other types such as RTF get wiped out), and in this case nothing at all gets pasted. I've also tried re-assigning the Embed Source object back to the clipboard's data object, so that the remaining data types are still there (but with mismatched content, probably), which results in an empty embedded document getting pasted.
Here's a sample of what I'm doing with Open XML:
using OpenMcdf;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
...
object dataObj = Forms.Clipboard.GetDataObject();
object embedSrcObj = dateObj.GetData("Embed Source");
if (embedSrcObj is Stream)
{
// read it with OpenMCDF
Stream stream = embedSrcObj as Stream;
CompoundFile cf = new CompoundFile(stream);
CFStream cfs = cf.RootStorage.GetStream("package");
byte[] bytes = cfs.GetData();
string savedDoc = Path.GetTempFileName() + ".docx";
File.WriteAllBytes(savedDoc, bytes);
// And then use the OpenXML SDK to read/edit the document:
using (WordprocessingDocument openDoc = WordprocessingDocument.Open(savedDoc, true))
{
OpenXmlElement body = openDoc.MainDocumentPart.RootElement.ChildElements[0];
foreach (OpenXmlElement ele in body.ChildElements)
{
if (ele is Paragraph)
{
Paragraph para = (Paragraph)ele;
if (para.ParagraphProperties != null && para.ParagraphProperties.ParagraphStyleId != null)
{
string styleName = para.ParagraphProperties.ParagraphStyleId.Val;
Run run = para.LastChild as Run; // I know I'm assuming things here but it's sufficient for a test case
run.RunProperties = new RunProperties();
run.RunProperties.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text("test"));
}
}
// etc.
}
openDoc.MainDocumentPart.Document.Save(); // I think this is redundant in later versions than what I'm using
}
// repackage the document
bytes = File.ReadAllBytes(savedDoc);
cf.RootStorage.Delete("Package");
cfs = cf.RootStorage.AddStream("Package");
cfs.Append(bytes);
MemoryStream ms = new MemoryStream();
cf.Save(ms);
ms.Position = 0;
dataObj.SetData("Embed Source", ms);
// or,
// Clipboard.SetData("Embed Source", ms);
}
Question
What am I doing wrong? Is this just a bad/unworkable approach?

Enterprise Library Fluent API and Rolling Log Files Not Rolling

I am using the Fluent API to handle various configuration options for Logging using EntLib.
I am building up the loggingConfiguration section manually in code. It seems to work great except that the RollingFlatFileTraceListener doesn't actually Roll the file. It will respect the size limit and cap the amount of data it writes to the file appropriately, but it doesn't not actually create a new file and continue the logs.
I've tested it with a sample app and the app.config and it seems to work. So I'm guess that I am missing something although every config option that seems like it needs is there.
Here is the basics of the code (with hard-coded values to show a config that doesn't seem to be working):
//Create the config builder for the Fluent API
var configBuilder = new ConfigurationSourceBuilder();
//Start building the logging config section
var logginConfigurationSection = new LoggingSettings("loggingConfiguration", true, "General");
logginConfigurationSection.RevertImpersonation = false;
var _rollingFileListener = new RollingFlatFileTraceListenerData("Rolling Flat File Trace Listener", "C:\\tracelog.log", "----------------------", "",
10, "MM/dd/yyyy", RollFileExistsBehavior.Increment,
RollInterval.Day, TraceOptions.None,
"Text Formatter", SourceLevels.All);
_rollingFileListener.MaxArchivedFiles = 2;
//Add trace listener to current config
logginConfigurationSection.TraceListeners.Add(_rollingFileListener);
//Configure the category source section of config for flat file
var _rollingFileCategorySource = new TraceSourceData("General", SourceLevels.All);
//Must be named exactly the same as the flat file trace listener above.
_rollingFileCategorySource.TraceListeners.Add(new TraceListenerReferenceData("Rolling Flat File Trace Listener"));
//Add category source information to current config
logginConfigurationSection.TraceSources.Add(_rollingFileCategorySource);
//Add the loggingConfiguration section to the config.
configBuilder.AddSection("loggingConfiguration", logginConfigurationSection);
//Required code to update the EntLib Configuration with settings set above.
var configSource = new DictionaryConfigurationSource();
configBuilder.UpdateConfigurationWithReplace(configSource);
//Set the Enterprise Library Container for the inner workings of EntLib to use when logging
EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
Any help would be appreciated!
Your timestamp pattern is wrong. It should be yyy-mm-dd instead of MM/dd/yyyy. The ‘/’ character is not supported.
Also, you could accomplish your objective by using the fluent configuration interface much easier. Here's how:
ConfigurationSourceBuilder formatBuilder = new ConfigurationSourceBuilder();
ConfigurationSourceBuilder builder = new ConfigurationSourceBuilder();
builder.ConfigureLogging().LogToCategoryNamed("General").
SendTo.
RollingFile("Rolling Flat File Trace Listener")
.CleanUpArchivedFilesWhenMoreThan(2).WhenRollFileExists(RollFileExistsBehavior.Increment)
.WithTraceOptions(TraceOptions.None)
.RollEvery(RollInterval.Minute)
.RollAfterSize(10)
.UseTimeStampPattern("yyyy-MM-dd")
.ToFile("C:\\logs\\Trace.log")
.FormatWith(new FormatterBuilder().TextFormatterNamed("textFormatter"));
var configSource = new DictionaryConfigurationSource();
builder.UpdateConfigurationWithReplace(configSource);
EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
var writer = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
DateTime stopWritingTime = DateTime.Now.AddMinutes(10);
while (DateTime.Now < stopWritingTime)
{
writer.Write("test", "General");
}