// Copyright (c) 2001, 2003, Oracle Corporation.  All rights reserved.  
// File: SVMCDemo.java

/**
 * This sample program describes how to use the ODM Java API to build, test and 
 * apply a classification model with text data using the 
 * SupportVectorMachine(SVM) algorithm.
 * This program uses the "MINING_BUILD_TEXT" dataset for building a text mining
 * model to predict which customer should receive an "AFFINITY_CARD".
 * It then tests the model  by computing model accuracy, confusion matrix 
 * and lift results using "MINING_TEST_TEXT" dataset. After testing the model
 * it applies the built model on "MINING_APPLY_TEXT" dataset to predict 
 * which customers must receive "AFFINITY_CARD".
 * 
 * NOTE:
 *   Before executing this program you need to provide DB URL, username 
 *   and password in login method and a valid data schema name
 *   by changing the DATA_SCHEMA_NAME constant value in the program.
 */

// Generic Java Imports
import java.io.*;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
import java.sql.SQLException;

// ODM Imports
import oracle.dmt.odm.*;
import oracle.dmt.odm.data.*;
import oracle.dmt.odm.settings.function.*;
import oracle.dmt.odm.settings.algorithm.*;
import oracle.dmt.odm.task.*;
import oracle.dmt.odm.model.*;
import oracle.dmt.odm.result.*;

public class SVMCDemo extends Object {

  private static DataMiningServer m_dms = null;
  private static oracle.dmt.odm.Connection m_dmsConn = null;
  //provide schema name that has the data tables/views
  private static final String DATA_SCHEMA_NAME = "schema name";
  public static void main( String args[] ) { 
    try {      
      //1. Login to the Data Mining Server
          //Create a DMS object
          m_dms = new DataMiningServer(
            "put DB URL here", //JDBC URL jdbc:oracle:thin:@<Host name>:<Port>:<SID>
            "user name", //User Name
            "password" //Password 
          );
          //Login to the DMS and create a DMS Connection
          m_dmsConn = m_dms.login();
          //Remove the objects to avoid name conflicts
          cleanup();
          //Text column needs to be indexed in order to perform text mining.
          //In this example, COMMENTS text column in MINING_BUILD_TEXT and 
          //MINING_TEST_TEXT tables are pre-indexed during the installation.  
          //However, you can comment out and modify the following code if you 
          //would like to perform context index for a different table.
          /*
           executeSql(m_dmsConn.getDBConnection(),
              "CREATE INDEX context_build_idx ON MINING_BUILD_TEXT(COMMENTS) "+
              "INDEXTYPE IS ctxsys.context PARAMETERS('nopopulate') ");
           executeSql(m_dmsConn.getDBConnection(),
              "CREATE INDEX context_test_idx ON MINING_TEST_TEXT(COMMENTS) "+
              "INDEXTYPE IS ctxsys.context PARAMETERS('nopopulate') ");
          */
      //2. Build a model
      buildModel();
      //3. Compute accuracy and confusion matrix for the model
      testModel();
      //4. Compute lift for the model
      computeLift();
      //5. Apply the model
      applyModel();
    } catch(Exception anyExp) {
      anyExp.printStackTrace(System.out);
    } finally {
      try {
      //6. Logout from the Data Mining Server
      m_dms.logout(m_dmsConn);
      } catch(Exception anyExp1) { }//Ignore
    }
  }

  /**
   * To build a mining model, the build dataset location details and 
   * the mining function settings are required. 
   * In this example, we will build a classification model using the build 
   * dataset "MINING_BUILD_TEXT" in the user schema, with the 
   * SupportVectorMachine algorithm. This model can be used to predict which 
   * customer should receive an "AFFINITY_CARD". After completing the build 
   * task, the model named "SVMCDemo_Model" will be created in the DMS.
   */
  public static void buildModel() throws ODMException, SQLException, IOException 
  {
    //1. Create PhysicalDataSpecification
        LocationAccessData lad = new LocationAccessData( 
                                       "MINING_BUILD_TEXT", //Build Table Name
                                       DATA_SCHEMA_NAME //Schema Name
                                     );
        NonTransactionalDataSpecification ntds = 
          new NonTransactionalDataSpecification( lad );
        Attribute sequenceId = new Attribute("CUST_ID", DataType.intType);  
        ntds.setSequenceAttribute(sequenceId);
        PhysicalDataSpecification pds = (PhysicalDataSpecification)ntds;
        
    //2. Create & Store Mining Function Settings
        SVMClassificationSettings svmAlgo = new SVMClassificationSettings(
          Normalization.minMax,
          KernelFunction.linear,
          0.001f,
          50000000,
          null,  // Standard deviation is not required if kernel function is "gaussian"
          null   // If complexity factor is null, default value will be computed
          );
      
        ClassificationFunctionSettings mfs =
          ClassificationFunctionSettings.create(
                   m_dmsConn,       //DMS Connection
                   svmAlgo,          //SVM algorithm settings
                   pds,             //Build data specification
                   "AFFINITY_CARD", //Target column
                   AttributeType.categorical, //Default attribute type
                   DataPreparationStatus.unprepared //Default preparation status
                   );
                         
        //Set inactive attributes by changing the default attribute usage
        mfs.adjustAttributeUsage( new String[]{"CUST_ID"},
                                  AttributeUsage.inactive); 

        //Set text attribute
        mfs.adjustAttributesType(new String[] {"COMMENTS"}, AttributeType.text);
            
        mfs.store( m_dmsConn, "SVMCDemo_MFS");
    //3. Create, store & execute Build Task
        MiningBuildTask buildTask = new MiningBuildTask(
                     pds, //Build data specification
                     "SVMCDemo_MFS", //Mining function settings name
                     "SVMCDemo_Model" //Mining model name
                     );
        executeTask(buildTask, "SVMCDemo_BuildTask" );
  }

