RT#39913 Conexiant API
authorJonathan Prykop <jonathan@freeside.biz>
Thu, 12 May 2016 22:57:12 +0000 (17:57 -0500)
committerJonathan Prykop <jonathan@freeside.biz>
Thu, 12 May 2016 22:57:12 +0000 (17:57 -0500)
FS/FS/cdr/conexiant.pm [new file with mode: 0644]
FS/bin/freeside-cdr-conexiant-import [new file with mode: 0755]

diff --git a/FS/FS/cdr/conexiant.pm b/FS/FS/cdr/conexiant.pm
new file mode 100644 (file)
index 0000000..852c2f6
--- /dev/null
@@ -0,0 +1,43 @@
+package FS::cdr::conexiant;
+use base qw( FS::cdr );
+
+use strict;
+use vars qw( %info );
+use FS::Record qw( qsearchs );
+use FS::cdr qw( _cdr_date_parser_maker _cdr_min_parser_maker );
+
+%info = (
+  'name'          => 'Conexiant',
+  'weight'        => 600,
+  'header'        => 1,
+  'type'          => 'csv',
+  'import_fields' => [
+    skip(3),               #LookupError,Direction,LegType
+    sub {                  #CallId
+      my($cdr,$value,$conf,$param) = @_;
+      if (qsearchs('cdr',{'uniqueid' => $value})) {
+        $param->{'skiprow'} = 1;
+        $param->{'empty_ok'} = 1;
+      } else {
+        $cdr->uniqueid($value);
+      }
+    },
+    'upstream_rateplanid', #ClientRateSheetId
+    skip(1),               #ClientRouteId
+    'src',                 #SourceNumber
+    skip(1),               #RawNumber
+    'dst',                 #DestNumber
+    skip(1),               #DestLRN
+    _cdr_date_parser_maker('startdate'),  #CreatedOn
+    _cdr_date_parser_maker('answerdate'), #AnsweredOn
+    _cdr_date_parser_maker('enddate'),    #HangupOn
+    skip(4),               #CallCause,SipCode,Price,USFCharge
+    'upstream_price',      #TotalPrice
+    _cdr_min_parser_maker('billsec'),     #PriceDurationMins
+    skip(2),               #SipEndpointId, SipEndpointName
+  ],
+);
+
+sub skip { map {''} (1..$_[0]) }
+
+1;
diff --git a/FS/bin/freeside-cdr-conexiant-import b/FS/bin/freeside-cdr-conexiant-import
new file mode 100755 (executable)
index 0000000..a79477c
--- /dev/null
@@ -0,0 +1,127 @@
+#!/usr/bin/perl
+
+use strict;
+
+use Cpanel::JSON::XS;
+use Getopt::Long;
+use LWP::UserAgent;
+use MIME::Base64;
+use Net::HTTPS::Any qw(https_post https_get);
+
+use FS::UID qw(adminsuidsetup);
+use FS::Record qw(qsearchs);
+use FS::cdr;
+use FS::cdr_batch;
+
+sub usage {
+"Usage:
+freeside-cdr-conexiant-import -h -u username -p apikey [-v] freesideuser
+
+Downloads any existing CDR files with the BilledCallsOnly flag and 
+imports records that have not been imported yet.  Silently skips 
+records that have already been imported.
+";
+}
+
+# should really be using a module for this
+`which unzip` or die "can't find unzip executable";
+
+my ($username,$password,$verbose);
+GetOptions(
+  "password=s"  => \$password,
+  "username=s"  => \$username,
+  "verbose"     => \$verbose,
+);
+
+my $fsuser = $ARGV[-1];
+
+die usage() unless $fsuser;
+
+adminsuidsetup($fsuser);
+
+my ( $page, $response, %reply_headers ) = https_post(
+  'host'    => 'api.conexiant.net',
+  'port'    => '443',
+  'path'    => '/v1/Cdrs/SearchCdrsDownloads',
+  'headers' => {
+    'Authorization' => 'Basic ' . MIME::Base64::encode("$username:$password",'')
+  },
+  'content' => '{}',
+);
+
+die "Bad response from conexiant server: $response"
+  unless $response =~ /^200/;
+
+my $result = decode_json($page);
+
+die "Error from conexiant: " . ($result->{'ErrorInfo'} || 'No error message')
+  unless $result->{'Success'};
+
+my $files = $result->{'Data'}->{'Result'};
+
+die "Unexpected results from conexiant, not an array"
+  unless ref($files) eq 'ARRAY';
+
+my $dir = $FS::UID::cache_dir. "/cache.". $FS::UID::datasrc;
+my $ua  = LWP::UserAgent->new;
+
+# Download files are created automatically at regular frequent intervals,
+# but they contain overlapping data.
+#
+# FS::cdr::conexiant automatically skips previously imported cdrs,
+# though if it does so for all records in a file, 
+# then batch_import thinks the file is empty
+foreach my $file (@$files) {
+  next unless $file->{'BilledCallsOnly'};
+  my $cdrbatch = 'conexiant-' . $file->{'Identifier'};
+  # files that were "empty" will unfortunately be re-downloaded,
+  # but the alternative is to leave an excess of empty batches in system,
+  # and re-downloading is harmless (all files expire after 48 hours anyway)
+  if (qsearchs('cdr_batch',{ 'cdrbatch' => $cdrbatch })) {
+    print "$cdrbatch already imported\n" if $verbose;
+    next;
+  }
+  if ($verbose) {
+    print "Downloading $cdrbatch\n".
+          "  Created ".$file->{'CreatedOn'}."\n".
+          "  Start   ".$file->{'QueryStart'}."\n".
+          "  End     ".$file->{'QueryEnd'}."\n".
+          "  Link    ".$file->{'ValidLink'}."\n";
+  }
+  my $zfh = new File::Temp( TEMPLATE => 'conexiant.XXXXXXXX',
+                           SUFFIX   => '.zip',
+                           DIR      => $dir,
+                         )
+    or die "can't open temporary file to store download: $!\n";
+  my $cfh = new File::Temp( TEMPLATE => 'conexiant.XXXXXXXX',
+                           SUFFIX   => '.csv',
+                           DIR      => $dir,
+                         )
+    or die "can't open temporary file to unzip download: $!\n";
+  # yeah, these files ain't secured in any way
+  my $response = $ua->get($file->{'ValidLink'}, ':content_file' => $zfh->filename);
+  unless ($response->is_success) {
+    die "Error downloading $cdrbatch: ".$response->status_line;
+  }
+  my $zfilename = $zfh->filename;
+  print $cfh `unzip -p $zfilename 'Conexiant Cdrs.csv'`;
+  seek($cfh,0,0);
+  print "Importing batch $cdrbatch\n" if $verbose;
+  my $error = FS::cdr::batch_import({
+    'batch_namevalue' => $cdrbatch,
+    'file'            => $cfh->filename,
+    'format'          => 'conexiant'
+  });
+  if ($error eq 'Empty file!') {
+    print "File contains no new cdrs, no batch created\n" if $verbose;
+    $error = '';
+  } elsif ($verbose && !$error) {
+    print "File successfully imported\n";
+  }
+  die "Error importing $cdrbatch: $error" if $error;
+}
+
+exit;
+
+
+