frontend.GUIController.java Source code

Java tutorial

Introduction

Here is the source code for frontend.GUIController.java

Source

/*******************************************************************************
 * Copyright (c) 2015 University of Illinois Board of Trustees, All rights reserved.
 * Developed at GSLIS/ the iSchool, by Dr. Jana Diesner, Shubhanshu Mishra, Liang Tao, and Chieh-Li Chin.    
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version.
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses>.
 *******************************************************************************/
package frontend;

//import com.sun.javafx.scene.control.skin.SkinBase;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.cell.ComboBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.text.Text;
import javafx.scene.web.WebView;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.util.Callback;

import org.apache.commons.io.FilenameUtils;

import sentinets.Prediction.MODELTYPE;
import core.AnnotationTask;
import core.AppProperties;
import frontend.Tweet.LABEL;

/**
 * This is the GUI Controller class.
 * @author ltao3, Shubhanshu
 */
public class GUIController implements Initializable {

    @SuppressWarnings("unused")
    private Label label;
    @FXML
    private Button next1;
    @FXML
    private Button next2;
    @FXML
    private Button next13;
    @FXML
    private Button save2;
    @FXML
    private Button next4;
    @FXML // fx:id="retrain"
    private Button retrain; // Value injected by FXMLLoader
    @FXML
    private TableView<Tweet> featureTable;
    @FXML
    private WebView webview;
    @FXML // fx:id="predicted"
    private CheckBox predicted; // Value injected by FXMLLoader
    @FXML // fx:id="visualize"
    private CheckBox visualize; // Value injected by FXMLLoader
    @FXML
    private ProgressBar progressBar;
    @FXML
    private Label progressMessage;
    @FXML
    private ComboBox<String> combo1;
    @FXML
    private ComboBox<String> combo2;
    @FXML
    private ComboBox<String> combo3;
    @FXML
    private ComboBox<String> combo4;
    @FXML
    private ComboBox<String> combo5;
    @FXML
    private ComboBox<String> combo6;
    @FXML
    private ComboBox<String> combo7;
    @FXML
    private ComboBox<String> combo8;
    @FXML
    private ComboBox<String> combo9;
    @FXML
    private ComboBox<String> combo10;
    @FXML
    private TabPane tabPane;
    @FXML
    private Tab tab1;
    @FXML
    private Tab tab2;
    @FXML
    private AnchorPane webpage;
    @FXML
    private Tab tab3;
    @FXML
    private Tab tab4;
    @FXML
    private Tab tab5;
    @FXML
    private AnchorPane anchorPane2;
    @FXML
    private TableColumn<Tweet, String> tweetText;
    @FXML
    private TableColumn<Tweet, LABEL> tweetLabel;
    @FXML
    private TableColumn<Tweet, String> tweetFeature;

    @FXML
    private TableColumn<Tweet, Double> tweetProb;

    @FXML // fx:id="predModel"
    private ComboBox<MODELTYPE> predModel; // Value injected by FXMLLoader

    @FXML // fx:id="retrainOutput"
    private TextArea retrainOutput; // Value injected by FXMLLoader

    @FXML // fx:id="outFolder"
    private TextField outFolder; // Value injected by FXMLLoader

    @FXML // fx:id="inFolder"
    private TextField inFolder; // Value injected by FXMLLoader
    @FXML
    private ComboBox<String> outFiles;
    @FXML
    private LineChart<Integer, Double> metricsUpdate;
    @FXML
    private NumberAxis xAxis;
    @FXML
    private NumberAxis yAxis;

    private File currentDir = new File(System.getProperty("user.dir"));

    private ObservableList<Tweet> personData = FXCollections.observableArrayList();

    private Stage stage;

    private AnnotationTask at;

    private String customModelFile = "";

    private ArrayList<Double[]> metrics = new ArrayList<Double[]>();

    final DirectoryChooser dirChooser = new DirectoryChooser();
    final FileChooser fileChooser = new FileChooser();

    ArrayList<ComboBox<String>> comboBoxes;

    public void setStage(final Stage stage) {
        this.stage = stage;
    }

    public void exit() {
        at.stopServer();
    }

