› Clovertech Forums › Read Only Archives › Cloverleaf › Tcl Library › Request for assistance with a FOREACH problem.
Hoping someone can provide me with the correct information to fix a issue I
Lawrence Nelson
System Architect - MaineHealth IT
Is your second foreach supposed to be nested inside of your first one?
-- Max Drown (Infor)
If you have a sample TEST message, could you please post it?
-- Max Drown (Infor)
I would say yes – there is a series of ORC’s each has an OBR
So –
go into the ORC do each OBR under it
> then move to the next ORC and do the same until the last OBR is reviewed under the last ORC
Lawrence Nelson
System Architect - MaineHealth IT
MSH|^~&|LinkLogic-0058|0058002^GOOD HEALTHTGCH|NorDx|GCH|20160714110123||ORM^O01|1784113281899770|P|2.3.1|||NE|NE
PID|1|650557|259374-0058002||S||||||||||||||||||||||||||||||||||||||
PV1|1|O|^^^GOOD HEALTHTGCH||||1477693182
ORC|NW|2516511-5|||||||20160714|||1477693182
OBR|1|2516511-5||CMPNX^Comp Metabolic Panel (NorDx)|||20160714||1||N|||||1477693182|||||||||||^^^^^R
NTE|1||Provider[] Lab[X] Collect
DG1|1||Z78.9^HEALTH MAINTENANCE, ROUTINE^I10^V70.0^HEALTH MAINTENANCE, ROUTINE^I9
ORC|NW|2516511-6|||||||20160714|||1477693182
OBR|1|2516511-6||CBCD^CBC with Diff (NorDx)|||20160714||1||N|||||1477693182|||||||||||^^^^^R
NTE|1||Provider[] Lab[X] Collect
DG1|1||Z78.9^HEALTH MAINTENANCE, ROUTINE^I10^V70.0^HEALTH MAINTENANCE, ROUTINE^I9
ORC|NW|2516511-7|||||||20160714|||1477693182
OBR|1|2516511-7||LPPR1^Lipid Profile (NorDx)|||20160714||1||N|||||1477693182|||||||||||^^^^^R
NTE|1||Provider[] Lab[X] Collect ~~Please draw regardless of fasting status
DG1|1||Z78.9^HEALTH MAINTENANCE, ROUTINE^I10^V70.0^HEALTH MAINTENANCE, ROUTINE^I9
ORC|NW|2516511-8|||||||20160714|||1477693182
OBR|1|2516511-8||TSH^TSH (NorDx)|||20160714||1||N|||||1477693182|||||||||||^^^^^R
NTE|1||Provider[] Lab[X] Collect
DG1|1||Z78.9^HEALTH MAINTENANCE, ROUTINE^I10^V70.0^HEALTH MAINTENANCE, ROUTINE^I9
Lawrence Nelson
System Architect - MaineHealth IT
This would be much easier in an XLATE which is designed to handle looping across messages. Just saying.
-- Max Drown (Infor)
I work at a lab. We have a significant problem with external doctors EMRs submitting duplicate tests on the order choice level – while at the same time they are sending in new messages with the duplicates orders. We are basically taking it out of the provider and EMR’s hands since they have never been able to resolve the problem.
Cloverleaf has no ‘memory’ on what’s been sent. I’m making one (memory bank of sorts) with a string of data that I’m sending out to an external database table- with more key elements in it than is described with the script I’ve included.
Lawrence Nelson
System Architect - MaineHealth IT
In order to parse through that message, you’ll need to add a bit more code. You’ll probably want to loop across all the segments and use flags to determine where you are in the message (ex. in an OBR segment loop).
Here’s your code cleaned up a bit for testing.
set fh [open test_main.hl7 r]; fconfigure $fh -translation binary; set msg [read $fh]; close $fh
[code]set fh [open test_main.hl7 r]; fconfigure $fh -translation binary; set msg [read $fh]; close $fh
-- Max Drown (Infor)
I’m not exactly clear on what your code needs to do based off of the use case you described above.
Basically every single OBR segment is written to a database and if the database call from the TCL script says it’s a duplicate than that OBR is stricken from the message and reported via email. (again none of this is in what I’ve included). This not something that an XLATE can readily do (my opinion) so I’ve writing this script to put on as a TPS Inbound Data pre-proc to a whole process.
Do you need to extract all ORC and all OBR segments?
-- Max Drown (Infor)
I need to report all the elements to the table – in the full script OBRs if found to be duplicated are removed.
Lawrence Nelson
System Architect - MaineHealth IT
In your code, this line gets the same set of OBR segments each time, regardless of the parent loop.
set OBRpos [lsearch -all $segList OBR*]
-- Max Drown (Infor)
Can you safely use ORC.02 and OBR.02 to relate the ORC segments to the OBR segments?
-- Max Drown (Infor)
This seems to work.
set fh [open test_main.hl7 r]; fconfigure $fh -translation binary; set msg [read $fh]; close $fh
[code]set fh [open test_main.hl7 r]; fconfigure $fh -translation binary; set msg [read $fh]; close $fh
-- Max Drown (Infor)
Well that seems to work – but some how it seems like a cheat 🙂
When I do the iterates on the xlates for these same messages its not a problem – it really seems like the the message should be able to cascade through the message and report out each OBR as it goes along – and not repeat without have to do what your solution is. NOT THAT I’M NOT GRATEFUL!!
Lawrence Nelson
System Architect - MaineHealth IT
The tcl scripting language is not HL7-aware. It’s just looking at strings.
In your case you’re lucky there’s an easy way to create a relationship, or else you’d need a lot more code.
I would do this in an xlate and use tcl fragments in the xlate as needed like to write to a database or whatever.
-- Max Drown (Infor)
For you or anyone else using lsearch to extract segments
Do not use it like: set ORCpos [lsearch -all $segList ORC*]
Lsearch, by default use glob matching. That means if the phrase ORC is anywhere in your data you will not get what you expect. What you want to do is tell lsearch you want every element that starts with ORC
set ORCpos [lsearch -all -regexp $segList {^ORC}]
Also a minor point. You do not need to put your keylget commands inside a catch statement. A command like keylget args CONTEXT ctx will return 1 if success or 0 if not. FWIW, if you ever get an error with a command like ‘keylget args CONTEXT” you better get yourself a new engine since this one is really bad 😀
I will leave it as an exercise for Max to correct the logic just wanted to insert my $0.02 worth
Hi Charlie – Thanks for the response –
Question for you on your comment
Yours
# set OBR_pos2 [lsearch -all -regexp $segList2 {^OBR}]
>> Your version is also reporting out NTE’s that follow the OBR – why is that
Mine
set OBR_pos2 [lsearch -regexp $segList2 {^OBR}]
does not
This section of code later in my script was having the same sorts of issues –
extracted area –
# Handle 1 (duplicate)
if {$rc == 1} {
set msec [clock click -microseconds]
echo “ORMCONTENT is a duplicate”
set msg [msgget $mh] ;# Get a copy of the message
set segList2 [split $msg r] ;# split msg by carriage return to get the second segment list
set fieldSep2 [string index $msg 3] ;# determine field separator should be pipe
set OBR_pos2 [lsearch -regexp $segList2 {^OBR}] ;# just the OBR’s
# set OBR_pos2 [lsearch -all -regexp $segList2 {^OBR}] ;# OBR break down
if {$OBR_id2 ne $ORC_id2} {continue}
foreach OBR_idx2 $OBR_pos2 {
set OBR_Seg2 [lindex $segList2 $OBR_idx2]
set segList2 [lreplace $segList2 $OBR_idx2 $OBR_idx2 ]
set newMsg [join $segList2 r]
msgset $mh $newMsg
if { $OBR_Seg2 != “”} {
echo *new message below
echo “$msg”
set emailto nelsol1@mmc.org
set mynote1 “From thread [msgmetaget $mh ORIGSOURCECONN]r”
set mynote2 “[msgmetaget $mh DRIVERCTL]r”
set mynote3 “$module r”
set mynote4 “Message sent to the Error Database:r”
set mynote5 >$msg< ;# was msg before newMsg
catch {exec echo $mynote1$mynote2$mynote3$mynote4$mynote5nnRemoved: $OBR_Seg2 nn $ORMCONTENT | mail -s “CONFMSG FULL duplicate segment removed $MSH_id9 $OBR_id2:$OBR_id3:$OBR_id4 $rc” $emailto}
}
} ;# end foreach
} ;# end if rc indicates a duplicate
Lawrence Nelson
System Architect - MaineHealth IT
set OBR_pos2 [lsearch -all -regexp $segList2 {^OBR}]
Should return all locations of OBR segments within the list. If not it is not a proper list
Take a look at your lreplace command. It should be:
lreplace list first last ?element element …?
I did not take the time to evaluate the whole script. I will let others chime in for that
Charlie is certainly right about using lsearch with the -regexp option to search for ORC/OBR only at the beginning of each list element.
Max and Charlie are also correct when stating that “eq” and “ne” should be used for string comparisons. Unexpected things happen if you use “==” and “!=”. I learned this lesson the hard (and confusing) way.
So, to the original question.
The basic problem, as Max observed, is that the code is looping through all OBR segments for each ORC segment – thus duplicating results.
Max’s solution checks that the ORC and OBR segments match – based on the placer order numbers in ORC-2.2 and OBR-2.2.
If those are reliable, that’s probably the most certain way to match ORC to OBR.
If they’re not reliable, or if one order number may be listed in multiple ORC or OBR segments, you can find OBR segments by position relative to the a given ORC by removing the OBR foreach and replacing it with processing for the “next” OBR after the current ORC – something like this:
set ORCpos [lsearch -all -regexp $segList “^ORC”] ;# ORC break down
foreach ORCidx $ORCpos {
set ORCSeg [lindex $segList $ORCidx]
set ORCFlds [split $ORCSeg $fieldSep]
set ORC_id1 [lindex $ORCFlds 1]
set ORC_id2 [lindex $ORCFlds 2]
set ORC_id3 [lindex $ORCFlds 3]
set ORC_idSeg1 [split $ORC_id1 $compSep]
set ORC_idSeg2 [split $ORC_id2 $compSep]
set ORC_idSeg3 [split $ORC_id3 $compSep]
set ORC_id1a [lindex $ORC_idSeg1 0]
set ORC_id2a [lindex $ORC_idSeg2 0]
set ORC_id3a [lindex $ORC_idSeg3 0]
#OBR
#
# The problem was that the original code was looping through all OBR segments FOREACH ORC segment
# What we really want is to find the NEXT OBR after each ORC
#
# So, we lsearch for the next OBR starting at the current ORCidx
# position – using the -start option for lsearch
#
if { [set OBRidx [lsearch -regexp -start $ORCidx $segList “^OBR”]] >= 0 } {
# Process only one OBR per ORC
set OBRSeg [lindex $segList $OBRidx]
set OBRFlds [split $OBRSeg $fieldSep]
set OBR_id2 [lindex $OBRFlds 2]
set OBR_id3 [lindex $OBRFlds 3]
set OBR_id4 [lindex $OBRFlds 4]
set OBR_idSeg2 [split $OBR_id2 $compSep]
set OBR_idSeg3 [split $OBR_id3 $compSep]
set OBR_idSeg4 [split $OBR_id4 $compSep]
set OBR_id2a [lindex $OBR_idSeg2 0]
set OBR_id3a [lindex $OBR_idSeg3 0]
set OBR_id4a [lindex $OBR_idSeg4 0]
set ORMCONTENT {}
set ORMCONTENT “$ORC_id1a:$ORC_id2a:$ORC_id3a:$OBR_id2a:$OBR_id3a:$OBR_id4a”
echo ORMCONTENT = $ORMCONTENT
}
}
This produces your desired result :
ORMCONTENT = NW:2516511-5::2516511-5::CMPNX
ORMCONTENT = NW:2516511-6::2516511-6::CBCD
ORMCONTENT = NW:2516511-7::2516511-7::LPPR1
ORMCONTENT = NW:2516511-8::2516511-8::TSH
Jeff Dinsmore
Chesapeake Regional Healthcare
Hi,
I am assuming that you need to extract the list of OBR segments for each ORC segment in your report.
since you are searching the segment list in general, it finds all OBR segments without regard to which ORC they belong to.
the search for the OBR segment should be limited to the OBR’s preceding the next ORC. so if you search for the next OBR one at a time (until you get to the next ORC), rather than using the -all parameter, it should give you the list you need.
To Jeff Dinsmore – and all others –
I have implemented Max- Charlie then Jeff’s suggestions successfully.
Thank you all for your time and input and expertise.
Lawrence Nelson
System Architect - MaineHealth IT
Here’s another way to do it:
set fieldSep [string index $msg 3]
set compSep [string index $msg 4]
set segList [split $msg r]
foreach seg [lsearch -inline -all -regexp $segList {^(ORC|OBR)}] {
set fieldList [split $seg $fieldSep]
set segName [lindex $fieldList 0]
foreach fieldNum { 1 2 3 4 } {
set field [lindex $fieldList $fieldNum]
set compList [split $field $compSep]
set ${segName}_id${fieldNum}a [lindex $compList 0]
}
if { $segName eq “OBR” } {
set ORMCONTENT “$ORC_id1a:$ORC_id2a:$ORC_id3a:$OBR_id2a:$OBR_id3a:$OBR_id4a”
puts “ORMCONTENT = $ORMCONTENT”
}
}
The main advantage is you only do one lsearch, and anytime you’re processing an OBR segment you have the values left over from the last ORC that you processed.