Announcement

Collapse
No announcement yet.

useful tool?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    Last edited by csmith; 05-25-2010, 09:24 AM. Reason: clarification

  • #2
    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.

    Comment


    • #3
      Wow, nice work indeed!
      Jacob Brauer
      Director, Software Development
      NextGen Healthcare

      sigpic

      Comment


      • #4
        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

        Comment


        • #5
          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

          sigpic

          Comment


          • #6
            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

            Comment


            • #7
              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

              sigpic

              Comment


              • #8
                this is awesome!

                Comment


                • #9
                  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?

                  Comment


                  • #10
                    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!

                    Comment


                    • #11
                      Originally posted by TMarz View Post
                      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?

                      Ok, just an FYI. The export * command for the shell is broken until 2.0 so if your using this handy script you will need to export each channel individually or else find a way to extract the channels from wherever they are stored native.

                      Comment


                      • #12
                        Hi,

                        I found a 'bug' (the script wasn't taking into account multiple steps in the source transformers) and I squashed it. Here's the latest version:

                        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 ($channel->{sourceConnector}{transformer}{steps}[$i]){
                              $j = 0;
                              while($codeText = $channel->{sourceConnector}{transformer}{steps}[$i]{'com.webreach.mirth.model.Step'}[$j]{script}){ 
                                while ($codeText =~ /(routeMessage\(\'.+\')/g ){
                                  $processed = $1;
                                  $processed =~ s/routeMessage\(//;
                                  $processed =~ s/'//g;
                                  @edge = [$channel->{name}, $processed];
                                  push(@edges, @edge);
                                }
                              $j++;
                              }
                            $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( );
                        Thanks!
                        Last edited by csmith; 07-30-2010, 06:17 AM. Reason: added reason for the version change

                        Comment

                        Working...
                        X