package com.github.aldurd392.UnitedTweetsAnalyzer;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import weka.classifiers.AbstractClassifier;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayesUpdateable;
import weka.classifiers.trees.J48;
import weka.core.Instances;
import weka.experiment.InstanceQuery;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * UnitedTweetsAnalyzer - com.github.aldurd392.UnitedTweetsAnalyzer
 * Created by aldur on 25/06/15.
 */
public class Learner {
    private final static Logger logger = LogManager.getLogger(Learner.class.getSimpleName());

    public final static Map<String, Class> classifiers = Collections.unmodifiableMap(
            new HashMap<String, Class>() {
                {
                    put("all", null);
                    put("nbayes", NaiveBayesUpdateable.class);
                    put("dtree", J48.class);
                }
            }
    );

    private final static String stringQuery = String.format(
            "SELECT %s.*, %s.%s " +
                    "FROM %s, %s " +
                    "WHERE %s.%s = %s.%s",
            Storage.TABLE_USER, Storage.TABLE_TWEET, Storage.COUNTRY,
            Storage.TABLE_USER, Storage.TABLE_TWEET,
            Storage.TABLE_USER, Storage.ID, Storage.TABLE_TWEET, Storage.USER_ID);

    private Instances training_data = null;
    private AbstractClassifier classifier = null;

    public Learner(String classifier_name) throws Exception {
        super();

        this.loadData();
        this.classifierFactory(classifier_name);
    }

    public void loadData() throws Exception {
        InstanceQuery query = null;

        try {
            query = new InstanceQuery();
            query.setUsername("nobody");
            query.setPassword("");
            query.setQuery(stringQuery);

            this.training_data = query.retrieveInstances();
            this.training_data.setClass(this.training_data.attribute(Storage.COUNTRY));
            this.training_data.randomize(new Random(0));
        } catch (Exception e) {
            logger.fatal("Error while executing DB query.");
            logger.debug(e);

            throw e;
        } finally {
            if (query != null) {
                query.close();
            }
        }
    }

    private void classifierFactory(String classifier_name) throws Exception {
        Class<?> classifier_class = Learner.classifiers.get(classifier_name);

        if (classifier_class == null) {
            final String error = "Unknown classifier name " + classifier_name;
            logger.error(error);
            throw new Exception(error);
        }

        try {
            Constructor<?> constructor = classifier_class.getConstructor();
            Object object = constructor.newInstance();

            assert (object instanceof AbstractClassifier);
            this.classifier = (AbstractClassifier) object;
        } catch (NoSuchMethodException |
                InvocationTargetException |
                InstantiationException |
                IllegalAccessException e) {
            logger.error("Error while instantiating classifier {}", classifier_class.getSimpleName());
            logger.debug(e);
            throw e;
        }

        logger.debug("Classifier {} correctly created.", this.classifier.getClass().getSimpleName());
    }

    public void buildClassifier() throws Exception {
        logger.info("Building classifier {}...", this.classifier.getClass().getSimpleName());

        try {
            this.classifier.buildClassifier(this.training_data);
        } catch (Exception e) {
            logger.error("Error while building classifier");
            throw e;
        }
    }

    private Map.Entry<Instances, Instances> splitTrainingTestData(float percentage_split) {
        assert (percentage_split > 0 && percentage_split < 1);

        final int trainingSize = (int) Math.round(this.training_data.numInstances() * (1.0 - percentage_split));
        final int testingSize = this.training_data.numInstances() - trainingSize;

        final Instances train = new Instances(training_data, 0, trainingSize);
        final Instances test = new Instances(training_data, trainingSize, testingSize);

        return new AbstractMap.SimpleEntry<>(train, test);
    }

    public void evaluateClassifier(float evaluation_rate) {
        logger.info("Evaluating built classifier {} and evaluation rate {}...",
                this.classifier.getClass().getSimpleName(), evaluation_rate);

        try {
            Evaluation eval;

            if (evaluation_rate < 1) {
                Map.Entry<Instances, Instances> data = splitTrainingTestData(evaluation_rate);
                eval = new Evaluation(data.getKey());
                eval.evaluateModel(this.classifier, data.getValue());
            } else {
                eval = new Evaluation(this.training_data);
                int rounded_evaluation_rate = Math.round(evaluation_rate);
                eval.crossValidateModel(
                        this.classifier,
                        this.training_data,
                        rounded_evaluation_rate,
                        new Random()
                );
            }

            System.out.println(eval.toSummaryString("\nResults\n======\n", false));
        } catch (Exception e) {
            logger.error("Error while evaluating the classifier");
            logger.debug(e);
        }
    }
}

@@ -852,46 +852,8 @@
   %7B%0A
-                    put(%22all%22, null);%0A
     
@@ -3415,32 +3415,39 @@
 Name());%0A    %7D%0A%0A
+    /*%0A
     public void 
@@ -3778,32 +3778,39 @@
 %0A        %7D%0A    %7D
+%0A    */
 %0A%0A    private Ma
@@ -4401,52 +4401,141 @@
 oid 
-evaluateClassifier(float evaluation_rate) %7B%0A
+buildAndEvaluate(float evaluation_rate) %7B%0A        try %7B%0A            Evaluation eval;%0A%0A            if (evaluation_rate %3C 1) %7B%0A        
     
@@ -4551,17 +4551,30 @@
 r.info(%22
-E
+Building and e
 valuatin
@@ -4579,14 +4579,8 @@
 ing 
-built 
 clas
@@ -4593,26 +4593,30 @@
  %7B%7D 
-and evaluation rat
+with testing percentag
 e %7B%7D
@@ -4621,16 +4621,24 @@
 %7B%7D...%22,%0A
+        
         
@@ -4712,91 +4712,8 @@
 );%0A%0A
-        try %7B%0A            Evaluation eval;%0A%0A            if (evaluation_rate %3C 1) %7B%0A
     
@@ -4795,32 +4795,98 @@
 aluation_rate);%0A
+%0A                this.classifier.buildClassifier(data.getKey());%0A%0A
                 
@@ -5144,24 +5144,216 @@
 tion_rate);%0A
+%0A                logger.info(%22Building and evaluating classifier %7B%7D with %7B%7D-fold validation...%22,%0A                        this.classifier.getClass().getSimpleName(), rounded_evaluation_rate);%0A%0A
             

fix bad build with cross validation