**free

//  Example of training Watson's Visual Recognition API
//
//  Trains Watson by creating a classifier named "insuranceclaim" with
//  the following classes:
//     -- Broken Windshield
//     -- Flat Tires
//     -- Motorcycle Involved
//     -- Vandalism
//  also includes negatives (negative examples)
//
ctl-opt dftactgrp(*no) actgrp(*new)
        bnddir('HTTPAPI': 'YAJL')
        option(*srcstmt: *noshowcpy);

/copy HTTPAPI_H
/copy YAJL_H
/copy IFSIO_H

dcl-pi *n;
   Classifier char(32) const options(*nopass);
   WaitFor    char(32) const options(*nopass);
end-pi;

dcl-pr sleep uns(10) ExtProc('sleep');
   secs uns(10) value;
end-pr;

dcl-c IFSDIR '/home/sklement/watson_insurance_images';
dcl-c WATSON_API_KEY 'amgXdJODw4_nZ6HcLMRZrHmv2qJzTCOrrH6PtZMm5E7M';

dcl-s myClassifier  varchar(100);
dcl-s myWaitFor     char(10) inz('*YES');
dcl-s msg           varchar(500);
dcl-s cmd           varchar(200);
dcl-s status        varchar(20);

if %parms >= 1;
   myClassifier = %trim(Classifier);
endif;
if %parms >= 2;
   myWaitFor = %xlate('yesno':'YESNO': %trim(WaitFor));
endif;

*inlr = *on;

if %len(myClassifier) = 0;
   myClassifier = DoTraining(msg);
   if msg <> '';
      http_comp(msg);
      return;
   endif;
endif;

if %len(myClassifier) > 0 and myWaitFor = '*YES';
   dou status = 'ready';
      status = CheckStatus(myClassifier: msg);
      if msg <> '';
         http_comp(msg);
         return;
      endif;
      ShowStatus(myClassifier: status);
      if status <> 'ready';
         sleep(60);
      endif;
   enddo;
   http_comp( 'Classifier ' + myClassifier
            + ' is ' + status + '.');
endif;

*inlr = *on;