    public File selectFileDir(boolean isFile) {
        File file = null;
        if (isFile) {
            fileChooser.setInitialDirectory(currentDir);
            file = fileChooser.showOpenDialog(stage);
            currentDir = file;
        } else {
            dirChooser.setInitialDirectory(currentDir);
            file = dirChooser.showDialog(stage);
            currentDir = file;
        }
        return file;
    }

    @FXML
    private void chooseInput(ActionEvent event) throws Exception {
        File file = selectFileDir(false);
        if (file != null) {
            setInputFolder(file.getAbsolutePath());
        }
    }

    public void setInputFolder(String path) throws Exception {
        System.out.println("setInputFolder(" + path + ")");
        inFolder.setText(path);
        at.setInputDir(inFolder.getText());
        List<String> headers = at.readHeader();
        for (int i = 0; i < comboBoxes.size(); i++) {
            comboBoxes.get(i).getSelectionModel().clearSelection();
            comboBoxes.get(i).getItems().clear();
            comboBoxes.get(i).getItems().addAll(headers);
            comboBoxes.get(i).setPromptText("Choose one");
            if (headers.contains(at.defaultProperties.getHeader(AppProperties.propKeys[i]))) {
                comboBoxes.get(i).setValue(at.defaultProperties.getHeader(AppProperties.propKeys[i]));
            }
        }
        if (at.usingCustomConfig() && at.defaultProperties.getModel() != null) {
            predModel.setValue(MODELTYPE.CUSTOM);
            customModelFile = at.defaultProperties.getModel();
            System.out.println(customModelFile);
            at.setCustomModelFile(customModelFile);
        }
        progressBar.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS);
    }

    @FXML
    private void chooseOutput(ActionEvent event) throws Exception {
        File file = selectFileDir(false);
        if (file != null) {
            setOutputFolder(file.getAbsolutePath());
            //Save file here
        }
    }

    public void setOutputFolder(String path) throws Exception {
        System.out.println("setOutputFolder(" + path + ")");
        outFolder.setText(path);
        at.outputDir = outFolder.getText();
    }

    @SuppressWarnings("unused")
    private void selectModelFile(ActionEvent event) throws Exception {
        File file = selectFileDir(true);
        if (file != null) {
            customModelFile = file.getAbsolutePath();
            System.out.println(customModelFile);
            at.setCustomModelFile(customModelFile);
            //Save file here
        }
    }

    @FXML
    private void save(ActionEvent event) throws Exception {
        //ADD the existing file to the output folder
        SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
        selectionModel.select(tab5);
    }

    @FXML
    private void retrain(ActionEvent event) throws Exception {
        //ADD retrain the model
        System.out.println("Retraining the model");
        SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
        selectionModel.select(tab2);
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) throws IllegalArgumentException {
        at = new AnnotationTask();
        comboBoxes = new ArrayList<ComboBox<String>>();
        comboBoxes.add(combo1);
        comboBoxes.add(combo3);
        comboBoxes.add(combo5);
        comboBoxes.add(combo7);
        comboBoxes.add(combo9);
        comboBoxes.add(combo2);
        comboBoxes.add(combo4);
        comboBoxes.add(combo6);
        comboBoxes.add(combo8);
        comboBoxes.add(combo10);

        for (int i = 0; i < comboBoxes.size(); i++) {
            comboBoxes.get(i).setPromptText("Choose one");
        }

        predModel.getItems().add(MODELTYPE.SENTIMENT_WORD);
        predModel.getItems().add(MODELTYPE.CUSTOM);
        /* Handle ComboBox event.
        predModel.setOnAction((event) -> {
        if(predModel.getSelectionModel().getSelectedItem().equals(MODELTYPE.CUSTOM)){
           System.out.println("ComboBox Action (selected: " + MODELTYPE.CUSTOM + ")");
           try {
           this.selectModelFile(event);
        } catch (Exception e) {
           e.printStackTrace();
        }
        }
        });//*/
        //predModel.getItems().add(MODELTYPE.SENTIMENT);
        //predModel.getSelectionModel().selectFirst();
        //progressBar = new ProgressIndicator();

        // always use 'custom' config - what's in the config file!
        predModel.getSelectionModel().selectLast();
        predModel.setDisable(true);

        //        tabPane.prefWidthProperty().bind(scene.widthProperty());
        //        tabPane.prefHeightProperty().bind(scene.heightProperty());

        tweetText.setCellValueFactory(new PropertyValueFactory<Tweet, String>("tweet"));
        tweetText.setCellFactory(new Callback<TableColumn<Tweet, String>, TableCell<Tweet, String>>() {
            @Override
            public TableCell<Tweet, String> call(TableColumn<Tweet, String> param) {
                final TableCell<Tweet, String> cell = new TableCell<Tweet, String>() {
                    private Text text;

                    @Override
                    public void updateItem(String item, boolean empty) {
                        super.updateItem(item, empty);
                        if (!isEmpty()) {
                            text = new Text(item.toString());
                            text.wrappingWidthProperty().bind(getTableColumn().widthProperty());
                            setGraphic(text);
                        }
                    }
                };
                return cell;
            }
        });
        tweetLabel.setCellValueFactory(new PropertyValueFactory<Tweet, LABEL>("label"));
        tweetLabel.setCellFactory(ComboBoxTableCell.<Tweet, LABEL>forTableColumn(LABEL.values()));
        tweetLabel.setEditable(true);
        // add here
        //        
        //        tweetLabel.setOnEditCommit(
        //                (CellEditEvent<Tweet, LABEL> t) -> {
        //                    ((Tweet) t.getTableView().getItems().get(
        //                            t.getTablePosition().getRow())
        //                            ).setLabel(t.getNewValue());
        //            });

        tweetLabel.setOnEditCommit(event -> {
            @SuppressWarnings("rawtypes")
            final TableColumn.CellEditEvent _evn = (TableColumn.CellEditEvent) event;
            int rowId = _evn.getTablePosition().getRow();
            String newVal = _evn.getNewValue().toString();
            Tweet t = ((Tweet) _evn.getTableView().getItems().get(rowId));
            t.setLabel(LABEL.getEnum(newVal));
            System.out.println("Getting new value at row : " + rowId + " with label " + newVal);
            System.out.println("Tweet information : " + t.toString());
            String[] meta = t.getMeta();
            System.out.println(t.getTweet() + "\t" + meta[meta.length - 8]);
            System.out.println(
                    personData.get(rowId).getTweet() + "\t" + personData.get(rowId).getMeta()[meta.length - 8]);

        });

        tweetFeature.setCellValueFactory(new PropertyValueFactory<Tweet, String>("feature"));
        tweetProb.setCellValueFactory(new PropertyValueFactory<Tweet, Double>("probability"));
        dirChooser.setInitialDirectory(new File(System.getProperty("user.dir")));
        /*tweetText.prefWidthProperty().bind(featureTable.widthProperty().divide(4)); // w * 2/4
        tweetLabel.prefWidthProperty().bind(featureTable.widthProperty().divide(2)); // w * 1/4
        tweetFeature.prefWidthProperty().bind(featureTable.widthProperty().divide(4)); // w * 1/4        
         */

    }

    @FXML
    private void processLogin(ActionEvent event) throws Exception {
        processInput(new Runnable() {
            @Override
            public void run() {
                SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
                selectionModel.select(tab3);
                outFiles.getItems().clear();
                outFiles.getItems().addAll(at.files);
                System.out.println("Added files to drop down: " + at.files);
                outFiles.getSelectionModel().selectFirst();

                next1.setDisable(false);
                featureTable.setEditable(true);
                tweetLabel.setEditable(true);
                tweetText.setEditable(true);
                tweetFeature.setVisible(true);
                tweetProb.setVisible(true);
            }
        }, new Runnable() {
            @Override
            public void run() {
                System.err.println("Task Failed");
            }
        });
    }

    public Thread processInput(Runnable rSuccess, Runnable rFail) throws Exception {
        System.out.println("Steping to next step...");
        progressBar.progressProperty().unbind();
        progressMessage.textProperty().unbind();
        next1.setDisable(true);
        final ArrayList<String> comboBoxValues = new ArrayList<String>();
        for (ComboBox<String> c : comboBoxes) {
            comboBoxValues.add(c.getValue());
        }
        Task<Boolean> t = new Task<Boolean>() {
            @Override
            protected Boolean call() throws Exception {
                updateMessage("Doing Prediction");
                updateProgress(3, 10);
                int status = at.processAfterLoadFile(comboBoxValues, predModel.getValue(), visualize.isSelected(),
                        predicted.isSelected());
                if (status > 0) {
                    System.err.println("ProcessAfterLoadFile Error occured.");
                    return false;
                }
                updateMessage("Finished Prediction");
                if (visualize.isSelected()) {
                    updateProgress(5, 10);
                    at.showVisualization();
                    updateMessage("Finished Visualization");
                    updateProgress(8, 10);
                }
                updateProgress(10, 10);
                updateMessage("Done");

                //webpage.getChildren().add(new Browser(url));
                //at.addTweets(at.outputDir+"/visualization/Sentiment.tsv", personData);
                return true;
            };

            @Override
            protected void succeeded() {
                super.succeeded();
                updateMessage("Done!");
            }

            @Override
            protected void cancelled() {
                super.cancelled();
                updateMessage("Cancelled!");
            }

            @Override
            protected void failed() {
                super.failed();
                updateMessage("Failed!");
            }

            @Override
            protected void done() {
                super.done();
                updateMessage("Done!");
            }
        };
        progressBar.progressProperty().bind(t.progressProperty());
        progressMessage.textProperty().bind(t.messageProperty());

        t.setOnSucceeded(new EventHandler<WorkerStateEvent>() {

            @Override
            public void handle(WorkerStateEvent event) {
                if (t.getValue()) {
                    Platform.runLater(rSuccess);
                } else {
                    Platform.runLater(rFail);
                }
            }
        });

        Thread mythread = new Thread(t);
        mythread.start();
        return mythread;
    }

    @FXML
    void updateTable(ActionEvent event) {
        if (outFiles.getItems().size() < 1) {
            return;
        }
        fillTable(outFiles.getSelectionModel().getSelectedItem());
    }

    public void fillTable(String fileName) {
        System.out.println("Filling table using: " + fileName);
        String prefixName;
        prefixName = FilenameUtils.getBaseName(fileName);
        personData = FXCollections.observableArrayList();
        at.addTweets(at.outputDir + "/original/" + prefixName + ".tsv", personData);
        featureTable.setItems(personData);
    }

    @FXML
    private void processSave(ActionEvent event) {
        System.out.println("Steping to next step saving file...");
        String path = outFolder.textProperty().getValue().toString();
        at.saveChanges(path, personData, predicted.isSelected());

        SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
        selectionModel.select(tab5);
    }

    @FXML
    private void processSaveChange(ActionEvent event) {
        String path = outFolder.textProperty().getValue().toString();
        System.out.println("Saving changes to folder " + path);
        at.saveChanges(path, personData, predicted.isSelected());
    }

    @FXML
    void retrainModel(ActionEvent event) {
        retrainOutput.setText(at.updateModel(metrics));
        showChart();
    }

    @FXML
    void showAbout(ActionEvent event) {
        /*Dialogs.create()
        .title("About")
        .message(getLicense())
        .showInformation();*/
        Alert alert = new Alert(AlertType.INFORMATION);
        alert.initOwner(stage);
        alert.initModality(Modality.APPLICATION_MODAL);
        alert.setTitle("SAIL 1.1: About");
        alert.setHeaderText("SAIL 1.1 details");
        alert.setContentText("SAIL 1.1");
        Label label = new Label("SAIL License details:");

        TextArea textArea = new TextArea(getLicense());
        textArea.setEditable(false);
        textArea.setWrapText(true);

        textArea.setMaxWidth(Double.MAX_VALUE);
        textArea.setMaxHeight(Double.MAX_VALUE);
        GridPane.setVgrow(textArea, Priority.ALWAYS);
        GridPane.setHgrow(textArea, Priority.ALWAYS);

        GridPane expContent = new GridPane();
        expContent.setMaxWidth(Double.MAX_VALUE);
        expContent.add(label, 0, 0);
        expContent.add(textArea, 0, 1);

        // Set expandable Exception into the dialog pane.
        alert.getDialogPane().setExpandableContent(expContent);

        alert.showAndWait();

    }

    private String getLicense() {
        String file = GUI.licenseFile;
        StringBuilder output = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = br.readLine()) != null) {
                // process the line.
                output.append(line + "\n");

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return output.toString();

    }

    @FXML
    void editPreferences(ActionEvent event) {
        EditPreferences dialog = new EditPreferences(stage);
        Optional<AppProperties> result = dialog.showAndWait();
        result.ifPresent(props -> {
            at.setCustomModelFile(props.getModel());
            at.setLexiconPath(props.getLexicon());
            at.setQueryTerms(props.getQueryList());

            System.out.println("Updating preferences to: ");
            System.out.println(props.toString());
            System.out.println("Saving in file: " + GUI.configFile);
            FileOutputStream saveFile;
            try {
                saveFile = new FileOutputStream(GUI.configFile);
                props.store(saveFile, "Saving file");
                saveFile.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private void showChart() {

        /*metricsUpdate.getXAxis().setAutoRanging(true);
        metricsUpdate.getYAxis().setAutoRanging(true);
        */
        ArrayList<XYChart.Series<Integer, Double>> metricSeries = new ArrayList<XYChart.Series<Integer, Double>>();
        metricSeries.add(new XYChart.Series<Integer, Double>()); // POSITIVE
        metricSeries.add(new XYChart.Series<Integer, Double>()); // NEGATIVE
        metricSeries.add(new XYChart.Series<Integer, Double>()); // WEIGHTED AVERAGE

        metricSeries.get(0).setName("Positive");
        metricSeries.get(1).setName("negative");
        metricSeries.get(2).setName("Weighted Average");

        int i = 0;
        double min = 100.0, max = 0.0;
        double score = 0.0;
        for (Double[] fScores : metrics) {
            for (int j = 0; j < 3; j++) {
                score = fScores[j] * 100;
                if (score < min) {
                    min = score;
                }
                if (score > max) {
                    max = score;
                }
                metricSeries.get(j).getData().add(new XYChart.Data<Integer, Double>(i, score));
            }
            i++;
        }

        yAxis.setLowerBound(min);
        yAxis.setUpperBound(max);
        xAxis.setTickUnit(1);

        metricsUpdate.getData().add(metricSeries.get(0)); // POSITIVE
        metricsUpdate.getData().add(metricSeries.get(1)); // NEGATIVE 
        metricsUpdate.getData().add(metricSeries.get(2)); // WEIGHTED AVERAGE

        System.err.println("Y min: " + min + ", Y max: " + max);
    }

    /*****************************************/
    /* config file methods
    /*****************************************/
    public String getProperty(String p) {
        System.out.println("getProperty(" + p + ")");
        AppProperties props = new AppProperties();
        try {
            props.load(new FileInputStream(GUI.configFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return (String) props.get(p);
    }

    public void setProperty(String p, String val) {
        System.out.println("setProperty(" + p + "," + val + ")");
        AppProperties props = new AppProperties();
        try {
            props.load(new FileInputStream(GUI.configFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        props.setProperty(p, val);
        switch (p) {
        case "model":
            at.setCustomModelFile(val);
            break;
        case "lexicon":
            at.setLexiconPath(val);
            break;
        case "query_list":
            at.setQueryTerms(val);
            break;
        }

        System.out.println("setProperty:saving in file: " + GUI.configFile);
        FileOutputStream saveFile;
        try {
            saveFile = new FileOutputStream(GUI.configFile);
            props.store(saveFile, "Saving file");
            saveFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void restoreDefaultProperties() {
        System.out.println("restoreDefaultProperties()");

        // load current properties
        AppProperties props = new AppProperties();
        try {
            props.load(new FileInputStream(GUI.configFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // load default
        AppProperties defaultProps = new AppProperties();
        try {
            defaultProps.load(new FileInputStream(GUI.configDefaultsFile));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // change current to default
        props.setProperty("model", defaultProps.getModel());
        at.setCustomModelFile(defaultProps.getModel());
        props.setProperty("lexicon", defaultProps.getLexicon());
        at.setLexiconPath(defaultProps.getLexicon());
        props.setProperty("query_list", defaultProps.getQueryList());
        at.setQueryTerms(defaultProps.getQueryList());

        System.out.println("restoreDefaultProperties:saving in file: " + GUI.configFile);
        FileOutputStream saveFile;
        try {
            saveFile = new FileOutputStream(GUI.configFile);
            props.store(saveFile, "Saving file");
            saveFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @FXML
    void closeWindow(ActionEvent event) {
        System.out.println("closeWindow:closing...");
        stage.close();
    }

}