web stats
useful tool? - Mirth Community

Go Back   Mirth Community > Mirth Connect > Development

Reply
 
Thread Tools Display Modes
  #1  
Old 05-25-2010, 10:10 AM
csmith csmith is offline
OBX.2 Kenobi
 
Join Date: Mar 2009
Posts: 57
csmith is an unknown quantity at this point
Default useful tool?

Hi,

Oftentimes, I have a lot of complicated channels deployed from the same Mirth installation and I loose track of what's doing what. I was thinking it would be nice to have a script that would generate a directed graph indicating which channels fed into which.

So, I decided to write one this afternoon and here it is:

Code:
#!/usr/bin/perl
use Graph::Easy;
use XML::Smart;

#open the directory specified in the parameter and read the files

opendir(DIR, $ARGV[0]);
@files = readdir DIR;
foreach $file (@files){
  $path = $ARGV[0]."/".$file;
  if ($file ne "." && $file ne ".."){
    push(@channels, XML::Smart->new($path));
  }
}

#loop through the channels and make a hash where the keys are channel id's and the values are channel names

foreach $channel (@channels){
    $channel = $channel->cut_root;
    $idNameHash{$channel->{id}} = $channel->{name};
}

#loop again, but this time make an array of destination, name pairings

foreach $channel (@channels){
    $i = 0;
    while ($channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]){
      @properties = @{$channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]{properties}{property}};
      if ($properties[3] ne "sink"){
        @edge = [$channel->{name}, $idNameHash{$properties[3]}];
        push(@edges, @edge);
    }
    $i++;
   }
}

#generate the directed graph

my $graph = Graph::Easy->new();
$i=0;
while($edges[$i]){
    $first = sprintf("%s",$edges[$i]->[0]);
    $second = sprintf("%s",$edges[$i]->[1]);
    $graph->add_edge($first, $second);
    $i++;
}
print $graph->as_ascii( );
As you can see, it is written in PERL and takes as a parameter the directory in which you've exported all the channels in your MirthConnect installation. To use it, you'll need to install the Graph::Easy and XML::Smart modules.

If you run the script against the attached collection of test Mirth channels, you'll see the following graph rendered in ASCII:

Code:
          +---+
          | E |
          +---+
            ^
            |
            |
+---+     +---+     +---+
| A | --> | B | --> | C |
+---+     +---+     +---+
            |
            |
            v
          +---+
          | D |
          +---+
This shows that the channel named 'A' feeds into 'B' which then feeds into 'C', 'D' and 'E'.

The script doesn't recognize the situation when a channel feeds another by virtue of the javascript call 'router.routeMessage', but it could be made to do so. Also, if you want a webpage as output instead, try $graph->as_html_page() instead of $graph->as_ascii().

If people think this is useful, maybe something like it could be made part of admin interface someday.

Thanks and Happy Mirthing!
Attached Files
File Type: xml A.xml (4.1 KB, 46 views)
File Type: xml B.xml (5.5 KB, 9 views)
File Type: xml C.xml (4.1 KB, 7 views)
File Type: xml D.xml (4.1 KB, 7 views)
File Type: xml E.xml (4.1 KB, 7 views)

Last edited by csmith; 05-25-2010 at 10:24 AM. Reason: clarification
Reply With Quote
  #2  
Old 05-25-2010, 11:05 AM
jbartels jbartels is offline
Mirth Guru
 
Join Date: Oct 2006
Posts: 727
jbartels is on a distinguished road
Default

Nice work!
__________________
Jon Bartels

Zen is hiring!!!!
http://consultzen.com/careers/
Talented healthcare IT professionals wanted. Engineers to sales to management.
Good benefits, great working environment, genuinely interesting work.
Reply With Quote
  #3  
Old 05-25-2010, 02:02 PM
jacobb jacobb is offline
Mirth Employee
 
Join Date: Aug 2006
Location: Irvine, CA
Posts: 1,218
jacobb is an unknown quantity at this point
Default

