Parsing Symphony Chat with Perl

Viewed 82

We changed over from MindAlign to Symphony Chat at work. Symphony seems akin to ZenDesk chat software. We use the Symphony Chat to assign tickets to people.

When I cut and paste from the Symphony terminal it comes out like this (it has no newlines - it is just one big contiguous line of text):

16th Jun 2020 7:57:18 am Tom Lewin: WJ: RE: BART failed STP - JIMBCI - INC101981467816th Jun 2020 8:20:38 am Nathan Winslow: II : RE: Loans are experiencing issues sending RUNZ - INC101981521816th Jun 2020 8:57:58 am Nathan Winslow: NW : RE: FW: Missing pool factor PnL [Restricted - Internal] - INC101981603016th Jun 2020 9:13:49 am Nathan Winslow: JK : RE: missing sales credits - INC101981633816th Jun 2020 9:24:26 am Nathan Winslow: KB : RE: Bookbuilder not responding - INC1019816567

So I wrote this script to form it better - it works fine.

#!/usr/bin/perl
use warnings ;
use strict ;

my $filename = shift @ARGV ;
open(my $fh, '<', $filename) or die "Could not open file 
$filename $!";
while (my $line = <$fh>) {

chomp $line ;
my @words = split /(\d{2}(?:st|nd|rd|th) \w{3} \d{4})/a, $line;

foreach my $word(@words) {
        if($word =~ /\d{2}(?:st|nd|rd|th) \w{3} \d{4}/a) {
        chomp $word ;
        print $word ;
        }
        else {  print "$word\n"; }
    }
}

This is the output. Which is fine - however I find myself spending too much time cleaning up the data.

16th Jun 2020 7:57:18 am Tom Lewin: WJ: RE: BART failed STP - JIMBCI - INC1019814678
16th Jun 2020 8:20:38 am Nathan Winslow: II : RE: Loans are experiencing issues sending RUNZ - INC1019815218
16th Jun 2020 8:57:58 am Nathan Winslow: NW : RE: FW: Missing pool factor PnL [Restricted - Internal] - INC1019816030
16th Jun 2020 9:13:49 am Nathan Winslow: JK : RE: missing sales credits - INC1019816338   
16th Jun 2020 9:24:26 am Nathan Winslow: KB : RE: Bookbuilder not responding - INC1019816567

This is the problem - this is the output I need:

06/16/2020 WJ: RE: BART failed STP - JIMBCI - INC1019814678
06/16/2020 II : RE: Loans are experiencing issues sending RUNZ - INC1019815218 
06/16/2020 NW : RE: FW: Missing pool factor PnL - INC1019816030
06/16/2020 JK : RE: missing sales credits - INC1019816338
06/16/2020 KB : RE: Bookbuilder not responding - INC1019816567

I have tried regex's and splitting the lines but it is just a big mess. The WJ NW JK KB II those are initials - they are constant. Sometimes they have a space after the colon (:), sometimes they don't. However all I need is the Date and the Data starting at the Initial and ending at the last digit of the ticket number INC00000000.

2 Answers

One way to pull out what you need is a regex, given that you have clear anchors

my @parts = $string =~ /
    ([0-9]+)             # numbers for day
    (?:[^0-9]+)?\s+      # for st|nd|rd|th (optional!), space. not captured
    (\w+)\s+             # month
    ([0-9]+)\s+          # year
    .+?                  # the rest, but only up to the initials 
    ( (?:WJ|NW|JK|KB|II): .+? INC[0-9]+ )
/x; 

Some patterns here can be strengthened, or weakened (for one, instead of an alternation of expected initials we could have [A-Z]+:, allowing for others and for more letters).

Then convert time to the desired timestamp. A good tool for that is Time::Piece. Altogether

use warnings;
use strict;
use feature 'say';

use Time::Piece;

my $string = q(16th Jun 2020 7:57:18 am Tom Lewin: WJ: RE: BART failed STP - JIMBCI - INC101981467816th Jun 2020 8:20:38 am Nathan Winslow: II : RE: Loans are experiencing issues sending RUNZ - INC101981521816th Jun 2020 8:57:58 am Nathan Winslow: NW : RE: FW: Missing pool factor PnL [Restricted - Internal] - INC101981603016th Jun 2020 9:13:49 am Nathan Winslow: JK : RE: missing sales credits - INC101981633816th Jun 2020 9:24:26 am Nathan Winslow: KB : RE: Bookbuilder not responding - INC1019816567);

my @parts = $string =~ /
    ([0-9]+) (?:[^0-9]+)?\s+ (\w+)\s+ (\w+)\s+ .+? 
    ( (?:WJ|NW|JK|KB|II): .+? INC[0-9]+ )
/x;
#say for @parts;  say '---';

my $dt = Time::Piece->strptime("@parts[0..2]", "%d %b %Y");

say $dt->mdy('/'), ' ', $parts[3];

The last bit is perhaps better done as

my $date = Time::Piece
    -> strptime( join(' ', splice @parts, 0, 3), "%d %b %Y")
    -> mdy('/');

say "$date @parts";

where now we don't have to count off the exact number of elements to print.

In this case @parts in the end has just one element, but changes to requirements do happen. Also, if some elements are in fact needed on hand as separate for other purposes (add sets of capturing parenthesis) then @parts will have more elements.

These print what is needed.

The easiest way would be to "fix" the line before printing it.

So add

use Date::Parse;
sub fix_line {
  my $output = shift;
  if ($output =~ s/^(.*? (am|pm)).*?\s([A-Z]{2} ?:)/$3/) {
     my @date = localtime(str2time($1));
     $output = sprintf("%02d/%02d/%02d %s", @date[4]+1,@date[3],@date[5]-100,$output) ;
  }
  return $output."\n";
}

at the bottom of our string and change your inner fer-loop to:

my $nl = '';
foreach my $word(@words) {
   if($word =~ /\d{2}(?:st|nd|rd|th) \w{3} \d{4}/a) {
        chomp $word ;
        $nl.= $word ;
    }
    else {  
         print fix_line("$nl$word");
         $nl=''
    }
}
Related