  /**
   * This method stores the given task with the specified name in the DMS 
   * and submits the task for asynchronous execution in the DMS. After 
   * completing the task successfully it returns true. If there is a task 
   * failure, then it prints error description and returns false.
   * 
   * @param taskObj task object
   * @param taskName name of the task
   * 
   * @return boolean returns true when the task is successful
   */
  public static boolean executeTask(MiningTask taskObj, String taskName)
    throws ODMException, SQLException {
    boolean isTaskSuccess = false;
    taskObj.store(m_dmsConn, taskName); //Store the task
    taskObj.execute(m_dmsConn); //Submit the task for asynchronous execution
    System.out.print(taskName + " is started, please wait. ");
    //Wait for completion of the task
    MiningTaskStatus taskStatus = taskObj.waitForCompletion(m_dmsConn);
    //Check the status of the task after completion
    isTaskSuccess = taskStatus.getTaskState().equals(MiningTaskState.success);
    if( isTaskSuccess ) {
      //Task completed successfully
      System.out.println("It is successful.");
    } else {//Task failed
      throw new ODMException("It is failed.\nFailure Description: " + 
        taskStatus.getStateDescription() );
    }
    return isTaskSuccess;
  }

  /**
   * To assess accuracy & compute a confusion matrix for the SVM mining model, 
   * the test dataset location details and input model are required. 
   * In this example, we will test SVMCDemo_Model using "MINING_TEST_TEXT". 
   * After completing the test task, SVMCDemo_TestResults object will be  
   * created in the DMS. Classification test results have both the accuracy and 
   * confusion matrix.
   */
  public static void testModel() throws ODMException, SQLException, IOException 
  {
    //1. Create PhysicalDataSpecification
      LocationAccessData lad = new LocationAccessData( 
                                 "MINING_TEST_TEXT", //Test table/view Name
                                 DATA_SCHEMA_NAME ); //Schema Name
      PhysicalDataSpecification pds = 
        new NonTransactionalDataSpecification( lad );
    //2. Create, store & execute Test Task
      ClassificationTestTask testTask = new ClassificationTestTask(
                   pds, //test data specification
                   "SVMCDemo_Model", //Name of the model to be tested
                   "SVMCDemo_TestResults" ); //Test results name
      //Store & execute the task
      boolean isTaskSuccess = executeTask(testTask, "SVMCDemo_TestTask"); 
      if( isTaskSuccess ) {
        //Restore & display test results
        ClassificationTestResult testResult = 
          ClassificationTestResult.restore(m_dmsConn, "SVMCDemo_TestResults");
        System.out.println(testResult.toString());
      }
  }

  /**
   * To compute lift of the mining model on a test dataset, test dataset 
   * location details and input model are required. 
   * In this example, we compute lift using SVMCDemo_Model and the same dataset
   * used for testing the model i.e.,"MINING_TEST_TEXT". 
   * After completing the lift task, SVMCDemo_LiftResults object will be created 
   * in the DMS. A Lift results object provides at each quantile: quantile lift,
   * cumulative lift, target density, cumulative target density.
   */
  public static void computeLift() throws ODMException, SQLException, IOException
  {
    //1. Create PhysicalDataSpecification
      LocationAccessData lad = new LocationAccessData( 
                                     "MINING_TEST_TEXT", //Test table/view Name
                                     DATA_SCHEMA_NAME //Schema Name
                                   );
      PhysicalDataSpecification pds = 
        new NonTransactionalDataSpecification( lad );
    //2. Create, store & execute lift Task
      Category positiveCategory = new Category(
        "Positive value",
        "1",
        DataType.intType);        
      MiningLiftTask liftTask = new MiningLiftTask(
        pds, // Physical Data Spec for input data
        10, // Number of quantiles to be used
        positiveCategory, // Positive target value
        "SVMCDemo_Model", //Name of the model to be tested
        "SVMCDemo_LiftResults" //Lift results name
      );
      //Store & execute the task
      boolean isTaskSuccess = executeTask(liftTask, "SVMCDemo_LiftTask"); 
      if( isTaskSuccess ) {
        //Restore & display lift results
        MiningLiftResult liftResult = 
          MiningLiftResult.restore(m_dmsConn, "SVMCDemo_LiftResults");
        System.out.println(liftResult.toString());
      }
  }