Wow, nice work indeed!
__________________
Jacob Brauer
Director, Software Development
NextGen Healthcare

Reply With Quote
  #4  
Old 05-26-2010, 09:56 AM
csmith csmith is offline
OBX.2 Kenobi
 
Join Date: Mar 2009
Posts: 57
csmith is an unknown quantity at this point
Default

Hi,

Thanks for the interest!

I've modified the code to find destination channels specified by calls to 'router.routeMessage' in javascript source transformers. See below:

Code:
#!/usr/bin/perl
use Graph::Easy;
use XML::Smart;

#open the directory specified in the parameter and read the files

opendir(DIR, $ARGV[0]);
@files = readdir DIR;
foreach $file (@files){
  $path = $ARGV[0]."/".$file;
  if ($file ne "." && $file ne ".."){
    push(@channels, XML::Smart->new($path));
  }
}

#loop through the channels and make a hash where the keys are channel id's and the values are channel names

foreach $channel (@channels){
    $channel = $channel->cut_root;
    $idNameHash{$channel->{id}} = $channel->{name};
}

#loop again, but this time make an array of destination, name pairings

foreach $channel (@channels){
    #loop for destination connectors first
    $i = 0;
   while ($channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]){
      @properties = @{$channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]{properties}{property}};
      if ($idNameHash{$properties[3]}){
        @edge = [$channel->{name}, $idNameHash{$properties[3]}];
        push(@edges, @edge);
    }
    $i++;
   }
   #now, loop for destinations specified in javascript source transformers
   $i = 0;
    while ($codeText = $channel->{sourceConnector}{transformer}{steps}[$i]{'com.webreach.mirth.model.Step'}{script}){
      while ($codeText =~ /(routeMessage\(\'.+\')/g ){
        $processed = $1;
        $processed =~ s/routeMessage\(//;
        $processed =~ s/'//g;
        @edge = [$channel->{name}, $processed];
        push(@edges, @edge);
      }
    $i++;
   }
}

#generate the directed graph

my $graph = Graph::Easy->new();
$i=0;
while($edges[$i]){
    $first = sprintf("%s",$edges[$i]->[0]);
    $second = sprintf("%s",$edges[$i]->[1]);
    $graph->add_edge_once($first, $second);
    $i++;
}
print $graph->as_ascii( );
I've also attached an amended collection of test channels that exercises this new feature. When run against this new collection, the script issues the following:

Code:
  +--------------+
  |              |
  |              |
  |    +---------+---------+
  |    |         |         v
  |  +---+     +---+     +---+
  |  | A | --> | B | --> | C |
  |  +---+     +---+     +---+
  |    |         |
  |    |         |
  |    v         v
  |  +---+     +---+
  +> | E |     | D |
     +---+     +---+
The feeds A->E and A->C are specified in A's javascript source transformer.

Note that the script doesn't output channels that don't feed into other channels. It could be modified to do so, however.
Attached Files
File Type: xml A.xml (4.9 KB, 9 views)
File Type: xml C.xml (4.1 KB, 1 views)
File Type: xml D.xml (4.1 KB, 1 views)
File Type: xml E.xml (4.1 KB, 1 views)
File Type: xml B.xml (5.5 KB, 1 views)
Reply With Quote
  #5  
Old 05-26-2010, 02:16 PM
jacobb jacobb is offline
Mirth Employee
 
Join Date: Aug 2006
Location: Irvine, CA
Posts: 1,218
jacobb is an unknown quantity at this point
Default

Even better.

Now if you could only do it using a Java charting/graphing tool by looking through the Channel model...then we could include it in Mirth Connect.
__________________
Jacob Brauer
Director, Software Development
NextGen Healthcare

Reply With Quote
  #6  
Old 05-27-2010, 06:03 AM
csmith csmith is offline
OBX.2 Kenobi
 
Join Date: Mar 2009
Posts: 57
csmith is an unknown quantity at this point
Default

Hi,

You mean write a plug-in? Like one that would reside here, http://www.mirthcorp.com/community/f...onnect/plugins , in the source?

I've modified the script slightly so that now it outputs channels that are 'orphaned', i.e., those that don't explicitly feed into (or out of) any of the others in the installation.

Code:
#!/usr/bin/perl
use Graph::Easy;
use XML::Smart;

#open the directory specified in the parameter and read the files

opendir(DIR, $ARGV[0]);
@files = readdir DIR;
foreach $file (@files){
  $path = $ARGV[0]."/".$file;
  if ($file ne "." && $file ne ".."){
    push(@channels, XML::Smart->new($path));
  }
}

#loop through the channels and make a hash where the keys are channel id's and the values are channel names

foreach $channel (@channels){
    $channel = $channel->cut_root;
    $idNameHash{$channel->{id}} = $channel->{name};
    $nameUsageHash{$channel->{name}} = 0;
}

#loop again, but this time make an array of destination, name pairings

foreach $channel (@channels){
    #loop for destination connectors first
    $i = 0;
   while ($channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]){
      @properties = @{$channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]{properties}{property}};
      if ($idNameHash{$properties[3]}){
        @edge = [$channel->{name}, $idNameHash{$properties[3]}];
        push(@edges, @edge);
    }
    $i++;
   }
   #now, loop for destinations specified in javascript source transformers
   $i = 0;
    while ($codeText = $channel->{sourceConnector}{transformer}{steps}[$i]{'com.webreach.mirth.model.Step'}{script}){
      while ($codeText =~ /(routeMessage\(\'.+\')/g ){
        $processed = $1;
        $processed =~ s/routeMessage\(//;
        $processed =~ s/'//g;
        @edge = [$channel->{name}, $processed];
        push(@edges, @edge);
      }
    $i++;
   }
}
#generate the directed graph

my $graph = Graph::Easy->new();
$i=0;
while($edges[$i]){
    $first = sprintf("%s",$edges[$i]->[0]);
    $second = sprintf("%s",$edges[$i]->[1]);
    $counter = $nameUsageHash{$first};
    $nameUsageHash{$first} = $counter + 1;
    $counter = $nameUsageHash{$second};
    $nameUsageHash{$second} = $counter + 1;
    $graph->add_edge_once($first, $second);
    $i++;
}

#output orphaned channels as unconnected nodes
while (($key, $value) = each (%nameUsageHash)){
  if ($value == 0){
    $graph->add_node($key);
  }
}
print $graph->as_ascii( );
I've modified the test collection so that channel 'B' no longer feeds into 'D'. The output:
Code:
  +-------------------+
  |                   v
+---+     +---+     +---+
| A | --> | B | --> | C |
+---+     +---+     +---+
  |         |
  |         |
  v         |
+---+       |
| E | <-----+
+---+
+---+
| D |
+---+
As you can see, channel 'D' is now drawn but stands alone as an orphaned node.

Thanks
Attached Files
File Type: xml A.xml (4.9 KB, 4 views)
File Type: xml B.xml (4.8 KB, 2 views)
File Type: xml C.xml (4.1 KB, 2 views)
File Type: xml D.xml (4.1 KB, 2 views)
File Type: xml E.xml (4.1 KB, 2 views)
Reply With Quote
  #7  
Old 05-27-2010, 10:21 AM
jacobb jacobb is offline
Mirth Employee
 
Join Date: Aug 2006
Location: Irvine, CA
Posts: 1,218
jacobb is an unknown quantity at this point
Default

Yeah, some sort of plugin would be great. It might just be in the core client, though, because it would be nice to really integrate it into the channel viewer. It would also help if it graphed out transformers and destinations so that you could double click those and jump directly to them in the channel. It's definitely something we've wanted to have as an option for a long time.
__________________
Jacob Brauer
Director, Software Development
NextGen Healthcare

Reply With Quote
  #8  
Old 07-13-2010, 10:35 AM
deepa deepa is offline
OBX.1 Kenobi
 
Join Date: Feb 2010
Posts: 39
deepa is on a distinguished road
Default

this is awesome!
Reply With Quote
  #9  
Old 07-15-2010, 06:58 AM
TMarz TMarz is offline
OBX.2 Kenobi
 
Join Date: Jul 2009
Posts: 60
TMarz is on a distinguished road
Default

I agree. This looks great. I was in the process of testing it myself and am having trouble exporting my channels from the mirth shell. I can export one at a time, but I can not seem to export all.

I am using:
export * "/tmp/channels"

but it throws an Invocation error and kicks me out of the shell. I know I can export all through the GUI but I would then need to put all of those into a file on the Linux box the shell is running on. Any ideas?
Reply With Quote
  #10  
Old 07-15-2010, 09:04 AM
csmith csmith is offline
OBX.2 Kenobi
 
Join Date: Mar 2009
Posts: 57
csmith is an unknown quantity at this point
Default

Hi,

Thanks for your interest!

This version picks up destinations specified in javascript destination writers:

Code:
#!/usr/bin/perl
use Graph::Easy;
use XML::Smart;

#open the directory specified in the parameter and read the files

opendir(DIR, $ARGV[0]);
@files = readdir DIR;
foreach $file (@files){
  $path = $ARGV[0]."/".$file;
  if ($file ne "." && $file ne ".."){
    push(@channels, XML::Smart->new($path));
  }
}

#loop through the channels and make a hash where the keys are channel id's and the values are channel names

foreach $channel (@channels){
    $channel = $channel->cut_root;
    $idNameHash{$channel->{id}} = $channel->{name};
    $nameUsageHash{$channel->{name}} = 0;
}

#loop again, but this time make an array of destination, name pairings

foreach $channel (@channels){
    #loop for destination connectors first
    $i = 0;
   while ($channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]){
      @properties = @{$channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]{properties}{property}};
      if ($idNameHash{$properties[3]}){
        @edge = [$channel->{name}, $idNameHash{$properties[3]}];
        push(@edges, @edge);
    }
    $i++;
   }
   #now, loop for destinations specified in javascript source transformers
   $i = 0;
    while ($codeText = $channel->{sourceConnector}{transformer}{steps}[$i]{'com.webreach.mirth.model.Step'}{script}){
      while ($codeText =~ /(routeMessage\(\'.+\')/g ){
        $processed = $1;
        $processed =~ s/routeMessage\(//;
        $processed =~ s/'//g;
        @edge = [$channel->{name}, $processed];
        push(@edges, @edge);
      }
    $i++;
   }

   #now, loop for destinations specified in javascript destination writers 
   $i = 0;
    while ($codeText = $channel->{destinationConnectors}{'com.webreach.mirth.model.Connector'}[$i]{properties}{property}){
      while ($codeText =~ /(routeMessage\(\'.+\')/g ){
        $processed = $1;
        $processed =~ s/routeMessage\(//;
        $processed =~ s/'//g;
        @edge = [$channel->{name}, $processed];
        push(@edges, @edge);
      }
    $i++;
   }

}
#generate the directed graph

my $graph = Graph::Easy->new();
$i=0;
while($edges[$i]){
    $first = sprintf("%s",$edges[$i]->[0]);
    $second = sprintf("%s",$edges[$i]->[1]);
    $counter = $nameUsageHash{$first};
    $nameUsageHash{$first} = $counter + 1;
    $counter = $nameUsageHash{$second};
    $nameUsageHash{$second} = $counter + 1;
    $graph->add_edge_once($first, $second);
    $i++;
}

#output orphaned channels as unconnected nodes
while (($key, $value) = each (%nameUsageHash)){
  if ($value == 0){
    $graph->add_node($key);
  }
}
print $graph->as_ascii( );
When I get some time, I hope to write a plug-in to make this an integrated feature of MirthConnect.
It looks like it would be a tough job though.

Thanks again!
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -8. The time now is 12:24 AM.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Mirth Corporation