/* * Copyright (C) 2015 Adrien Guille <adrien.guille@univ-lyon2.fr> * * 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 3 of the License, or * (at your option) 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 main.java.fr.ericlab.sondy.core.ui; import com.sun.javafx.css.StyleManager; import impl.org.controlsfx.skin.CheckComboBoxSkin; import main.java.fr.ericlab.sondy.core.app.Main; import main.java.fr.ericlab.sondy.core.app.AppParameters; import main.java.fr.ericlab.sondy.core.structures.Dataset; import main.java.fr.ericlab.sondy.core.structures.Datasets; import main.java.fr.ericlab.sondy.core.text.stopwords.StopwordSets; import main.java.fr.ericlab.sondy.core.utils.UIUtils; import java.io.File; import java.nio.file.Paths; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Task; import javafx.concurrent.WorkerStateEvent; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.Separator; import javafx.scene.control.Skin; import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import javafx.stage.FileChooser; import org.controlsfx.control.CheckComboBox; import org.controlsfx.control.PropertySheet; import org.controlsfx.control.RangeSlider; /** * * @author Adrien GUILLE, Laboratoire ERIC, Université Lumière Lyon 2 */ public class DataManipulationUI { public GridPane grid; // Available datasets // - list ListView<String> datasetListView; Datasets availableDatasets; // Selected dataset // - info Label selectedDatasetIdLabel; Label selectedDatasetDescriptionLabel; Label messageCountLabel; Label userCountLabel; Label relationshipCountLabel; // - available preprocessed corpora ChoiceBox preprocessedCorpusList; // Import TextField newDatasetIdentifierField; TextField newDatasetDescriptionField; // - property map Button messagesFileButton; Button networkFileButton; HashMap<String,String> newDatasetProperties; // - Preprocess ChoiceBox stemmingChoiceBox; ChoiceBox lemmatizationChoiceBox; ChoiceBox tokenizationChoiceBox; TextField timeSliceLengthField; // - Filter CheckComboBox<String> stopwordListsCheckComboBox; StopwordSets stopwordLists; RangeSlider resizeSlider; public DataManipulationUI(){ StyleManager.getInstance().addUserAgentStylesheet(PropertySheet.class.getResource("propertysheet.css").toExternalForm()); // Initializing the main grid grid = new GridPane(); grid.setPadding(new Insets(5,5,5,5)); // Adding separators grid.add(new Text("Available datasets"),0,0); grid.add(new Separator(),0,1); grid.add(new Text("Import a dataset"),0,3); grid.add(new Separator(),0,4); grid.add(new Text("Preprocess the selected dataset"),0,6); grid.add(new Separator(),0,7); grid.add(new Text("Filter the selected preprocessed dataset"),0,9); grid.add(new Separator(),0,10); // Initializing specific UIs availableDatasetsUI(); newDatasetProperties = new HashMap<>(); importUI(); preprocessUI(); filterUI(); } public final void availableDatasetsUI(){ initializeDatasetListView(); selectedDatasetDescriptionLabel = new Label("Selected dataset description"); selectedDatasetDescriptionLabel.setId("smalltext"); UIUtils.setSize(selectedDatasetDescriptionLabel,Main.columnWidthLEFT,24); VBox existingDatasetLEFT = new VBox(); existingDatasetLEFT.getChildren().addAll(datasetListView,new Rectangle(0,3),selectedDatasetDescriptionLabel); // Right part selectedDatasetIdLabel = new Label("Selected dataset id"); selectedDatasetIdLabel.setId("smalltext"); UIUtils.setSize(selectedDatasetIdLabel,Main.columnWidthRIGHT,16); messageCountLabel = new Label("Message count"); messageCountLabel.setId("smalltext"); UIUtils.setSize(messageCountLabel,Main.columnWidthRIGHT,16); userCountLabel = new Label("User count"); userCountLabel.setId("smalltext"); UIUtils.setSize(userCountLabel,Main.columnWidthRIGHT,16); relationshipCountLabel = new Label("Relationship count"); relationshipCountLabel.setId("smalltext"); UIUtils.setSize(relationshipCountLabel,Main.columnWidthRIGHT,16); initializePreprocessedCorpusList(); UIUtils.setSize(preprocessedCorpusList, Main.columnWidthRIGHT, 24); preprocessedCorpusList.setItems(AppParameters.dataset.preprocessedCorpusList); VBox existingDatasetRIGHT = new VBox(); existingDatasetRIGHT.getChildren().addAll(selectedDatasetIdLabel,messageCountLabel,userCountLabel,relationshipCountLabel,new Rectangle(0,3),preprocessedCorpusList); // Both parts HBox existingDatasetBOTH = new HBox(5); existingDatasetBOTH.getChildren().addAll(existingDatasetLEFT,existingDatasetRIGHT); grid.add(existingDatasetBOTH,0,2); } public final void importUI(){ GridPane gridLEFT = new GridPane(); // Labels Label messagesLabel = new Label("Messages file"); UIUtils.setSize(messagesLabel, Main.columnWidthLEFT/2, 24); Label networkLabel = new Label("Network file"); UIUtils.setSize(networkLabel, Main.columnWidthLEFT/2, 24); Label datasetIdentificatorLabel = new Label("Dataset ID"); UIUtils.setSize(datasetIdentificatorLabel, Main.columnWidthLEFT/2, 24); Label datasetDescriptionLabel = new Label("Dataset description"); UIUtils.setSize(datasetDescriptionLabel, Main.columnWidthLEFT/2, 24); gridLEFT.add(messagesLabel,0,0); gridLEFT.add(new Rectangle(0,3),0,1); gridLEFT.add(networkLabel,0,2); gridLEFT.add(new Rectangle(0,3),0,3); gridLEFT.add(datasetIdentificatorLabel,0,4); gridLEFT.add(new Rectangle(0,3),0,5); gridLEFT.add(datasetDescriptionLabel,0,6); // Values messagesFileButton = createChooseFileButton("messagesFile"); UIUtils.setSize(messagesFileButton,Main.columnWidthLEFT/2, 24); networkFileButton = createChooseFileButton("networkFile"); UIUtils.setSize(networkFileButton,Main.columnWidthLEFT/2, 24); newDatasetIdentifierField = new TextField(); newDatasetIdentifierField.setPromptText("unique identifier"); UIUtils.setSize(newDatasetIdentifierField,Main.columnWidthLEFT/2, 24); newDatasetDescriptionField = new TextField(); newDatasetDescriptionField.setPromptText("short dataset description"); UIUtils.setSize(newDatasetDescriptionField,Main.columnWidthLEFT/2, 24); gridLEFT.add(messagesFileButton,1,0); gridLEFT.add(networkFileButton,1,2); gridLEFT.add(newDatasetIdentifierField,1,4); gridLEFT.add(newDatasetDescriptionField,1,6); HBox importDatasetBOTH = new HBox(5); importDatasetBOTH.getChildren().addAll(gridLEFT,createImportButton()); grid.add(importDatasetBOTH,0,5); } public final void preprocessUI(){ GridPane gridLEFT = new GridPane(); // Labels Label stemmingLabel = new Label("Stemming"); UIUtils.setSize(stemmingLabel, Main.columnWidthLEFT/2, 24); Label LemmatizationLabel = new Label("Lemmatization"); UIUtils.setSize(LemmatizationLabel, Main.columnWidthLEFT/2, 24); Label TokenizationLabel = new Label("Tokenization"); UIUtils.setSize(TokenizationLabel, Main.columnWidthLEFT/2, 24); Label partitionLabel = new Label("Partition and index messages"); UIUtils.setSize(partitionLabel, Main.columnWidthLEFT/2, 24); gridLEFT.add(stemmingLabel,0,0); gridLEFT.add(new Rectangle(0,3),0,1); gridLEFT.add(LemmatizationLabel,0,2); gridLEFT.add(new Rectangle(0,3),0,3); gridLEFT.add(TokenizationLabel,0,4); gridLEFT.add(new Rectangle(0,3),0,5); gridLEFT.add(partitionLabel,0,6); // Values stemmingChoiceBox = new ChoiceBox(); stemmingChoiceBox.getItems().addAll("disabled","French","English","Arabic","Persian"); UIUtils.setSize(stemmingChoiceBox,Main.columnWidthLEFT/2, 24); lemmatizationChoiceBox = new ChoiceBox(); lemmatizationChoiceBox.getItems().addAll("disabled","French","English"); UIUtils.setSize(lemmatizationChoiceBox,Main.columnWidthLEFT/2, 24); tokenizationChoiceBox = new ChoiceBox(); tokenizationChoiceBox.getItems().addAll("1-gram","2-gram","3-gram"); UIUtils.setSize(tokenizationChoiceBox,Main.columnWidthLEFT/2, 24); timeSliceLengthField = new TextField(); timeSliceLengthField.setPromptText("time-slice length in minutes (e.g. 30)"); UIUtils.setSize(timeSliceLengthField,Main.columnWidthLEFT/2, 24); gridLEFT.add(stemmingChoiceBox,1,0); gridLEFT.add(lemmatizationChoiceBox,1,2); gridLEFT.add(tokenizationChoiceBox,1,4); gridLEFT.add(timeSliceLengthField,1,6); HBox preprocessDatasetBOTH = new HBox(5); preprocessDatasetBOTH.getChildren().addAll(gridLEFT,createPreprocessButton()); grid.add(preprocessDatasetBOTH,0,8); } public final void filterUI(){ GridPane gridLEFT = new GridPane(); // Labels Label stopwordsLabel = new Label("Stop words removal"); UIUtils.setSize(stopwordsLabel, Main.columnWidthLEFT/2, 24); Label resizingLabel = new Label("Resizing"); UIUtils.setSize(resizingLabel, Main.columnWidthLEFT/2, 24); gridLEFT.add(stopwordsLabel,0,0); gridLEFT.add(new Rectangle(0,3),0,1); gridLEFT.add(resizingLabel,0,2); // Values stopwordLists = new StopwordSets(); stopwordListsCheckComboBox = new CheckComboBox<>(stopwordLists.availableSets); stopwordListsCheckComboBox.setStyle("-fx-font-size: 12px;"); stopwordListsCheckComboBox.skinProperty().addListener(new ChangeListener<Skin>() { @Override public void changed(ObservableValue<? extends Skin> observable, Skin oldValue, Skin newValue) { if(oldValue==null && newValue!=null){ CheckComboBoxSkin skin = (CheckComboBoxSkin)newValue; ComboBox combo = (ComboBox)skin.getChildren().get(0); combo.setPrefWidth(Main.columnWidthLEFT/2); combo.setMaxWidth(Double.MAX_VALUE); } } }); // stopwordListsCheckComboBox.setMaxWidth(Double.MAX_VALUE); // UIUtils.setSize(stopwordListsCheckComboBox,Main.columnWidthLEFT/2, 24); gridLEFT.add(stopwordListsCheckComboBox,1,0); resizeSlider = new RangeSlider(); resizeSlider.setBlockIncrement(0.1); UIUtils.setSize(resizeSlider,Main.columnWidthLEFT/2, 24); resizeSlider.resize(Main.columnWidthLEFT/2, 24); gridLEFT.add(resizeSlider,1,2); HBox filterDatasetBOTH = new HBox(5); filterDatasetBOTH.getChildren().addAll(gridLEFT,createFilterButton()); grid.add(filterDatasetBOTH,0,11); } public final void initializeDatasetListView(){ availableDatasets = new Datasets(); availableDatasets.update(); datasetListView = new ListView<>(); datasetListView.setOrientation(Orientation.HORIZONTAL); datasetListView.setItems(availableDatasets.list); datasetListView.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends String> ov, String old_val, String new_val) -> { if(new_val != null){ selectedDatasetIdLabel.setText(new_val); AppParameters.dataset.load(new_val); messageCountLabel.setText(AppParameters.dataset.corpus.messageCount+" messages"); userCountLabel.setText(AppParameters.dataset.corpus.authorCount+" users"); relationshipCountLabel.setText(AppParameters.dataset.network.relationshipCount+" relationships"); selectedDatasetDescriptionLabel.setText(AppParameters.dataset.description); resizeSlider.setMin(0); resizeSlider.setLowValue(0); resizeSlider.setMax(AppParameters.dataset.corpus.getLength()); resizeSlider.setHighValue(AppParameters.dataset.corpus.getLength()); clearPreprocessUI(); } }); UIUtils.setSize(datasetListView,Main.columnWidthLEFT,64); } public final void initializePreprocessedCorpusList(){ preprocessedCorpusList = new ChoiceBox(); UIUtils.setSize(preprocessedCorpusList, Main.columnWidthRIGHT, 24); preprocessedCorpusList.setItems(AppParameters.dataset.preprocessedCorpusList); preprocessedCorpusList.valueProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue ov, String t, String t1) { clearFilterUI(); if(t1 != null){ LogUI.addLogEntry("Loading '"+AppParameters.dataset.id+"' ("+t1+")... "); AppParameters.dataset.corpus.loadFrequencies(t1); AppParameters.timeSliceA = 0; AppParameters.timeSliceB = AppParameters.dataset.corpus.messageDistribution.length; LogUI.addLogEntry("Done."); resizeSlider.setMin(0); resizeSlider.setLowValue(0); resizeSlider.setMax(AppParameters.dataset.corpus.getLength()); resizeSlider.setHighValue(AppParameters.dataset.corpus.getLength()); } } }); } public final Button createChooseStopwordsButton(){ Button button = new Button("Choose stopword sets..."); button.setOnAction((ActionEvent event) -> { }); return button; } public final Button createChooseFileButton(String buttonId){ Button button = new Button("Choose file..."); button.setOnAction((ActionEvent event) -> { FileChooser fileChooser = new FileChooser(); FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("CSV files (*.csv)", "*.csv"); fileChooser.getExtensionFilters().add(extFilter); File file = fileChooser.showOpenDialog(null); if(file != null){ newDatasetProperties.put(buttonId, file.getAbsolutePath()); button.setText(Paths.get(file.getPath()).getFileName().toString()); } }); return button; } public final VBox createImportButton(){ VBox buttonBox = new VBox(); buttonBox.setAlignment(Pos.CENTER); Button importButton = new Button("Import"); UIUtils.setSize(importButton,Main.columnWidthRIGHT,24); importButton.setOnAction((ActionEvent event) -> { AppParameters.disableUI(true); newDatasetProperties.put("id",newDatasetIdentifierField.getText()); newDatasetProperties.put("description",newDatasetDescriptionField.getText()); Dataset dataset = new Dataset(); AppParameters.disableUI(true); String messagesFilePath = newDatasetProperties.get("messagesFile"); String networkFilePath = newDatasetProperties.get("networkFile"); LogUI.addLogEntry("Importing '"+newDatasetIdentifierField.getText()+"' (messages file: "+messagesFilePath+", network file: "+networkFilePath+")..."); final Task<String> waitingTask = new Task<String>() { @Override public String call() throws Exception { String[] log = dataset.create(newDatasetProperties); LogUI.addLogEntry(log[0]); LogUI.addLogEntry(log[1]); return "" ; } }; waitingTask.setOnSucceeded((WorkerStateEvent event1) -> { newDatasetProperties.clear(); availableDatasets.update(); messagesFileButton.setText("Choose file..."); networkFileButton.setText("Choose file..."); newDatasetIdentifierField.clear(); newDatasetDescriptionField.clear(); AppParameters.disableUI(false); }); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(waitingTask); }); buttonBox.getChildren().addAll(importButton); return buttonBox; } public final VBox createPreprocessButton(){ VBox buttonBox = new VBox(); buttonBox.setAlignment(Pos.CENTER); Button preprocessButton = new Button("Preprocess"); UIUtils.setSize(preprocessButton,Main.columnWidthRIGHT,24); preprocessButton.setOnAction((ActionEvent event) -> { AppParameters.disableUI(true); int ngram = Integer.parseInt(tokenizationChoiceBox.getSelectionModel().getSelectedItem().toString().split("-")[0]); int timeSliceLength = Integer.parseInt(timeSliceLengthField.getText()); String stemming = stemmingChoiceBox.getSelectionModel().getSelectedItem().toString(); String lemmatization = lemmatizationChoiceBox.getSelectionModel().getSelectedItem().toString(); LogUI.addLogEntry("Preprocessing '"+AppParameters.dataset.id+"' (stemming: "+stemming+", lemmatization: "+lemmatization+", "+ngram+"-gram, "+timeSliceLength+"-min time-slices)..."); final Task<String> waitingTask = new Task<String>() { @Override public String call() throws Exception { String log = AppParameters.dataset.preprocess(stemming,lemmatization,ngram,timeSliceLength); LogUI.addLogEntry(log); return ""; } }; waitingTask.setOnSucceeded((WorkerStateEvent event1) -> { AppParameters.dataset.preprocess(stemmingChoiceBox.getSelectionModel().getSelectedItem().toString(),lemmatizationChoiceBox.getSelectionModel().getSelectedItem().toString(),ngram,timeSliceLength); AppParameters.dataset.updatePreprocessedCorpusList(); AppParameters.disableUI(false); clearPreprocessUI(); }); ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(waitingTask); }); buttonBox.getChildren().addAll(preprocessButton); return buttonBox; } public final VBox createFilterButton(){ VBox buttonBox = new VBox(); buttonBox.setAlignment(Pos.CENTER); Button filterButton = new Button("Filter"); UIUtils.setSize(filterButton,Main.columnWidthRIGHT,24); filterButton.setOnAction((ActionEvent event) -> { if(!AppParameters.dataset.corpus.preprocessing.equals("")){ LogUI.addLogEntry("Filtering '"+AppParameters.dataset.id+"' (stop word set(s): "+stopwordListsCheckComboBox.getCheckModel().getCheckedItems()+", time period: ["+resizeSlider.getLowValue()+";"+resizeSlider.getHighValue()+"])"); AppParameters.timeSliceA = AppParameters.dataset.corpus.convertDayToTimeSlice(resizeSlider.getLowValue()); if(resizeSlider.getHighValue() != AppParameters.dataset.corpus.getLength()) AppParameters.timeSliceB = AppParameters.dataset.corpus.convertDayToTimeSlice(resizeSlider.getHighValue()); AppParameters.updateStopwords(stopwordListsCheckComboBox.getCheckModel().getCheckedItems()); LogUI.addLogEntry("Done."); }else{ LogUI.addLogEntry("Error: no dataset loaded"); } }); buttonBox.getChildren().addAll(filterButton); return buttonBox; } public final void clearPreprocessUI(){ stemmingChoiceBox.getSelectionModel().clearSelection(); lemmatizationChoiceBox.getSelectionModel().clearSelection(); tokenizationChoiceBox.getSelectionModel().clearSelection(); timeSliceLengthField.clear(); preprocessedCorpusList.getSelectionModel().clearSelection(); } public final void clearFilterUI(){ stopwordListsCheckComboBox.getCheckModel().clearChecks(); resizeSlider.setHighValue(resizeSlider.getHighValue()); resizeSlider.setLowValue(resizeSlider.getLowValue()); } }