  /**
   * To apply a mining model to a data, apply dataset location details, 
   * input model and the output table location details are required. 
   * In this example, we apply the SVMCDemo_Model to the "MINING_APPLY_TEXT"  
   * dataset to predict which customers receive an "AFFINITY_CARD".
   * After completing the apply task, SVMCDemo_ApplyResults object will be  
   * created in the DMS and an apply output table will be created at the  
   * specfied location.
   */
  public static void applyModel() throws ODMException, SQLException, IOException 
  {
    //1. Create PhysicalDataSpecification
        LocationAccessData lad = new LocationAccessData( 
                                 "MINING_APPLY_TEXT", //Apply table/view Name
                                 DATA_SCHEMA_NAME //Schema Name
                               );
        PhysicalDataSpecification pds = 
          new NonTransactionalDataSpecification( lad );
    //2. Create MiningApplyOutput & apply output table/view details
        // Create MiningApplyOutput object
        MiningApplyOutput mao = MiningApplyOutput.createDefault();
        // Add all the source attributes to be returned with the scored result.
        // In this example, attribute "CUST_ID" from the source table will be 
        // returned with the result aliased as "CUST_ID"
        MiningAttribute sourceAttribute =
                  new MiningAttribute("CUST_ID", DataType.intType,
                                      AttributeType.notApplicable);
        Attribute destinationAttribute = new Attribute("CUST_ID", DataType.intType);

        ApplySourceAttributeItem m_ApplySourceAttributeItem =
           new ApplySourceAttributeItem(sourceAttribute,destinationAttribute);
        // Add the ApplySourceAttributeItem object to the MiningApplyOutput
        mao.addItem(m_ApplySourceAttributeItem);
        //Output table location details
        LocationAccessData outputTable =
                 new LocationAccessData("SVMCDemo_Apply_Output_Table", DATA_SCHEMA_NAME);      
    //3. Create, store & execute apply Task
        MiningApplyTask applyTask = new MiningApplyTask(
                     pds, //test data specification
                     "SVMCDemo_Model", //Input model name
                     mao, //MiningApplyOutput object
                     outputTable, //Apply output table location details
                     "SVMCDemo_ApplyResults" //Apply results name
                     );
        //Store & execute the task
        boolean isTaskSuccess = executeTask(applyTask, "SVMCDemo_ApplyTask"); 
        if( isTaskSuccess ) {
          //Restore & display apply results
          MiningApplyResult applyResult = 
            MiningApplyResult.restore(m_dmsConn, "SVMCDemo_ApplyResults");
          System.out.println(applyResult.toString());
        }
  }

  /**
   * Cleanup the previously created objects if any. 
   */
  public static void cleanup() throws ODMException, SQLException
  {
    //Remove the MiningFunctionSettings
    try {
      ClassificationFunctionSettings.remove(m_dmsConn, "SVMCDemo_MFS");
    }catch(Exception anyExp1) { }//Ignore
    //Remove the MiningBuildTask
    try {
      MiningBuildTask.remove(m_dmsConn, "SVMCDemo_BuildTask");
    }catch(Exception anyExp2) { }//Ignore
    //Remove the MiningModel
    try {
      MiningModel.remove(m_dmsConn, "SVMCDemo_Model");
    }catch(Exception anyExp3) { }//Ignore
    //Remove the ClassificationTestTask
    try {
      ClassificationTestTask.remove(m_dmsConn, "SVMCDemo_TestTask");
    }catch(Exception anyExp4) { }//Ignore
    //Remove the ClassificationTestResults
    try {
      ClassificationTestResult.remove(m_dmsConn, "SVMCDemo_TestResults");
    }catch(Exception anyExp5) { }//Ignore
    //Remove the MiningLiftTask
    try {
      MiningLiftTask.remove(m_dmsConn, "SVMCDemo_LiftTask");
    }catch(Exception anyExp6) { }//Ignore
    //Remove the MiningLiftResult
    try {
      MiningLiftResult.remove(m_dmsConn, "SVMCDemo_LiftResults");
    }catch(Exception anyExp7) { }//Ignore
    //Remove the MiningApplyTask
    try {
      MiningApplyTask.remove(m_dmsConn, "SVMCDemo_ApplyTask");
    }catch(Exception anyExp8) { }//Ignore
    //Remove the MiningApplyResult and apply output table
    try {
      MiningApplyResult.remove(m_dmsConn, "SVMCDemo_ApplyResults", true);
    }catch(Exception anyExp9) { }//Ignore    
  }
  
  /**
   * Execute sql statement in a given database connection. 
   */
  public static void executeSql ( java.sql.Connection conn, String sql) throws SQLException
  {
    Statement stmt = conn.createStatement ();
    try {
      boolean okay = stmt.execute(sql);
    }
    catch (Exception e) {
      e.printStackTrace();
    }
    finally {
      stmt.close();
    }
  }
}