dcl-proc DoTraining;

   dcl-pi *n varchar(100);
      errorMsg varchar(500);
   end-pi;

   dcl-s tempfile    varchar(100);
   dcl-s contentType char(64);
   dcl-s rc          int(10);
   dcl-s url         varchar(1000);
   dcl-s form        pointer;
   dcl-s response    varchar(100000);

   dcl-ds result qualified;
      classifier_id varchar(100);
      name          varchar(100);
      status        varchar(20);
      owner         varchar(40);
      created       char(24);
      updated       char(24);
      retrained     char(24);
      num_classes   int(10);
      dcl-ds classes dim(20);
         class varchar(100);
      end-ds;
      core_ml_enabled ind;
   end-ds;

   errorMsg = '';

   // --------------------------------------------------------------
   //   Watson wants the images sent in ZIP files encoded as a
   //   multipart/form-data (MFD) file.
   //
   //   -- httpapi creates MFD in an IFS stream file, so I use
   //        http_tempFile() to create a random temp file name
   //   -- http_mfd_encoder_open() starts the form within HTTPAPI
   //   -- Each zip file is added with http_mfd_encoder_addstmf
   //   -- The form field names are named "XXX_positive_examples"
   //        where XXX is the classifier name to create, and
   //        "positive_examples" means its a positive example
   //   -- A form field named "negative_examples" is for negative
   //        examples.
   //   -- http_mfd_close() finishes the form making it ready
   //        to send.
   // --------------------------------------------------------------
   tempFile = http_tempfile();

   form = http_mfd_encoder_open( tempFile: contentType );

   http_mfd_encoder_addvar_s( form
                            : 'name'
                            : 'insuranceclaims');

   http_mfd_encoder_addstmf( form
                           : 'brokenwindshield_positive_examples'
                           : IFSDIR + '/BrokenWindshield.zip'
                           : 'application/zip' );

   http_mfd_encoder_addstmf( form
                           : 'flattire_positive_examples'
                           : IFSDIR + '/FlatTire.zip'
                           : 'application/zip' );

   http_mfd_encoder_addstmf( form
                           : 'motorcycleaccident_positive_examples'
                           : IFSDIR + '/MotorcycleAccident.zip'
                           : 'application/zip' );

   http_mfd_encoder_addstmf( form
                           : 'vandalism_positive_examples'
                           : IFSDIR + '/Vandalism.zip'
                           : 'application/zip' );

   http_mfd_encoder_addstmf( form
                           : 'negative_examples'
                           : IFSDIR + '/Negatives.zip'
                           : 'application/zip' );


   http_mfd_encoder_close(form);


   // --------------------------------------------------------------
   //   the base URL with /v3/classifiers is used to tell it to
   //   create a new classifier.
   //
   //   to update a classfifier the url would be /v3/classifiers/ID
   //   where "ID" is the name of the classfier to update.
   //
   //   version 2018-03-19 is the current version (as I write this.)
   // --------------------------------------------------------------
   http_debug(*on: '/tmp/watson-train-insurance.txt');

   url = 'https://gateway.watsonplatform.net'
       + '/visual-recognition/api/v3/classifiers'
       + '?version=2018-03-19';

   http_setAuth( HTTP_AUTH_BASIC
               : 'apikey'
               : WATSON_API_KEY );

   http_xproc( HTTP_POINT_UPLOAD_STATUS
             : %paddr(show_progress)
             : *null );

   rc = http_req( 'POST'
                : url
                : *omit: response
                : tempFile: *omit
                : %trim(contentType) );

   unlink(tempFile);

   http_xproc( HTTP_POINT_UPLOAD_STATUS
             : *null
             : *null );

   if rc <> 1;
      errorMsg = http_error();
      return '';
   endif;

   monitor;
     data-into result %DATA( response
                           : 'case=convert countprefix=num_ +
                              allowmissing=yes')
                      %PARSER('YAJLINTO');
   on-error;
      errorMsg = 'Response JSON parse failed. See Job Log';
      return '';
   endmon;


   // --------------------------------------------------------------
   //   Check result.  Most of the time, the status will be
   //   'training' until it has finished training the service
   // --------------------------------------------------------------

   if result.status = 'failed';
      errorMsg = 'Training failed. +
                 See /tmp/watson_train_insurance.txt';
      return '';
   endif;

   if result.status <> 'training';
      errorMsg = 'Unexpected status: ' + result.status
               + ' classifier_id:' + result.classifier_id;
   endif;

   return result.classifier_id;

end-proc;

dcl-proc CheckStatus;

   dcl-pi *n varchar(20);
      ClassifierId varchar(100);
      ErrorMsg     varchar(500);
   end-pi;

   dcl-ds result qualified;
      classifier_id varchar(100);
      name          varchar(100);
      status        varchar(20);
      owner         varchar(40);
      created       char(24);
      updated       char(24);
      retrained     char(24);
      num_classes   int(10);
      dcl-ds classes dim(20);
         class varchar(100);
      end-ds;
      core_ml_enabled ind;
   end-ds;

   dcl-s response varchar(5000);
   dcl-s url varchar(1000);

   errorMsg = '';

   // --------------------------------------------------------------
   //    Loop and wait for a ready status
   // --------------------------------------------------------------

   url = 'https://gateway.watsonplatform.net'
       + '/visual-recognition/api/v3/classifiers/'
       + ClassifierId
       + '?version=2018-03-19';

   http_debug(*on: '/tmp/watson-check-insurance.txt');

   http_setAuth( HTTP_AUTH_BASIC
               : 'apikey'
               : WATSON_API_KEY );

   response = http_string('GET': url);

   monitor;
     data-into result %DATA( response
                           : 'case=convert countprefix=num_ +
                              allowmissing=yes')
                      %PARSER('YAJLINTO');
   on-error;
      errorMsg = 'Response JSON parse failed. See Job Log';
      return '';
   endmon;

   return result.status;

end-proc;


dcl-proc show_progress;

   dcl-pi *n;
      bytes uns(10) value;
      total uns(10) value;
   end-pi;

   dcl-pr QMHSNDPM extPgm('QMHSNDPM');
     MsgId      char(7)     Const;
     MsgFile    char(20)    Const;
     MsgDta     char(32767) options(*varsize) Const;
     MsgDtaLen  int(10)     Const;
     MsgType    char(10)    Const;
     CallStkEnt char(10)    Const;
     CallStkCnt int(10)     Const;
     MessageKey char(4);
     ErrorCode  char(32767) options(*varsize);
   end-pr;

   dcl-s msg varchar(100);
   dcl-s msgkey char(4);
   dcl-s pct int(10);

   dcl-ds errorCode qualified;
      bytesProvided  int(10) inz(0);
      bytesAvailable int(10) inz(0);
   end-ds;

   pct = %inth((bytes * 100) / total);

   msg = 'Uploaded ' + %char(bytes) +
              ' of ' + %char(total) +
                ' (' + %char(pct) + '%)';

   QMHSNDPM( 'CPF9897'
           : 'QCPFMSG   QSYS'
           : msg
           : %len(msg)
           : '*STATUS'
           : '*EXT'
           : 0
           : msgkey
           : errorCode );

end-proc;


dcl-proc ShowStatus;

   dcl-pi *n;
      classifier varchar(100) const;
      status     varchar(20)  const;
   end-pi;

   dcl-pr QMHSNDPM extPgm('QMHSNDPM');
     MsgId      char(7)     Const;
     MsgFile    char(20)    Const;
     MsgDta     char(32767) options(*varsize) Const;
     MsgDtaLen  int(10)     Const;
     MsgType    char(10)    Const;
     CallStkEnt char(10)    Const;
     CallStkCnt int(10)     Const;
     MessageKey char(4);
     ErrorCode  char(32767) options(*varsize);
   end-pr;

   dcl-s msg varchar(200);
   dcl-s msgkey char(4);

   dcl-ds errorCode qualified;
      bytesProvided  int(10) inz(0);
      bytesAvailable int(10) inz(0);
   end-ds;

   msg = 'Classifier ' + classifier
       + ' is ' + status
       + '...';

   QMHSNDPM( 'CPF9897'
           : 'QCPFMSG   QSYS'
           : msg
           : %len(msg)
           : '*STATUS'
           : '*EXT'
           : 0
           : msgkey
           : errorCode );

end-proc; 