diff --git a/.gitignore b/.gitignore index 80d0921..cc9376b 100644 --- a/.gitignore +++ b/.gitignore @@ -2545,3 +2545,4 @@ bin/ /javafx/src/javafx.web/javafx/scene/web/WebView.java /javafx/src/javafx.web/module-info.java /javafx/src.zip +/test_output/ynb.xlsx diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8cc0ac3..a8a3bfb 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -19,4 +19,11 @@ opens org.awdevelopment.smithlab.gui to javafx.fxml, javafx.graphics, javafx.controls; opens org.awdevelopment.smithlab.gui.controllers to javafx.fxml, javafx.graphics, javafx.controls; opens org.awdevelopment.smithlab to javafx.fxml, javafx.graphics, javafx.controls; + exports org.awdevelopment.smithlab.config.exceptions; + opens org.awdevelopment.smithlab.config to javafx.controls, javafx.fxml, javafx.graphics; + exports org.awdevelopment.smithlab.gui.controllers.main; + opens org.awdevelopment.smithlab.gui.controllers.main to javafx.controls, javafx.fxml, javafx.graphics; + exports org.awdevelopment.smithlab.gui.controllers.main.validatable_field; + opens org.awdevelopment.smithlab.gui.controllers.main.validatable_field to javafx.controls, javafx.fxml, javafx.graphics; + opens org.awdevelopment.smithlab.logging to javafx.controls, javafx.fxml, javafx.graphics; } \ No newline at end of file diff --git a/src/main/java/org/awdevelopment/smithlab/MainApplication.java b/src/main/java/org/awdevelopment/smithlab/MainApplication.java index 19ce01a..159c750 100644 --- a/src/main/java/org/awdevelopment/smithlab/MainApplication.java +++ b/src/main/java/org/awdevelopment/smithlab/MainApplication.java @@ -3,8 +3,9 @@ import javafx.stage.Stage; import org.apache.logging.log4j.LogManager; import org.awdevelopment.smithlab.args.Arguments; -import org.awdevelopment.smithlab.config.Config; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; import org.awdevelopment.smithlab.config.Mode; +import org.awdevelopment.smithlab.config.OutputSheetsConfig; import org.awdevelopment.smithlab.data.experiment.Experiment; import org.awdevelopment.smithlab.gui.FXMLResourceType; import org.awdevelopment.smithlab.gui.SceneLoader; @@ -21,15 +22,16 @@ public static void main(String[] args) { if (args.length == 0) launch(); else { Arguments arguments = new Arguments(args, LOGGER); - Config config = new Config(arguments); try { - if (config.mode() == Mode.GENERATE_EMPTY_INPUT_SHEET) { - OutputGenerator outputGenerator = new OutputGenerator(config, LOGGER); + if (arguments.getMode() == Mode.GENERATE_EMPTY_INPUT_SHEET) { + EmptyInputSheetConfig config = new EmptyInputSheetConfig(arguments, LOGGER); + OutputGenerator outputGenerator = new OutputGenerator(config); outputGenerator.generateEmptyInputSheet(); - } else if (config.mode() == Mode.GENERATE_OUTPUT_SHEETS) { + } else if (arguments.getMode() == Mode.GENERATE_OUTPUT_SHEETS) { + OutputSheetsConfig config = new OutputSheetsConfig(arguments, LOGGER); + OutputGenerator outputGenerator = new OutputGenerator(config); InputReader reader = new InputReader(config, LOGGER); Experiment experiment = reader.readExperimentData(); - OutputGenerator outputGenerator = new OutputGenerator(config, LOGGER); outputGenerator.generateOutput(experiment); } } catch (Exception ignored) { diff --git a/src/main/java/org/awdevelopment/smithlab/args/Arguments.java b/src/main/java/org/awdevelopment/smithlab/args/Arguments.java index c42f9b5..9ed8599 100644 --- a/src/main/java/org/awdevelopment/smithlab/args/Arguments.java +++ b/src/main/java/org/awdevelopment/smithlab/args/Arguments.java @@ -1,7 +1,7 @@ package org.awdevelopment.smithlab.args; import org.awdevelopment.smithlab.args.exceptions.*; -import org.awdevelopment.smithlab.config.ConfigDefault; +import org.awdevelopment.smithlab.config.ConfigDefaults; import org.awdevelopment.smithlab.config.Mode; import org.awdevelopment.smithlab.config.SampleLabelingType; import org.awdevelopment.smithlab.config.SortOption; @@ -21,25 +21,25 @@ public class Arguments { private final LoggerHelper LOGGER; - private OutputType outputType = ConfigDefault.OUTPUT_TYPE; - private Mode mode = ConfigDefault.MODE; - private boolean writeToDifferentFile = ConfigDefault.WRITE_TO_DIFFERENT_FILE; - private String outputFileName = ConfigDefault.OUTPUT_FILENAME; - private File inputFile = ConfigDefault.INPUT_FILE; - private SortOption outputSorting = ConfigDefault.SORT_OPTION; - private byte replicateNumber = ConfigDefault.NUMBER_OF_REPLICATES; - private String emptyInputSheetName = ConfigDefault.EMPTY_INPUT_SHEET_NAME; - private Set conditions = ConfigDefault.CONDITIONS; - private Set strains = ConfigDefault.STRAINS; - private Set days = ConfigDefault.DAYS; - private byte numDays = ConfigDefault.NUM_DAYS; - private boolean includeBaselineColumn = ConfigDefault.INCLUDE_BASELINE_COLUMN; - private SampleLabelingType sampleLabelingType = ConfigDefault.SAMPLE_LABELING_TYPE; - private byte numConditions = ConfigDefault.NUM_CONDITIONS; - private byte numStrains = ConfigDefault.NUM_STRAINS; - private boolean usingNumDays = ConfigDefault.USING_NUM_DAYS; - private boolean usingNumConditions = ConfigDefault.USING_NUM_CONDITIONS; - private boolean usingNumStrains = ConfigDefault.USING_NUM_STRAINS; + private OutputType outputType = ConfigDefaults.OUTPUT_TYPE; + private Mode mode = ConfigDefaults.MODE; + private boolean writeToDifferentFile = ConfigDefaults.WRITE_TO_DIFFERENT_FILE; + private String outputFileName = ConfigDefaults.OUTPUT_FILENAME; + private File inputFile = ConfigDefaults.INPUT_FILE; + private SortOption outputSorting = ConfigDefaults.SORT_OPTION; + private byte replicateNumber = ConfigDefaults.NUMBER_OF_REPLICATES; + private String emptyInputSheetName = ConfigDefaults.EMPTY_INPUT_SHEET_NAME; + private Set conditions = ConfigDefaults.CONDITIONS; + private Set strains = ConfigDefaults.STRAINS; + private Set days = ConfigDefaults.DAYS; + private byte numDays = ConfigDefaults.NUM_DAYS; + private boolean includeBaselineColumn = ConfigDefaults.INCLUDE_BASELINE_COLUMN; + private SampleLabelingType sampleLabelingType = ConfigDefaults.SAMPLE_LABELING_TYPE; + private byte numConditions = ConfigDefaults.NUM_CONDITIONS; + private byte numStrains = ConfigDefaults.NUM_STRAINS; + private boolean usingNumDays = ConfigDefaults.USING_NUM_DAYS; + private boolean usingNumConditions = ConfigDefaults.USING_NUM_CONDITIONS; + private boolean usingNumStrains = ConfigDefaults.USING_NUM_STRAINS; public Arguments(String[] args, LoggerHelper logger) { this.LOGGER = logger; @@ -160,7 +160,7 @@ else if (outputFileName.equals(inputFile.getPath())) { } } if (replicateNumber == -1) { - if (outputType == OutputType.BOTH || outputType == OutputType.STATISTICAL_TESTS) { + if (outputType == OutputType.BOTH || outputType == OutputType.OASIS) { throw new NoReplicatesProvidedException(outputType); } else { LOGGER.atWarn("No replicate number provided. " + diff --git a/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java b/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java new file mode 100644 index 0000000..89ae578 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java @@ -0,0 +1,85 @@ +package org.awdevelopment.smithlab.config; + +import org.awdevelopment.smithlab.config.exceptions.ConfigException; +import org.awdevelopment.smithlab.config.exceptions.ConfigOptionNotFoundException; +import org.awdevelopment.smithlab.config.exceptions.WrongObjectTypeException; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +public abstract class AbstractConfig { + + private final Set> config; + private final LoggerHelper LOGGER; + + protected AbstractConfig(LoggerHelper logger) { + this.config = new HashSet<>(); + this.LOGGER = logger; + config.add(new ConfigEntry<>(ConfigOption.GUI, ConfigDefaults.GUI)); + } + + public T get(ConfigOption option) { + try { + return getConfigEntryValue(option); + } catch (ConfigException e) { + LOGGER().atFatal(e.getMessage()); + System.exit(1); + } + return null; + } + + public void set(ConfigOption option, T value) { + try { + setConfigEntry(option, value); + } catch (ConfigException e) { + LOGGER().atFatal(e.getMessage()); + System.exit(1); + } + } + + protected void addConfigEntry(ConfigEntry entry) { + config.add(entry); + } + + private Optional> getConfigEntry(ConfigOption option) { + return config.stream().filter(e -> e.option().equals(option)).findFirst(); + } + + private T getConfigEntryValue(ConfigOption option) throws ConfigException { + Optional> entryOptional = getConfigEntry(option); + if (entryOptional.isPresent()) { + ConfigEntry typedEntry; + try { + typedEntry = (ConfigEntry) entryOptional.get(); + } catch (ClassCastException e) { + throw new WrongObjectTypeException(entryOptional.get(), this); + } + return typedEntry.value(); + } else { + throw new ConfigOptionNotFoundException(option, this); + } + } + + + private void setConfigEntry(ConfigOption option, T value) throws ConfigException { + Optional> entryOptional = getConfigEntry(option); + if (entryOptional.isPresent()) { + ConfigEntry typedEntry; + try { typedEntry = (ConfigEntry) entryOptional.get(); } + catch (ClassCastException e) { throw new WrongObjectTypeException(entryOptional.get(), this); } + typedEntry.setValue(value); + } else throw new ConfigOptionNotFoundException(option, this); + } + + public boolean GUI() { return get(ConfigOption.GUI); } + public void setGUI(boolean GUI) { set(ConfigOption.GUI, GUI); } + + @Override + public abstract String toString(); + + public abstract Mode mode(); + + public LoggerHelper LOGGER() { return LOGGER; } +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/Config.java b/src/main/java/org/awdevelopment/smithlab/config/Config.java deleted file mode 100644 index a6883ec..0000000 --- a/src/main/java/org/awdevelopment/smithlab/config/Config.java +++ /dev/null @@ -1,173 +0,0 @@ -package org.awdevelopment.smithlab.config; - -import org.awdevelopment.smithlab.args.Arguments; -import org.awdevelopment.smithlab.data.Condition; -import org.awdevelopment.smithlab.data.Strain; -import org.awdevelopment.smithlab.io.output.formats.OutputType; - -import java.io.File; -import java.util.Set; - -public class Config { - - private final ConfigEntry inputFile; - private final ConfigEntry mode; - private final ConfigEntry outputFilename; - private final ConfigEntry outputType; - private final ConfigEntry writeToDifferentFile; - private final ConfigEntry sortOption; - private final ConfigEntry numberOfReplicates; - private final ConfigEntry emptyInputSheetName; - private final ConfigEntry GUI; - private final ConfigEntry> conditions; - private final ConfigEntry> strains; - private final ConfigEntry> days; - private final ConfigEntry numDays; - private final ConfigEntry includeBaselineColumn; - private final ConfigEntry sampleLabelingType; - private final ConfigEntry numConditions; - private final ConfigEntry numStrains; - private final ConfigEntry usingNumDays; - private final ConfigEntry usingNumConditions; - private final ConfigEntry usingNumStrains; - - public Config() { - this.numberOfReplicates = new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefault.NUMBER_OF_REPLICATES); - this.inputFile = new ConfigEntry<>(ConfigOption.INPUT_FILE, ConfigDefault.INPUT_FILE); - this.mode = new ConfigEntry<>(ConfigOption.MODE, ConfigDefault.MODE); - this.outputFilename = new ConfigEntry<>(ConfigOption.OUTPUT_FILE, ConfigDefault.OUTPUT_FILENAME); - this.outputType = new ConfigEntry<>(ConfigOption.OUTPUT_TYPE, ConfigDefault.OUTPUT_TYPE); - this.writeToDifferentFile = new ConfigEntry<>(ConfigOption.WRITE_TO_DIFFERENT_FILE, ConfigDefault.WRITE_TO_DIFFERENT_FILE); - this.sortOption = new ConfigEntry<>(ConfigOption.SORT_OPTION, ConfigDefault.SORT_OPTION); - this.emptyInputSheetName = new ConfigEntry<>(ConfigOption.EMPTY_INPUT_SHEET_NAME, ConfigDefault.EMPTY_INPUT_SHEET_NAME); - this.GUI = new ConfigEntry<>(ConfigOption.GUI, Boolean.TRUE); - this.conditions = new ConfigEntry<>(ConfigOption.CONDITIONS, ConfigDefault.CONDITIONS); - this.strains = new ConfigEntry<>(ConfigOption.STRAINS, ConfigDefault.STRAINS); - this.days = new ConfigEntry<>(ConfigOption.DAYS, ConfigDefault.DAYS); - this.numDays = new ConfigEntry<>(ConfigOption.NUM_DAYS, ConfigDefault.NUM_DAYS); - this.includeBaselineColumn = new ConfigEntry<>(ConfigOption.INCLUDE_BASELINE_COLUMN, ConfigDefault.INCLUDE_BASELINE_COLUMN); - this.sampleLabelingType = new ConfigEntry<>(ConfigOption.SAMPLE_LABELING_TYPE, ConfigDefault.SAMPLE_LABELING_TYPE); - this.numConditions = new ConfigEntry<>(ConfigOption.NUM_CONDITIONS, ConfigDefault.NUM_CONDITIONS); - this.numStrains = new ConfigEntry<>(ConfigOption.NUM_STRAINS, ConfigDefault.NUM_STRAINS); - this.usingNumDays = new ConfigEntry<>(ConfigOption.USING_NUM_DAYS, ConfigDefault.USING_NUM_DAYS); - this.usingNumConditions = new ConfigEntry<>(ConfigOption.USING_NUM_CONDITIONS, ConfigDefault.USING_NUM_CONDITIONS); - this.usingNumStrains = new ConfigEntry<>(ConfigOption.USING_NUM_STRAINS, ConfigDefault.USING_NUM_STRAINS); - } - - public Config(Arguments arguments) { - this.inputFile = new ConfigEntry<>(ConfigOption.INPUT_FILE, arguments.getInputFile()); - this.mode = new ConfigEntry<>(ConfigOption.MODE, arguments.getMode()); - this.outputFilename = new ConfigEntry<>(ConfigOption.OUTPUT_FILE, arguments.getOutputFileName()); - this.outputType = new ConfigEntry<>(ConfigOption.OUTPUT_TYPE, arguments.getOutputType()); - this.writeToDifferentFile = new ConfigEntry<>(ConfigOption.WRITE_TO_DIFFERENT_FILE, arguments.writeToDifferentFile()); - this.sortOption = new ConfigEntry<>(ConfigOption.SORT_OPTION, arguments.getOutputSorting()); - this.numberOfReplicates = new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, arguments.getReplicateNumber()); - this.emptyInputSheetName = new ConfigEntry<>(ConfigOption.EMPTY_INPUT_SHEET_NAME, arguments.getEmptyInputSheetName()); - this.GUI = new ConfigEntry<>(ConfigOption.GUI, Boolean.FALSE); - this.conditions = new ConfigEntry<>(ConfigOption.CONDITIONS, arguments.getConditions()); - this.strains = new ConfigEntry<>(ConfigOption.STRAINS, arguments.getStrains()); - this.days = new ConfigEntry<>(ConfigOption.DAYS, arguments.getDays()); - this.numDays = new ConfigEntry<>(ConfigOption.NUM_DAYS, arguments.getNumDays()); - this.includeBaselineColumn = new ConfigEntry<>(ConfigOption.INCLUDE_BASELINE_COLUMN, arguments.getIncludeBaselineColumn()); - this.sampleLabelingType = new ConfigEntry<>(ConfigOption.SAMPLE_LABELING_TYPE, arguments.getSampleLabelingType()); - this.numConditions = new ConfigEntry<>(ConfigOption.NUM_CONDITIONS, arguments.getNumConditions()); - this.numStrains = new ConfigEntry<>(ConfigOption.NUM_STRAINS, arguments.getNumStrains()); - this.usingNumDays = new ConfigEntry<>(ConfigOption.USING_NUM_DAYS, arguments.usingNumDays()); - this.usingNumConditions = new ConfigEntry<>(ConfigOption.USING_NUM_CONDITIONS, arguments.usingNumConditions()); - this.usingNumStrains = new ConfigEntry<>(ConfigOption.USING_NUM_STRAINS, arguments.usingNumStrains()); - } - - public File inputFile() { - return inputFile.value(); - } - - public Mode mode() { - return mode.value(); - } - - public String outputFilename() { - return outputFilename.value(); - } - - public OutputType outputType() { - return outputType.value(); - } - - public boolean writeToDifferentFile() { - return writeToDifferentFile.value(); - } - - public byte numReplicates() { return numberOfReplicates.value(); } - - public SortOption sortOption() { return sortOption.value(); } - - public String emptyInputSheetName() { return emptyInputSheetName.value(); } - - public boolean GUI() { return GUI.value(); } - - public Set conditions() { return conditions.value(); } - - public Set strains() { return strains.value(); } - - public Set days() { return days.value(); } - - public byte numDays() { return numDays.value(); } - - public boolean includeBaselineColumn() { return includeBaselineColumn.value(); } - - public SampleLabelingType sampleLabelingType() { return sampleLabelingType.value(); } - - public byte numConditions() { return numConditions.value(); } - - public byte numStrains() { return numStrains.value(); } - - public boolean usingNumDays() { return usingNumDays.value(); } - - public boolean usingNumConditions() { return usingNumConditions.value(); } - - public boolean usingNumStrains() { return usingNumStrains.value(); } - - public void setOutputType(OutputType outputType) { this.outputType.setValue(outputType); } - - public void setInputFile(File inputFile) { this.inputFile.setValue(inputFile); } - - public void setMode(Mode mode) { this.mode.setValue(mode); } - - public void setOutputFilename(String outputFilename) { this.outputFilename.setValue(outputFilename); } - - public void setWriteToDifferentFile(boolean writeToDifferentFile) { this.writeToDifferentFile.setValue(writeToDifferentFile); } - - public void setNumberOfReplicates(byte numberOfReplicates) { this.numberOfReplicates.setValue(numberOfReplicates); } - - public void setSortOption(SortOption sortOption) { this.sortOption.setValue(sortOption); } - - public void setEmptyInputSheetName(String emptyInputSheetName) { this.emptyInputSheetName.setValue(emptyInputSheetName); } - - public void setConditions(Set conditions) { this.conditions.setValue(conditions); } - - public void setStrains(Set strains) { this.strains.setValue(strains); } - - public void addCondition(Condition condition) { this.conditions.value().add(condition); } - - public void addStrain(Strain strain) { this.strains.value().add(strain); } - - public void addDay(byte day) { this.days.value().add(day); } - - public void setDays(Set days) { this.days.setValue(days); } - - public void setNumDays(byte numDays) { this.numDays.setValue(numDays); } - - public void setIncludeBaselineColumn(boolean includeBaselineColumn) { this.includeBaselineColumn.setValue(includeBaselineColumn); } - - public void setSampleLabelingType(SampleLabelingType sampleLabelingType) { this.sampleLabelingType.setValue(sampleLabelingType); } - - public void setNumConditions(byte numConditions) { this.numConditions.setValue(numConditions); } - - public void setNumStrains(byte numStrains) { this.numStrains.setValue(numStrains); } - - public void setUsingNumDays(boolean usingNumDays) { this.usingNumDays.setValue(usingNumDays); } - - public void setUsingNumConditions(boolean usingNumConditions) { this.usingNumConditions.setValue(usingNumConditions); } - - public void setUsingNumStrains(boolean usingNumStrains) { this.usingNumStrains.setValue(usingNumStrains); } -} diff --git a/src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java b/src/main/java/org/awdevelopment/smithlab/config/ConfigDefaults.java similarity index 89% rename from src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java rename to src/main/java/org/awdevelopment/smithlab/config/ConfigDefaults.java index c7de056..dbdc94b 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java +++ b/src/main/java/org/awdevelopment/smithlab/config/ConfigDefaults.java @@ -8,10 +8,12 @@ import java.util.HashSet; import java.util.Set; -public class ConfigDefault { +public class ConfigDefaults { public static final byte NUM_DAYS = 0; + public static final Object GUI = Boolean.TRUE; private static final String DEFAULT_FILENAME = "Spreadsheet Formatter Output.xlsx"; + public static final String EMPTY_INPUT_SHEET_FILENAME = "Empty Data Entry Sheet.xlsx"; public static final OutputType OUTPUT_TYPE = OutputType.BOTH; public static final String OUTPUT_FILENAME = DEFAULT_FILENAME; public static final Mode MODE = Mode.GENERATE_OUTPUT_SHEETS; diff --git a/src/main/java/org/awdevelopment/smithlab/config/ConfigManager.java b/src/main/java/org/awdevelopment/smithlab/config/ConfigManager.java new file mode 100644 index 0000000..3c4894e --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/ConfigManager.java @@ -0,0 +1,39 @@ +package org.awdevelopment.smithlab.config; + +import org.awdevelopment.smithlab.logging.LoggerHelper; + +public class ConfigManager { + + public OutputSheetsConfig getOutputSheetsConfig() { return outputSheetsConfig; } + + public EmptyInputSheetConfig getEmptyInputSheetConfig() { return emptyInputSheetConfig; } + + public ImageRecognitionConfig getImageRecognitionConfig() { return imageRecognitionConfig; } + + private final OutputSheetsConfig outputSheetsConfig; + private final EmptyInputSheetConfig emptyInputSheetConfig; + private final ImageRecognitionConfig imageRecognitionConfig; + + public ConfigManager(LoggerHelper LOGGER) { + outputSheetsConfig = new OutputSheetsConfig(LOGGER); + emptyInputSheetConfig = new EmptyInputSheetConfig(LOGGER); + imageRecognitionConfig = new ImageRecognitionConfig(LOGGER); + } + + public void updateConfigValue(Mode mode, ConfigOption configOption, T value) { + switch (mode) { + case Mode.GENERATE_OUTPUT_SHEETS -> outputSheetsConfig.set(configOption, value); + case Mode.GENERATE_EMPTY_INPUT_SHEET -> emptyInputSheetConfig.set(configOption, value); + case Mode.IMAGE_RECOGNITION -> imageRecognitionConfig.set(configOption, value); + } + } + + public T getConfigValue(Mode mode, ConfigOption configOption) { + return switch (mode) { + case Mode.GENERATE_OUTPUT_SHEETS -> outputSheetsConfig.get(configOption); + case Mode.GENERATE_EMPTY_INPUT_SHEET -> emptyInputSheetConfig.get(configOption); + case Mode.IMAGE_RECOGNITION -> imageRecognitionConfig.get(configOption); + }; + } + +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java b/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java new file mode 100644 index 0000000..05e98ea --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java @@ -0,0 +1,83 @@ +package org.awdevelopment.smithlab.config; + +import org.awdevelopment.smithlab.args.Arguments; +import org.awdevelopment.smithlab.data.Condition; +import org.awdevelopment.smithlab.data.Strain; +import org.awdevelopment.smithlab.logging.LoggerHelper; +import java.util.Set; + +public class EmptyInputSheetConfig extends AbstractConfig { + + private static final Mode MODE = Mode.GENERATE_EMPTY_INPUT_SHEET; + + public EmptyInputSheetConfig(LoggerHelper logger) { + super(logger); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, ConfigDefaults.SORT_OPTION)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefaults.NUMBER_OF_REPLICATES)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_FILE, ConfigDefaults.OUTPUT_FILENAME)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.EMPTY_INPUT_SHEET_NAME, ConfigDefaults.EMPTY_INPUT_SHEET_NAME)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.CONDITIONS, ConfigDefaults.CONDITIONS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.STRAINS, ConfigDefaults.STRAINS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.DAYS, ConfigDefaults.DAYS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.INCLUDE_BASELINE_COLUMN, ConfigDefaults.INCLUDE_BASELINE_COLUMN)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.SAMPLE_LABELING_TYPE, ConfigDefaults.SAMPLE_LABELING_TYPE)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_DAYS, ConfigDefaults.NUM_DAYS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_CONDITIONS, ConfigDefaults.NUM_CONDITIONS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_STRAINS, ConfigDefaults.NUM_STRAINS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_DAYS, ConfigDefaults.USING_NUM_DAYS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_CONDITIONS, ConfigDefaults.USING_NUM_CONDITIONS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_STRAINS, ConfigDefaults.USING_NUM_STRAINS)); + } + + public EmptyInputSheetConfig(Arguments args, LoggerHelper logger) { + super(logger); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, args.getOutputSorting())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, args.getReplicateNumber())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.EMPTY_INPUT_SHEET_NAME, args.getEmptyInputSheetName())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.CONDITIONS, args.getConditions())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.STRAINS, args.getStrains())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.DAYS, args.getDays())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.INCLUDE_BASELINE_COLUMN, ConfigDefaults.INCLUDE_BASELINE_COLUMN)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.SAMPLE_LABELING_TYPE, args.getSampleLabelingType())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_DAYS, args.getNumDays())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_CONDITIONS, args.getNumConditions())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_STRAINS, args.getNumStrains())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_DAYS, args.usingNumDays())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_CONDITIONS, args.usingNumConditions())); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_STRAINS, args.usingNumStrains())); + } + + public Set conditions() { return (Set) get(ConfigOption.CONDITIONS); } + public Set strains() { return (Set) get(ConfigOption.STRAINS); } + public byte numReplicates() { return (byte) get(ConfigOption.NUMBER_OF_REPLICATES); } + public String emptyInputSheetName() { return (String) get(ConfigOption.EMPTY_INPUT_SHEET_NAME); } + public boolean includeBaselineColumn() { return (boolean) get(ConfigOption.INCLUDE_BASELINE_COLUMN); } + public boolean usingNumDays() { return (boolean) get(ConfigOption.USING_NUM_DAYS); } + public boolean usingNumConditions() { return (boolean) get(ConfigOption.USING_NUM_CONDITIONS); } + public boolean usingNumStrains() { return (boolean) get(ConfigOption.USING_NUM_STRAINS); } + public byte numDays() { return (byte) get(ConfigOption.NUM_DAYS); } + public byte numConditions() { return (byte) get(ConfigOption.NUM_CONDITIONS); } + public byte numStrains() { return (byte) get(ConfigOption.NUM_STRAINS); } + public Set days() { return (Set) get(ConfigOption.DAYS); } + public SortOption sortOption() { return (SortOption) get(ConfigOption.SORT_OPTION); } + public SampleLabelingType sampleLabelingType() { return (SampleLabelingType) get(ConfigOption.SAMPLE_LABELING_TYPE); } + public void setConditions(Set conditions) { set(ConfigOption.CONDITIONS, conditions); } + public void setStrains(Set strains) { set(ConfigOption.STRAINS, strains); } + public void setNumReplicates(byte numReplicates) { set(ConfigOption.NUMBER_OF_REPLICATES, numReplicates); } + public void setEmptyInputSheetName(String emptyInputSheetName) { set(ConfigOption.EMPTY_INPUT_SHEET_NAME, emptyInputSheetName); } + public void setIncludeBaselineColumn(boolean includeBaselineColumn) { set(ConfigOption.INCLUDE_BASELINE_COLUMN, includeBaselineColumn); } + public void setUsingNumDays(boolean usingNumDays) { set(ConfigOption.USING_NUM_DAYS, usingNumDays); } + public void setUsingNumConditions(boolean usingNumConditions) { set(ConfigOption.USING_NUM_CONDITIONS, usingNumConditions); } + public void setUsingNumStrains(boolean usingNumStrains) { set(ConfigOption.USING_NUM_STRAINS, usingNumStrains); } + public void setNumDays(byte numDays) { set(ConfigOption.NUM_DAYS, numDays); } + public void setNumConditions(byte numConditions) { set(ConfigOption.NUM_CONDITIONS, numConditions); } + public void setNumStrains(byte numStrains) { set(ConfigOption.NUM_STRAINS, numStrains); } + public void setDays(Set days) { set(ConfigOption.DAYS, days); } + public void setSortOption(SortOption sortOption) { set(ConfigOption.SORT_OPTION, sortOption); } + public void setSampleLabelingType(SampleLabelingType sampleLabelingType) { set(ConfigOption.SAMPLE_LABELING_TYPE, sampleLabelingType); } + + @Override + public String toString() { return "Empty Input Sheet Configuration"; } + @Override + public Mode mode() { return MODE; }; +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java b/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java new file mode 100644 index 0000000..b6a7278 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java @@ -0,0 +1,16 @@ +package org.awdevelopment.smithlab.config; + +import org.awdevelopment.smithlab.logging.LoggerHelper; + +public class ImageRecognitionConfig extends AbstractConfig { + + public ImageRecognitionConfig(LoggerHelper logger) { + super(logger); + } + + @Override + public String toString() { return "Image Recognition Configuration"; } + + @Override + public Mode mode() { return Mode.IMAGE_RECOGNITION; } +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/Mode.java b/src/main/java/org/awdevelopment/smithlab/config/Mode.java index 7155246..f884cb6 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/Mode.java +++ b/src/main/java/org/awdevelopment/smithlab/config/Mode.java @@ -1,5 +1,5 @@ package org.awdevelopment.smithlab.config; public enum Mode { - GENERATE_OUTPUT_SHEETS, GENERATE_EMPTY_INPUT_SHEET + GENERATE_OUTPUT_SHEETS, GENERATE_EMPTY_INPUT_SHEET, IMAGE_RECOGNITION } diff --git a/src/main/java/org/awdevelopment/smithlab/config/OutputSheetsConfig.java b/src/main/java/org/awdevelopment/smithlab/config/OutputSheetsConfig.java new file mode 100644 index 0000000..e75e76c --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/OutputSheetsConfig.java @@ -0,0 +1,49 @@ +package org.awdevelopment.smithlab.config; + +import org.awdevelopment.smithlab.args.Arguments; +import org.awdevelopment.smithlab.io.output.formats.OutputType; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +import java.io.File; + +public class OutputSheetsConfig extends AbstractConfig { + + private static final Mode MODE = Mode.GENERATE_OUTPUT_SHEETS; + + public OutputSheetsConfig(Arguments args, LoggerHelper logger) { + super(logger); + addConfigEntry(new ConfigEntry<>(ConfigOption.INPUT_FILE, args.getInputFile())); + addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_FILE, args.getOutputFileName())); + addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_TYPE, args.getOutputType())); + addConfigEntry(new ConfigEntry<>(ConfigOption.WRITE_TO_DIFFERENT_FILE, args.writeToDifferentFile())); + addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, args.getOutputSorting())); + addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, args.getReplicateNumber())); + } + + public OutputSheetsConfig(LoggerHelper logger) { + super(logger); + addConfigEntry(new ConfigEntry<>(ConfigOption.INPUT_FILE, ConfigDefaults.INPUT_FILE)); + addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_FILE, ConfigDefaults.OUTPUT_FILENAME)); + addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_TYPE, ConfigDefaults.OUTPUT_TYPE)); + addConfigEntry(new ConfigEntry<>(ConfigOption.WRITE_TO_DIFFERENT_FILE, ConfigDefaults.WRITE_TO_DIFFERENT_FILE)); + addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, ConfigDefaults.SORT_OPTION)); + addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefaults.NUMBER_OF_REPLICATES)); + } + + public File inputFile() { return get(ConfigOption.INPUT_FILE); } + public String outputFilename() { return get(ConfigOption.OUTPUT_FILE); } + public OutputType outputType() { return get(ConfigOption.OUTPUT_TYPE); } + public boolean writeToDifferentFile() { return get(ConfigOption.WRITE_TO_DIFFERENT_FILE); } + public byte numberOfReplicates() { return get(ConfigOption.NUMBER_OF_REPLICATES); } + public SortOption sortOption() { return get(ConfigOption.SORT_OPTION); } + public void setInputFile(File inputFile) { set(ConfigOption.INPUT_FILE, inputFile); } + public void setOutputFilename(String outputFilename) { set(ConfigOption.OUTPUT_FILE, outputFilename); } + public void setOutputType(OutputType outputType) { set(ConfigOption.OUTPUT_TYPE, outputType); } + public void setWriteToDifferentFile(boolean writeToDifferentFile) { set(ConfigOption.WRITE_TO_DIFFERENT_FILE, writeToDifferentFile); } + public void setNumberOfReplicates(byte numberOfReplicates) { set(ConfigOption.NUMBER_OF_REPLICATES, numberOfReplicates); } + + @Override + public String toString() { return "Output Sheets Configuration"; } + @Override + public Mode mode() { return MODE; } +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigException.java b/src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigException.java new file mode 100644 index 0000000..bdcfb1f --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigException.java @@ -0,0 +1,7 @@ +package org.awdevelopment.smithlab.config.exceptions; + +public abstract class ConfigException extends Exception { + protected ConfigException(String message) { + super(message); + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigOptionNotFoundException.java b/src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigOptionNotFoundException.java new file mode 100644 index 0000000..e7ed6e2 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigOptionNotFoundException.java @@ -0,0 +1,10 @@ +package org.awdevelopment.smithlab.config.exceptions; + +import org.awdevelopment.smithlab.config.AbstractConfig; +import org.awdevelopment.smithlab.config.ConfigOption; + +public class ConfigOptionNotFoundException extends ConfigException { + public ConfigOptionNotFoundException(ConfigOption option, AbstractConfig config) { + super("Config option \"" + option + "\" not found in config \"" + config + "\"."); + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/config/exceptions/WrongObjectTypeException.java b/src/main/java/org/awdevelopment/smithlab/config/exceptions/WrongObjectTypeException.java new file mode 100644 index 0000000..d854f10 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/exceptions/WrongObjectTypeException.java @@ -0,0 +1,10 @@ +package org.awdevelopment.smithlab.config.exceptions; + +import org.awdevelopment.smithlab.config.ConfigEntry; + +public class WrongObjectTypeException extends ConfigException { + public WrongObjectTypeException(ConfigEntry entry, T value) { + super("Wrong object type for " + entry.option().name() + ": Needed type \"" + entry.value().getClass().getName() + + "\" but provided type was: \"" + value.getClass().getName() + "\"."); + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/data/experiment/Experiment.java b/src/main/java/org/awdevelopment/smithlab/data/experiment/Experiment.java index b0bd23c..4979363 100644 --- a/src/main/java/org/awdevelopment/smithlab/data/experiment/Experiment.java +++ b/src/main/java/org/awdevelopment/smithlab/data/experiment/Experiment.java @@ -11,7 +11,6 @@ public class Experiment extends AbstractExperiment { private final Set samples; private Sample[] sortedSamplesAlphabetically; private Sample[] sortedSamplesReverseAlphabetically; - private Sample[] sortedSamplesBySampleNumber; private short[] sortedDayNumbers; private short numReplicates; @@ -33,12 +32,10 @@ public Set getSamples() { } public Sample getSampleNumber(int sampleNumber) { - if (sortedSamplesBySampleNumber == null) { - sortedSamplesBySampleNumber = samples.stream() - .sorted(Comparator.comparing(Sample::getNumber)) - .toArray(Sample[]::new); + for (Sample sample : samples) { + if (sample.getNumber() == sampleNumber) return sample; } - return sortedSamplesBySampleNumber[sampleNumber]; + return null; } public Sample getSampleByAlphabeticalOrder(int sampleNumber) { diff --git a/src/main/java/org/awdevelopment/smithlab/gui/FXMLResourceLoader.java b/src/main/java/org/awdevelopment/smithlab/gui/FXMLResourceLoader.java index dd5746a..a1f2837 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/FXMLResourceLoader.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/FXMLResourceLoader.java @@ -22,7 +22,7 @@ public static FXMLResource load(FXMLResourceType fxmlResource, LoggerHelper LOGG controller = loader.getController(); } catch (IOException e) { LOGGER.atFatal("Failed to load FXML resource: \"" + fxmlResource+"\"", e); - throw new FailedToLoadFXMLException(fxmlResource); + throw new FailedToLoadFXMLException(fxmlResource, e); } return new FXMLResource(scene, controller); } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/FailedToLoadFXMLException.java b/src/main/java/org/awdevelopment/smithlab/gui/FailedToLoadFXMLException.java index 6d2c770..04687fe 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/FailedToLoadFXMLException.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/FailedToLoadFXMLException.java @@ -2,20 +2,20 @@ public class FailedToLoadFXMLException extends RuntimeException { - private FailedToLoadFXMLException(String message) { - super(message); + private FailedToLoadFXMLException(String message, Exception e) { + super(message, e); } - public FailedToLoadFXMLException(FXMLResourceType fxmlResource) { + public FailedToLoadFXMLException(FXMLResourceType fxmlResource, Exception e) { switch (fxmlResource) { case FXMLResourceType.APPLICATION -> - throw new FailedToLoadFXMLException("Failed to load .fxml file \"application.fxml\""); + throw new FailedToLoadFXMLException("Failed to load .fxml file \"application.fxml\"", e); case FXMLResourceType.CONDITIONS -> - throw new FailedToLoadFXMLException("Failed to load .fxml file \"conditions.fxml\""); + throw new FailedToLoadFXMLException("Failed to load .fxml file \"conditions.fxml\"", e); case FXMLResourceType.STRAINS -> - throw new FailedToLoadFXMLException("Failed to load .fxml file \"strains.fxml\""); + throw new FailedToLoadFXMLException("Failed to load .fxml file \"strains.fxml\"", e); case FXMLResourceType.TIMEPOINTS -> - throw new FailedToLoadFXMLException("Failed to load .fxml file \"timepoints.fxml\""); + throw new FailedToLoadFXMLException("Failed to load .fxml file \"timepoints.fxml\"", e); } } } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java b/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java index 485355d..12ed49d 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java @@ -1,7 +1,7 @@ package org.awdevelopment.smithlab.gui; import javafx.stage.Stage; -import org.awdevelopment.smithlab.config.Config; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; import org.awdevelopment.smithlab.gui.controllers.AbstractLabelController; import org.awdevelopment.smithlab.logging.LoggerHelper; @@ -10,15 +10,17 @@ public class SceneLoader { public static void loadScene(Stage stage, FXMLResourceType fxmlResourceType, LoggerHelper logger) { FXMLResource fxmlResource = FXMLResourceLoader.load(fxmlResourceType, logger); fxmlResource.controller().setLogger(logger); + fxmlResource.controller().setup(); stage.setTitle(fxmlResourceType.getWindowTitle()); stage.setScene(fxmlResource.scene()); stage.show(); } - public static AbstractLabelController loadScene(Stage stage, FXMLResourceType fxmlResourceType, LoggerHelper logger, Config config) { + public static AbstractLabelController loadScene(Stage stage, FXMLResourceType fxmlResourceType, LoggerHelper logger, EmptyInputSheetConfig config) { FXMLResource fxmlResource = FXMLResourceLoader.load(fxmlResourceType, logger); fxmlResource.controller().setLogger(logger); ((AbstractLabelController) fxmlResource.controller()).setConfig(config); + fxmlResource.controller().setup(); stage.setTitle(fxmlResourceType.getWindowTitle()); stage.setScene(fxmlResource.scene()); stage.show(); diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractController.java index 42afb4b..a3f9b08 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractController.java @@ -2,12 +2,14 @@ import org.awdevelopment.smithlab.logging.LoggerHelper; -public class AbstractController { +public abstract class AbstractController { private LoggerHelper LOGGER; public AbstractController() {} + public abstract void setup(); + public void setLogger(LoggerHelper LOGGER) { this.LOGGER = LOGGER; } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java index 1dc6060..f627bae 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java @@ -1,18 +1,18 @@ package org.awdevelopment.smithlab.gui.controllers; -import org.awdevelopment.smithlab.config.Config; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; -public class AbstractLabelController extends AbstractController { +public abstract class AbstractLabelController extends AbstractController { - private Config config; + private EmptyInputSheetConfig config; public AbstractLabelController() { super(); } - public void setConfig(Config config) { + public void setConfig(EmptyInputSheetConfig config) { this.config = config; } - public Config config() { + public EmptyInputSheetConfig config() { return config; } } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/ConditionsController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/ConditionsController.java index 02a2be8..2b0a810 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/ConditionsController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/ConditionsController.java @@ -15,6 +15,9 @@ public ConditionsController() { conditions = new HashSet<>(); } + @Override + public void setup() {} + public boolean usingNumConditions() { return usingNumConditions; } public void setUsingNumConditions(boolean usingNumConditions) { diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/MainApplicationController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/MainApplicationController.java deleted file mode 100644 index c3d831d..0000000 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/MainApplicationController.java +++ /dev/null @@ -1,852 +0,0 @@ -package org.awdevelopment.smithlab.gui.controllers; - -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.*; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.HBox; -import javafx.stage.FileChooser; -import javafx.stage.Stage; -import org.awdevelopment.smithlab.config.Config; -import org.awdevelopment.smithlab.config.Mode; -import org.awdevelopment.smithlab.config.SampleLabelingType; -import org.awdevelopment.smithlab.config.SortOption; -import org.awdevelopment.smithlab.data.experiment.Experiment; -import org.awdevelopment.smithlab.gui.FXMLResourceType; -import org.awdevelopment.smithlab.gui.SceneLoader; -import org.awdevelopment.smithlab.io.exceptions.InputFileException; -import org.awdevelopment.smithlab.io.exceptions.NoDaysException; -import org.awdevelopment.smithlab.io.exceptions.OutputException; -import org.awdevelopment.smithlab.io.input.InputReader; -import org.awdevelopment.smithlab.io.output.OutputGenerator; -import org.awdevelopment.smithlab.io.output.formats.OutputType; - -import java.io.File; - -public class MainApplicationController extends AbstractController { - - private final Config config; - - private TimepointsController timepointsController; - private StrainsController strainsController; - private ConditionsController conditionsController; - // EMPTY INPUT SHEET - @FXML - private Label numConditionsErrorLabel; - @FXML - private Label numTimepointsErrorLabel; - @FXML - private Label numStrainErrorLabel; - @FXML - private TextField numConditionsTextField; - @FXML - private TextField numStrainsTextField; - @FXML - private HBox strainsHBox; - @FXML - private HBox conditionsHBox; - @FXML - private TextField numTimepointsTextField; - @FXML - private TextField numReplicatesEmptyInputSheetTextField; - @FXML - private TextField outputFilenameEmptyInputSheetsTextField; - @FXML - private RadioButton conditionLabelingRadioButton; - @FXML - private RadioButton strainLabelingRadioButton; - @FXML - private RadioButton conditionAndStrainLabelingRadioButton; - @FXML - private CheckBox includeBaselineColumnCheckbox; - @FXML - private ChoiceBox sampleSortingMethodEmptyInputSheetChoiceBox; - @FXML - private Label statusLabelEmptyInputSheets; - @FXML - private Label timepointsAddedLabel; - // OUTPUT SHEETS - @FXML - private Label statusLabelOutputSheet; - @FXML - private Label replicatesErrorLabelOutputSheet; - @FXML - private Label outputStyleErrorLabel; - @FXML - private Label outputFilenameErrorLabel; - @FXML - private TabPane tabPane; - @FXML - private Tab outputSheetsTab; - @FXML - private Tab emptyInputSheetTab; - @FXML - private RadioButton outputStylePrismRadioButton; - @FXML - private TextField inputFileTextField; - @FXML - private Button inputFileBrowseButton; - @FXML - private Label inputFileExistsLabel; - @FXML - private RadioButton outputStyleTestsRadioButton; - @FXML - private RadioButton outputStyleRawRadioButton; - @FXML - private RadioButton outputStyleBothRadioButton; - @FXML - private TextField outputFileTextField; - @FXML - private CheckBox addSheetsToInputFileCheckbox; - @FXML - private TextField numReplicatesTextField; - @FXML - private ChoiceBox sampleSortingMethodChoiceBox; - - private RadioButton[] radioButtons; - private RadioButton[] sampleLabelingRadioButtons; - - private boolean failedEmptyReplicates = false; - private boolean failedEmptyOutputFilename = false; - private boolean failedEmptyInputFile = false; - private boolean failedEmptyNumTimepointsEmptyInputSheet = false; - private boolean failedEmptyNumReplicatesEmptyInputSheet = false; - private boolean failedEmptyOutputFilenameEmptyInputSheet = false; - private boolean failedEmptyConditionsEmptyInputSheet = false; - private boolean failedEmptyStrainsEmptyInputSheet = false; - - public MainApplicationController() { - super(); - config = new Config(); - } - - public void initialize() { - radioButtons = new RadioButton[] { - outputStylePrismRadioButton, - outputStyleTestsRadioButton, - outputStyleRawRadioButton, - outputStyleBothRadioButton - }; - sampleLabelingRadioButtons = new RadioButton[] { - conditionLabelingRadioButton, - strainLabelingRadioButton, - conditionAndStrainLabelingRadioButton - }; - sampleSortingMethodChoiceBox.getItems().addAll(SortOption.values()); - sampleSortingMethodChoiceBox.setValue(config.sortOption()); - sampleSortingMethodEmptyInputSheetChoiceBox.getItems().addAll(SortOption.values()); - sampleSortingMethodEmptyInputSheetChoiceBox.setValue(config.sortOption()); - setupErrorLabelsOutputSheet(); - updateFields(); - } - - private void setupErrorLabelsOutputSheet() { - replicatesErrorLabelOutputSheet.setText(""); - outputStyleErrorLabel.setText(""); - outputFilenameErrorLabel.setText(""); - inputFileExistsLabel.setText(""); - statusLabelOutputSheet.setText(""); - replicatesErrorLabelOutputSheet.setStyle(""); - outputStyleErrorLabel.setStyle(""); - outputFilenameErrorLabel.setStyle(""); - inputFileExistsLabel.setStyle(""); - statusLabelOutputSheet.setStyle(""); - } - - public void generateOutput() { - if (config.mode() == Mode.GENERATE_OUTPUT_SHEETS) { - if (notReadyForOutputOutputSheet()) return; - generateOutputSheets(); - } - else if (config.mode() == Mode.GENERATE_EMPTY_INPUT_SHEET) { - if (notReadyForOutputEmptyInputSheet()) return; - generateEmptyInputSheet(); - } - } - - - private void generateOutputSheets() { - getLogger().atDebug("FROM GUI: USER CLICKED GENERATE BUTTON"); - getLogger().atInfo("Generating output..."); - getLogger().atDebug(new String[] { - "Input file: " + config.inputFile().getPath(), - "Output file: " + config.outputFilename(), - "Output type: " + config.outputType(), - "Write to different file: " + config.writeToDifferentFile(), - "Sort option: " + config.sortOption(), - "Number of replicates: " + config.numReplicates(), - "Empty input sheet name: " + config.emptyInputSheetName() - }); - InputReader reader = new InputReader(config, getLogger()); - getLogger().atDebug("Successfully initialized InputReader - reading experiment data..."); - Experiment experiment; - try { - experiment = reader.readExperimentData(); - } catch (InputFileException e) { - errorOccurred(statusLabelOutputSheet, e.getMessage()); - return; - } - getLogger().atDebug("Successfully read experiment data - Initializing OutputGenerator..."); - OutputGenerator outputGenerator; - try { - outputGenerator = new OutputGenerator(config, getLogger()); - } catch (NoDaysException e) { - // SHOULD NEVER HAPPEN - throw new RuntimeException(e); - } - getLogger().atDebug("Successfully initialized OutputGenerator - generating output..."); - try { - outputGenerator.generateOutput(experiment); - } catch (OutputException e) { - errorOccurred(statusLabelOutputSheet, e.getMessage()); - return; - } - getLogger().atInfo("Successfully generated output!"); - failedEmptyInputFile = false; - failedEmptyOutputFilename = false; - failedEmptyReplicates = false; - setupErrorLabelsOutputSheet(); - statusLabelOutputSheet.setText("Successfully generated output!"); - statusLabelOutputSheet.setStyle("-fx-text-fill: green"); - } - - private boolean notReadyForOutputOutputSheet() { - if (sampleSortingMethodChoiceBox.getValue() == null) config.setSortOption(SortOption.NONE); - if (badInputFileField()) return true; - if (badOutputStyleRadioButton()) return true; - if (badOutputFilenameTextField()) return true; - if (badNumReplicatesTextField()) return true; - return false; - } - - private boolean badInputFileField() { - if (!checkInputFile()) return true; - if (inputFileExistsLabel.getText().isEmpty()) { - failedEmptyInputFile = true; - errorOccurred(inputFileExistsLabel, "Error: Please enter an input file path"); - return true; - } - return false; - } - - private boolean badOutputStyleRadioButton() { - if (outputStylePrismRadioButton.isSelected()) { - config.setOutputType(OutputType.PRISM); - } else if (outputStyleTestsRadioButton.isSelected()) { - config.setOutputType(OutputType.STATISTICAL_TESTS); - } else if (outputStyleRawRadioButton.isSelected()) { - config.setOutputType(OutputType.RAW); - } else if (outputStyleBothRadioButton.isSelected()) { - config.setOutputType(OutputType.BOTH); - } else { - errorOccurred(outputStyleErrorLabel, "Error: Please select an output style"); - return true; - } - return false; - } - - private boolean badOutputFilenameTextField() { - if (!checkOutputFilenameOutputSheets()) return true; - String outputFilename = outputFileTextField.getText(); - if (outputFilename.isEmpty() && !addSheetsToInputFileCheckbox.isSelected()) { - failedEmptyOutputFilename = true; - errorOccurred(outputFilenameErrorLabel, "Error: Please enter an output filename"); - return true; - } - return false; - } - - private boolean badNumReplicatesTextField() { - if (!checkNumReplicatesTextField()) { - System.out.println("badNumReplicatesTextField - checkNumReplicatesTextField() returned false"); - return true; - } - if (numReplicatesTextField.getText().isEmpty() - && (config.outputType() == OutputType.STATISTICAL_TESTS - || config.outputType() == OutputType.BOTH)) { - failedEmptyReplicates = true; - errorOccurred(replicatesErrorLabelOutputSheet, "Error: Please enter a number of replicates"); - return true; - } - return false; - } - - private boolean checkInputFile() { - if (inputFileTextField.getText().isEmpty() && !failedEmptyInputFile) { - clearError(inputFileExistsLabel); - return true; - } else if (inputFileTextField.getText().isEmpty()) { - errorOccurred(inputFileExistsLabel, "Error: Please enter an input file path"); - return false; - } - File inputFile = new File(inputFileTextField.getText()); - if (inputFile.exists()) { - if (inputFile.isDirectory()) { - errorOccurred(inputFileExistsLabel, "Error: Input file \"" + inputFileTextField.getText() + "\" is a directory"); - return false; - } else { - inputFileExistsLabel.setText("Input file exists!"); - inputFileExistsLabel.setStyle("-fx-text-fill: green"); - return true; - } - } else { - errorOccurred(inputFileExistsLabel, "Error: Input file \"" + inputFileTextField.getText() + "\" does not exist"); - return false; - } - } - - private boolean checkNumReplicatesTextField() { - if ((numReplicatesTextField.getText().isEmpty() && !failedEmptyReplicates) - || (config.outputType() == OutputType.PRISM || config.outputType() == OutputType.RAW)) { - clearError(replicatesErrorLabelOutputSheet); - return true; - } - else if (numReplicatesTextField.getText().isEmpty() && (config.outputType() == OutputType.STATISTICAL_TESTS || config.outputType() == OutputType.BOTH)) { - errorOccurred(replicatesErrorLabelOutputSheet, "Error: Please enter a number of replicates"); - return false; - } - try { - long numReplicates = Long.parseLong(numReplicatesTextField.getText()); - if (numReplicates < 1) { - errorOccurred(replicatesErrorLabelOutputSheet, "Error: Number must be > 0"); - return false; - } else if (numReplicates > 127) { - errorOccurred(replicatesErrorLabelOutputSheet, "Error: Number must be <= 127"); - return false; - } else { - clearError(replicatesErrorLabelOutputSheet); - config.setNumberOfReplicates((byte) numReplicates); - return true; - } - } catch (NumberFormatException e) { - errorOccurred(replicatesErrorLabelOutputSheet, "Error: Invalid number: \"" + numReplicatesTextField.getText() + "\""); - return false; - } - } - - private boolean checkOutputFilenameOutputSheets() { - if (addSheetsToInputFileCheckbox.isSelected()) { - config.setWriteToDifferentFile(false); - config.setOutputFilename(inputFileTextField.getText()); - return true; - } else return checkOutputFilename(outputFileTextField, failedEmptyOutputFilename, outputFilenameErrorLabel); - } - - public void browseForInputFile() { - getLogger().atDebug("Browsing for input file..."); - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Select Input File (ending in .xlsx)"); - File inputFile = fileChooser.showOpenDialog(inputFileBrowseButton.getScene().getWindow()); - if (inputFile != null) { - getLogger().atDebug("Selected input file: " + inputFile.getAbsolutePath()); - inputFileTextField.setText(inputFile.getAbsolutePath()); - updateInputFile(null); - } - } - - public void handleRadioButtonPress(ActionEvent actionEvent) { - updateFields(); - RadioButton radioButton = (RadioButton) actionEvent.getSource(); - for (RadioButton otherRadioButton : radioButtons) if (!otherRadioButton.equals(radioButton)) otherRadioButton.setSelected(false); - OutputType oldOutputType = config.outputType(); - if (outputStylePrismRadioButton.isSelected()) config.setOutputType(OutputType.PRISM); - else if (outputStyleTestsRadioButton.isSelected()) config.setOutputType(OutputType.STATISTICAL_TESTS); - else if (outputStyleRawRadioButton.isSelected()) config.setOutputType(OutputType.RAW); - else if (outputStyleBothRadioButton.isSelected()) config.setOutputType(OutputType.BOTH); - getLogger().atTrace("Radio button press ActionEvent caught: Switched from OutputType \"" - + oldOutputType +"\" to new OutputType \"" + radioButton.getText() + "\"."); - } - - public void handleAddSheetsCheckbox() { - if (addSheetsToInputFileCheckbox.isSelected()) { - outputFileTextField.setDisable(true); - config.setWriteToDifferentFile(false); - config.setOutputFilename(inputFileTextField.getText()); - if (!outputFilenameErrorLabel.getText().isEmpty()) clearError(outputFilenameErrorLabel); - } else { - outputFileTextField.setDisable(false); - config.setWriteToDifferentFile(true); - config.setOutputFilename(outputFileTextField.getText()); - } - } - - public void updateMode() { - if (tabPane.getSelectionModel().getSelectedItem() == outputSheetsTab) { - config.setMode(Mode.GENERATE_OUTPUT_SHEETS); - } else if (tabPane.getSelectionModel().getSelectedItem() == emptyInputSheetTab) { - config.setMode(Mode.GENERATE_EMPTY_INPUT_SHEET); - } - } - - public void updateFields() { - updateMode(); - updateInputFile(null); - updateOutputFilename(null); - updateSampleSortingMethod(); - } - - public void updateNumReplicates(KeyEvent keyEvent) { - if (numReplicatesTextField.getText().isEmpty() && !failedEmptyReplicates) clearError(replicatesErrorLabelOutputSheet); - if ( keyEvent == null - || !(numReplicatesTextField.getScene().focusOwnerProperty().get().getId().equals("numReplicatesTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumReplicatesTextField(); - } - } - - - public void updateOutputFilename(KeyEvent keyEvent) { - config.setOutputFilename(outputFileTextField.getText()); - if (outputFileTextField.getText().isEmpty() && !failedEmptyOutputFilename) clearError(outputFilenameErrorLabel); - if ( keyEvent == null - || !(outputFileTextField.getScene().focusOwnerProperty().get().getId().equals("outputFileTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkOutputFilenameOutputSheets(); - } - } - - - public void updateSampleSortingMethod() { config.setSortOption(sampleSortingMethodChoiceBox.getValue());} - - public void updateInputFile(KeyEvent keyEvent) { - File inputFile = new File(inputFileTextField.getText()); - config.setInputFile(inputFile); - if (inputFileTextField.getText().isEmpty() && !failedEmptyInputFile) clearError(inputFileExistsLabel); - if ( keyEvent == null - || !(inputFileTextField.getScene().focusOwnerProperty().get().getId().equals("inputFileTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkInputFile(); - } - } - - public void updateOutputReplicatesFields() { - updateOutputFilename(null); - updateNumReplicates(null); - } - - public void updateInputReplicatesFields() { - updateInputFile(null); - updateNumReplicates(null); - } - - public void updateInputOutputFields() { - updateInputFile(null); - updateOutputFilename(null); - } - - private void clearError(Label errorLabel) { - errorLabel.setText(""); - errorLabel.setStyle(""); - } - - private void errorOccurred(Label errorLabel, String errorMessage) { - getLogger().atError(errorMessage); - errorLabel.setText(errorMessage); - errorLabel.setStyle("-fx-text-fill: red"); - } - - private boolean notReadyForOutputEmptyInputSheet() { - if (badNumReplicatesEmptyInputSheet()) return true; - else if (badNumTimepointsEmptyInputSheet()) return true; - else if (badOutputFilenameEmptyInputSheet()) return true; - else if (badSampleLabelingEmptyInputSheet()) return true; - else if (badNumConditionsEmptyInputSheet()) return true; - else if (badNumStrainsEmptyInputSheet()) return true; - else return false; - - } - - private boolean badNumReplicatesEmptyInputSheet() { - if (!checkNumReplicatesEmptyInputSheet()) return true; - if (numReplicatesEmptyInputSheetTextField.getText().isEmpty()) { - failedEmptyNumReplicatesEmptyInputSheet = true; - errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter a number of replicates"); - return true; - } - return false; - } - - private boolean badNumTimepointsEmptyInputSheet() { - if (!checkNumTimepointsEmptyInputSheet()) return true; - if (numTimepointsTextField.getText().isEmpty()) { - failedEmptyNumTimepointsEmptyInputSheet = true; - errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter a number of timepoints"); - return true; - } - return false; - } - - private boolean badOutputFilenameEmptyInputSheet() { - if (!checkOutputFilenameEmptyInputSheets()) return true; - String outputFilename = outputFilenameEmptyInputSheetsTextField.getText(); - if (outputFilename.isEmpty()) { - failedEmptyOutputFilenameEmptyInputSheet = true; - errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter an output filename"); - return true; - } else if (!outputFilename.endsWith(".xlsx")) { - errorOccurred(statusLabelEmptyInputSheets, "Error: Output filename must end in .xlsx"); - return true; - } - return false; - } - - private boolean badSampleLabelingEmptyInputSheet() { - return !checkSampleLabelingRadioButtons(); - } - - private boolean badNumConditionsEmptyInputSheet() { - if (!checkNumConditionsEmptyInputSheet()) return true; - if (numConditionsTextField.getText().isEmpty()) { - failedEmptyConditionsEmptyInputSheet = true; - errorOccurred(numConditionsErrorLabel, "Error: Please enter a number of conditions"); - return true; - } - return false; - } - - private boolean badNumStrainsEmptyInputSheet() { - if (!checkNumStrainsEmptyInputSheet()) return true; - if (numStrainsTextField.getText().isEmpty()) { - failedEmptyStrainsEmptyInputSheet = true; - errorOccurred(numStrainErrorLabel, "Error: Please enter a number of strains"); - return true; - } - return false; - } - - private void generateEmptyInputSheet() { - OutputGenerator outputGenerator; - try { - outputGenerator = new OutputGenerator(config, getLogger()); - } catch (NoDaysException e) { - errorOccurred(statusLabelEmptyInputSheets, e.getMessage()); - return; - } - try { - outputGenerator.generateEmptyInputSheet(); - } catch (OutputException e) { - errorOccurred(statusLabelEmptyInputSheets, e.getMessage()); - return; - } - statusLabelEmptyInputSheets.setText("Successfully generated output!"); - statusLabelEmptyInputSheets.setStyle("-fx-text-fill: green"); - } - - public void emptyInputValidateFields() { - String focusID = (outputFilenameEmptyInputSheetsTextField.getScene().getFocusOwner().getId()); - if (timepointsController != null) { - config.setUsingNumDays(timepointsController.usingNumDays()); - config.setDays(timepointsController.getDays()); - } - if (strainsController != null) { - config.setUsingNumStrains(strainsController.usingNumStrains()); - config.setStrains(strainsController.getStrains()); - } - if (conditionsController != null) { - config.setUsingNumConditions(conditionsController.usingNumConditions()); - config.setConditions(conditionsController.getConditions()); - } - if (focusID == null || focusID.isEmpty()) focusID = "default"; - switch (focusID) { - case "numReplicatesEmptyInputSheetTextField" -> { - checkOutputFilenameEmptyInputSheets(); - checkNumTimepointsEmptyInputSheet(); - checkNumConditionsEmptyInputSheet(); - checkNumStrainsEmptyInputSheet(); - } - case "numTimepointsTextField" -> { - checkOutputFilenameEmptyInputSheets(); - checkNumReplicatesEmptyInputSheet(); - checkNumConditionsEmptyInputSheet(); - checkNumStrainsEmptyInputSheet(); - } - case "outputFilenameEmptyInputSheetsTextField" -> { - checkNumReplicatesEmptyInputSheet(); - checkNumTimepointsEmptyInputSheet(); - checkNumConditionsEmptyInputSheet(); - checkNumStrainsEmptyInputSheet(); - } - case "numConditionsTextField" -> { - checkOutputFilenameEmptyInputSheets(); - checkNumReplicatesEmptyInputSheet(); - checkNumTimepointsEmptyInputSheet(); - checkNumStrainsEmptyInputSheet(); - } - case "numStrainsTextField" -> { - checkOutputFilenameEmptyInputSheets(); - checkNumReplicatesEmptyInputSheet(); - checkNumTimepointsEmptyInputSheet(); - checkNumConditionsEmptyInputSheet(); - } - default -> { - checkNumReplicatesEmptyInputSheet(); - checkOutputFilenameEmptyInputSheets(); - checkNumTimepointsEmptyInputSheet(); - checkNumConditionsEmptyInputSheet(); - checkNumStrainsEmptyInputSheet(); - } - } - } - - private boolean checkNumTimepointsEmptyInputSheet() { - if (timepointsController == null || timepointsController.usingNumDays()) { - if (numTimepointsTextField.getText().isEmpty() && failedEmptyNumTimepointsEmptyInputSheet) { - errorOccurred(numTimepointsErrorLabel, "Error: Please enter a number of timepoints"); - return false; - } else if (numTimepointsTextField.getText().isEmpty()) { - clearError(numTimepointsErrorLabel); - return true; - } else { - try { - long numDays = Long.parseLong(numTimepointsTextField.getText()); - if (numDays < 1) { - errorOccurred(numTimepointsErrorLabel, "Error: Number of timepoints must be > 0"); - return false; - } else if (numDays > 127) { - errorOccurred(numTimepointsErrorLabel, "Error: Number of timepoints must be <= 127"); - return false; - } else { - clearError(numTimepointsErrorLabel); - config.setNumDays((byte) numDays); - return true; - } - } catch (NumberFormatException e) { - errorOccurred(numTimepointsErrorLabel, "Error: Invalid number: \"" + numTimepointsTextField.getText() + "\""); - return false; - } - } - } else { - clearError(numTimepointsErrorLabel); - config.setUsingNumDays(timepointsController.usingNumDays()); - config.setDays(timepointsController.getDays()); - return true; - } - } - - private boolean checkOutputFilenameEmptyInputSheets() { - return checkOutputFilename(outputFilenameEmptyInputSheetsTextField, failedEmptyOutputFilenameEmptyInputSheet, statusLabelEmptyInputSheets); - } - - private boolean checkOutputFilename(TextField textField, boolean failedEmptyBoolean, Label errorLabel) { - if (outputFilenameEmptyInputSheetsTextField.getText().isEmpty() && !failedEmptyOutputFilenameEmptyInputSheet) { - clearError(statusLabelEmptyInputSheets); - return true; - } else if (outputFilenameEmptyInputSheetsTextField.getText().isEmpty()){ - errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter an output filename"); - return false; - } else if (!outputFilenameEmptyInputSheetsTextField.getText().endsWith(".xlsx")) { - errorOccurred(statusLabelEmptyInputSheets, "Error: Output filename must end in .xlsx"); - return false; - } else { - clearError(statusLabelEmptyInputSheets); - return true; - } - } - - private boolean checkNumReplicatesEmptyInputSheet() { - if (numReplicatesEmptyInputSheetTextField.getText().isEmpty() && !failedEmptyNumReplicatesEmptyInputSheet) { - clearError(statusLabelEmptyInputSheets); - return true; - } - else if (numReplicatesEmptyInputSheetTextField.getText().isEmpty()) { - errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter a number of replicates"); - return false; - } - try { - long numReplicates = Long.parseLong(numReplicatesEmptyInputSheetTextField.getText()); - if (numReplicates < 1){ - errorOccurred(statusLabelEmptyInputSheets, "Error: Number must be > 0"); - return false; - } - else if (numReplicates > 127) { - errorOccurred(statusLabelEmptyInputSheets, "Error: Number must be <= 127"); - return false; - } - else { - clearError(statusLabelEmptyInputSheets); - return true; - } - } catch (NumberFormatException e) { - errorOccurred(statusLabelEmptyInputSheets, "Error: Invalid number: \"" + numReplicatesEmptyInputSheetTextField.getText() + "\""); - return false; - } - } - - private boolean checkNumConditionsEmptyInputSheet() { - if (conditionsController == null || conditionsController.usingNumConditions()) { - if (numConditionsTextField.getText().isEmpty() && !failedEmptyConditionsEmptyInputSheet) { - clearError(numConditionsErrorLabel); - return true; - } else if (numConditionsTextField.getText().isEmpty()) { - errorOccurred(numConditionsErrorLabel, "Error: Please enter a number of conditions"); - return false; - } else { - try { - long numConditions = Long.parseLong(numConditionsTextField.getText()); - if (numConditions < 1) { - errorOccurred(numConditionsErrorLabel, "Error: Number of conditions must be > 0"); - return false; - } else if (numConditions > 127) { - errorOccurred(numConditionsErrorLabel, "Error: Number of conditions must be <= 127"); - return false; - } else { - clearError(numConditionsErrorLabel); - config.setNumConditions((byte) numConditions); - return true; - } - } catch (NumberFormatException e) { - errorOccurred(numConditionsErrorLabel, "Error: Invalid number: \"" + numConditionsTextField.getText() + "\""); - return false; - } - } - } else { - clearError(numConditionsErrorLabel); - config.setUsingNumConditions(conditionsController.usingNumConditions()); - config.setConditions(conditionsController.getConditions()); - return true; - } - } - private boolean checkNumStrainsEmptyInputSheet() { - if (strainsController == null || strainsController.usingNumStrains()) { - if (numStrainsTextField.getText().isEmpty() && !failedEmptyStrainsEmptyInputSheet) { - clearError(numStrainErrorLabel); - return true; - } else if (numStrainsTextField.getText().isEmpty()) { - errorOccurred(numStrainErrorLabel, "Error: Please enter a number of strains"); - return false; - } else { - try { - long numStrains = Long.parseLong(numStrainsTextField.getText()); - if (numStrains < 1) { - errorOccurred(numStrainErrorLabel, "Error: Number of strains must be > 0"); - return false; - } else if (numStrains > 127) { - errorOccurred(numStrainErrorLabel, "Error: Number of strains must be <= 127"); - return false; - } else { - clearError(numStrainErrorLabel); - config.setNumStrains((byte) numStrains); - return true; - } - } catch (NumberFormatException e) { - errorOccurred(numStrainErrorLabel, "Error: Invalid number: \"" + numStrainsTextField.getText() + "\""); - return false; - } - } - } else { - clearError(numStrainErrorLabel); - config.setUsingNumStrains(strainsController.usingNumStrains()); - config.setStrains(strainsController.getStrains()); - return true; - } - } - - private boolean checkSampleLabelingRadioButtons() { - if (!conditionLabelingRadioButton.isSelected() && !strainLabelingRadioButton.isSelected() && !conditionAndStrainLabelingRadioButton.isSelected()) { - errorOccurred(statusLabelEmptyInputSheets, "Error: Please select a sample labeling method"); - return false; - } - return true; - } - - - public void updateSampleLabelingRadioButtons(ActionEvent actionEvent) { - emptyInputValidateFields(); - RadioButton selectedRadioButton = (RadioButton) actionEvent.getSource(); - for (RadioButton radioButton : sampleLabelingRadioButtons) if (!radioButton.equals(selectedRadioButton)) radioButton.setSelected(false); - switch (selectedRadioButton.getId()) { - case "conditionLabelingRadioButton" -> { - config.setSampleLabelingType(SampleLabelingType.CONDITION); - strainsHBox.setDisable(true); - conditionsHBox.setDisable(false); - } - case "strainLabelingRadioButton" -> { - config.setSampleLabelingType(SampleLabelingType.STRAIN); - strainsHBox.setDisable(false); - conditionsHBox.setDisable(true); - } - case "conditionAndStrainLabelingRadioButton" -> { - config.setSampleLabelingType(SampleLabelingType.CONDITION_AND_STRAIN); - strainsHBox.setDisable(false); - conditionsHBox.setDisable(false); - } - } - checkSampleLabelingRadioButtons(); - } - - public void openConditionsFXML() { - emptyInputValidateFields(); - SceneLoader.loadScene(new Stage(), FXMLResourceType.CONDITIONS, getLogger(), config); - } - - public void openStrainsFXML() { - emptyInputValidateFields(); - SceneLoader.loadScene(new Stage(), FXMLResourceType.STRAINS, getLogger(), config); - } - - public void openTimepointsFXML() { - emptyInputValidateFields(); - Stage stage = new Stage(); - timepointsController = (TimepointsController) SceneLoader.loadScene(stage, FXMLResourceType.TIMEPOINTS, getLogger(), config); - } - - public void updateNumReplicateEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if (numReplicatesEmptyInputSheetTextField.getText().isEmpty() && !failedEmptyNumReplicatesEmptyInputSheet) clearError(statusLabelEmptyInputSheets); - if ( keyEvent == null - || !(numReplicatesEmptyInputSheetTextField.getScene().focusOwnerProperty().get().getId().equals("numReplicatesEmptyInputSheetTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumReplicatesEmptyInputSheet(); - } - } - - public void updateNumTimepointsEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if (numTimepointsTextField.getText().isEmpty() && !failedEmptyNumTimepointsEmptyInputSheet) - clearError(statusLabelEmptyInputSheets); - if (keyEvent == null - || !(numTimepointsTextField.getScene().focusOwnerProperty().get().getId().equals("numTimepointsTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumTimepointsEmptyInputSheet(); - } - } - - public void updateNumConditionsEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if(numConditionsTextField.getText().isEmpty() && !failedEmptyConditionsEmptyInputSheet) clearError(statusLabelEmptyInputSheets); - if ( keyEvent == null - || !(numConditionsTextField.getScene().focusOwnerProperty().get().getId().equals("numConditionsTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumConditionsEmptyInputSheet(); - } - } - - public void updateNumStrainsEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if(numStrainsTextField.getText().isEmpty() && !failedEmptyStrainsEmptyInputSheet) clearError(statusLabelEmptyInputSheets); - if ( keyEvent == null - || !(numStrainsTextField.getScene().focusOwnerProperty().get().getId().equals("numStrainsTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumStrainsEmptyInputSheet(); - } - } - - public void handleIncludeBaselineColumn() { - emptyInputValidateFields(); - config.setIncludeBaselineColumn(includeBaselineColumnCheckbox.isSelected()); } -} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/StrainsController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/StrainsController.java index d9107d8..8445f7b 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/StrainsController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/StrainsController.java @@ -15,6 +15,9 @@ public StrainsController() { strains = new HashSet<>(); } + @Override + public void setup() {} + public boolean usingNumStrains() { return usingNumStrains; } public void setUsingNumStrains(boolean usingNumStrains) { this.usingNumStrains = usingNumStrains; } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/TimepointsController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/TimepointsController.java index 5918965..3860aa1 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/TimepointsController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/TimepointsController.java @@ -13,6 +13,9 @@ public TimepointsController() { days = new HashSet<>(); } + @Override + public void setup() {} + public boolean usingNumDays() { return usingNumDays; } private void addDay(byte day) { days.add(day); } private void removeDay(byte day) { days.remove(day); } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractConfigUpdater.java new file mode 100644 index 0000000..7d86867 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractConfigUpdater.java @@ -0,0 +1,141 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.Control; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import org.awdevelopment.smithlab.config.AbstractConfig; +import org.awdevelopment.smithlab.config.ConfigOption; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldStatus; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.ValidatableField; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +import java.io.File; + +public abstract class AbstractConfigUpdater { + + private final AbstractConfig config; + private final AbstractValidator validator; + private final AbstractFields fields; + LoggerHelper LOGGER() { return LOGGER; } + private final LoggerHelper LOGGER; + + AbstractConfigUpdater(AbstractConfig config, AbstractValidator validator, AbstractFields fields) { + this.config = config; + this.validator = validator; + this.fields = fields; + this.LOGGER = config.LOGGER(); + } + + public abstract void updateFields(); + + public abstract void updateSampleSortingMethod(); + + void updateTextField(ValidatableField validatableField, ConfigOption option, KeyEvent keyEvent) { + // If doing normal typing in properly marked field, exit early + Control field; TextField textField; + try { + field = fields.getControlByIDAndMode(validatableField.getControlID(), config.mode()); + textField = getTextField(validatableField); + } catch (IllegalFieldAccessException | ClassCastException e) { + LOGGER.atFatal("Error while trying to update text field with id \"" + validatableField.getControlID() + "\": ", e); + LOGGER.atFatal(e.getMessage()); + System.exit(1); + return; + } + boolean fieldIsFocused = field.isFocused(); + if (validatableField.status() == FieldStatus.EDITED_NOT_VALIDATED && (fieldIsFocused + && !(((TextField) field).getText().isEmpty()) && keyEvent != null && keyEvent.getCode() != KeyCode.ENTER + && keyEvent.getCode() != KeyCode.TAB)) return; + LOGGER.atDebug(""); + LOGGER.atDebug("Begin process of updating stored value for field with id: \"" + validatableField.getControlID() + "\"..."); + if (fieldIsFocused) LOGGER.atDebug("Field is focused."); + else LOGGER.atDebug("Field is not focused."); + switch (validatableField.status()) { + case EDITED_NOT_VALIDATED -> { + LOGGER.atDebug("Field status: Edited but not validated."); + if (!fieldIsFocused) { + LOGGER.atDebug("Validating..."); + validator.validateTextFieldByID(validatableField.getControlID()); + updateIfValidated(validatableField, option); + } else if (textField.getText().isEmpty()) { + LOGGER.atDebug("Field is empty, setting field status to empty."); + validatableField.setStatus(FieldStatus.EMPTY); + } + } + case EMPTY -> { + LOGGER.atDebug("Field status: Empty."); + if (fieldIsFocused) { + if (keyEvent != null && !keyEvent.getText().isEmpty()) { + LOGGER.atDebug("Key event is not null and is non-empty, setting field status to edited but not validated."); + validatableField.setStatus(FieldStatus.EDITED_NOT_VALIDATED); + } else { + LOGGER.atDebug("Key event is null or empty, field status remains empty."); + validatableField.setStatus(FieldStatus.EMPTY); + } + } else { + LOGGER.atDebug("Field is empty. Running validation to produce error message (field status will be set to invalid)."); + validator.validateTextFieldByID(validatableField.getControlID()); + } + } + case INVALID -> { + LOGGER.atDebug("Field status: Invalid."); + if (!fieldIsFocused) LOGGER.atDebug("Field will remain invalid."); + else if (keyEvent != null && (!keyEvent.getText().isEmpty() || keyEvent.getCode() == KeyCode.BACK_SPACE || keyEvent.getCode() == KeyCode.DELETE)) { + LOGGER.atDebug("Key event is not null and is non-empty or is a backspace/delete, setting field status to edited but not validated."); + validatableField.setStatus(FieldStatus.EDITED_NOT_VALIDATED); + } else LOGGER.atDebug("Key event is null or empty, field status remains invalid."); + } + case READY -> { + LOGGER.atDebug("Field status: Ready."); + if (fieldIsFocused) { + if (keyEvent != null && (!keyEvent.getText().isEmpty() || keyEvent.getCode() == KeyCode.BACK_SPACE || keyEvent.getCode() == KeyCode.DELETE)) { + LOGGER.atDebug("Key event is not null and is non-empty or is a backspace/delete, setting field status to edited but not validated."); + validatableField.setStatus(FieldStatus.EDITED_NOT_VALIDATED); + } else { + LOGGER.atDebug("Key event is null or empty, field status remains ready."); + validatableField.setStatus(FieldStatus.READY); + } + } else LOGGER.atDebug("Field is not focused, skipping validation as field is ready..."); + } + case UNTOUCHED -> { + LOGGER.atDebug("Field status: Untouched."); + if (fieldIsFocused) { + if (keyEvent != null && !keyEvent.getText().isEmpty()) { + LOGGER.atDebug("Key event is not null and is non-empty, setting field status to edited but not validated."); + validatableField.setStatus(FieldStatus.EDITED_NOT_VALIDATED); + } else { + LOGGER.atDebug("Key event is null or empty, field status remains untouched."); + validatableField.setStatus(FieldStatus.UNTOUCHED); + } + } else LOGGER.atDebug("Field is not focused, skipping validation as field is untouched..."); + } + } + } + + void updateIfValidated(ValidatableField field, ConfigOption option) { + LOGGER.atDebug("Checking if field was validated successfully..."); + if (field.status() == FieldStatus.READY) { + LOGGER.atDebug("Field was validated successfully, updating stored value..."); + switch (field.getFieldType()) { + case BYTE -> config.set(option, Byte.parseByte(getTextField(field).getText())); + case FILENAME, STRING -> config.set(option, getTextField(field).getText()); + case EXISTING_FILE -> config.set(option, new File(getTextField(field).getText())); + default -> throw new IllegalStateException("Unexpected value: " + field.getFieldType()); + } + LOGGER.atDebug("Stored value updated successfully."); + } else { + LOGGER.atDebug("Field was not validated successfully, not updating stored value."); + } + } + + TextField getTextField(ValidatableField field) { + try { + return (TextField) fields.getControlByIDAndMode(field.getControlID(), config.mode()); + } catch (IllegalFieldAccessException e) { + LOGGER.atFatal("Error while trying to get text field with id \"" + field.getControlID() + "\": ", e); + System.exit(1); + return null; + } + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java new file mode 100644 index 0000000..d088b14 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java @@ -0,0 +1,47 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.*; +import org.awdevelopment.smithlab.config.Mode; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.ValidatableField; + +public abstract class AbstractFields { + + private final MainApplicationController controller; + private final ValidatableField[] validatableFields; + + AbstractFields(MainApplicationController controller, ValidatableField[] validatableFields) { + this.controller = controller; + this.validatableFields = validatableFields; + } + + + protected Control getControlByIDAndMode(String id, Mode mode) throws IllegalFieldAccessException { + if (mode == Mode.GENERATE_OUTPUT_SHEETS) { + return switch (id) { + case "statusLabelOutputSheet", "replicatesErrorLabelOutputSheet", "outputStyleErrorLabel", + "outputFilenameErrorLabel", "tabPane", "outputSheetsTab", "emptyInputSheetTab", + "outputStylePrismRadioButton", "inputFileTextField", "inputFileBrowseButton", + "inputFileExistsLabel", "outputStyleTestsRadioButton", "outputStyleRawRadioButton", + "outputStyleBothRadioButton", "outputFileTextField", "addSheetsToInputFileCheckbox", + "numReplicatesTextField", "sampleSortingMethodChoiceBox" -> controller.getControlByID(id); + default -> throw new IllegalFieldAccessException(mode, id); + }; + } else if (mode == Mode.GENERATE_EMPTY_INPUT_SHEET) { + return switch (id) { + case "numConditionsErrorLabel", "numTimepointsErrorLabel", "numStrainErrorLabel", "tabPane", + "outputSheetsTab", "emptyInputSheetTab", "numReplicatesErrorLabelEmptyInputSheet", + "numConditionsTextField", "numStrainsTextField", "strainsHBox", "conditionsHBox", + "numTimepointsTextField", "numReplicatesEmptyInputSheetTextField", + "outputFilenameEmptyInputSheetTextField", "conditionLabelingRadioButton", + "strainLabelingRadioButton", "conditionAndStrainLabelingRadioButton", + "includeBaselineColumnCheckbox", "sampleSortingMethodEmptyInputSheetChoiceBox", + "statusLabelEmptyInputSheet", "timepointsAddedLabel" -> controller.getControlByID(id); + default -> throw new IllegalFieldAccessException(mode, id); + }; + + } + return controller.getControlByID(id); + } + + public ValidatableField[] getValidatableFields() { return validatableFields; } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java new file mode 100644 index 0000000..7419f48 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java @@ -0,0 +1,142 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.TextField; +import org.awdevelopment.smithlab.config.Mode; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldStatus; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldType; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.ValidatableField; +import org.awdevelopment.smithlab.logging.GUILogger; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +import java.io.File; + +public abstract class AbstractValidator { + + private final Mode mode; + public LoggerHelper LOGGER() { return LOGGER; } + private final LoggerHelper LOGGER; + private final GUILogger guiLogger; + private final AbstractFields fields; + private final ValidatableField[] validatableFields; + + protected AbstractValidator(LoggerHelper logger, GUILogger guiLogger, AbstractFields fields, Mode mode) { + this.guiLogger = guiLogger; + this.validatableFields = fields.getValidatableFields(); + this.fields = fields; + this.LOGGER = logger; + this.mode = mode; + } + + public void preliminaryFieldsValid() { + LOGGER.atTrace("Checking if all fields are valid (empty/untouched fields are okay)..."); + for (ValidatableField field : validatableFields) { + if (field.status() == FieldStatus.EDITED_NOT_VALIDATED) validateTextFieldByID(field.getControlID()); + } + } + + public boolean fieldsValid() { + LOGGER.atDebug("Checking if all fields are valid..."); + for (ValidatableField field : validatableFields) { + if (field.status() == FieldStatus.UNTOUCHED && (field.getFieldType() == FieldType.BYTE + || field.getFieldType() == FieldType.STRING || field.getFieldType() == FieldType.FILENAME + || field.getFieldType() == FieldType.EXISTING_FILE)) { + field.setStatus(FieldStatus.EMPTY); + validateTextFieldByID(field.getControlID()); + } else if (field.status() == FieldStatus.EDITED_NOT_VALIDATED) { + validateTextFieldByID(field.getControlID()); + } + if (field.status() == FieldStatus.INVALID || field.status() == FieldStatus.EMPTY) { + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" is invalid or empty."); + LOGGER.atDebug("Field status: " + field.status()); + LOGGER.atDebug("Output will not be generated!"); + return false; + } + } + return true; + } + + abstract void validateTextFieldByID(String id); + + void validateTextFieldNotEmpty(ValidatableField field) { + if (field.status() == FieldStatus.UNTOUCHED) return; + TextField textField = getTextField(field); + if (textField.getText().isEmpty()) { + field.setStatus(FieldStatus.INVALID); + guiLogger.errorOccurred(field.getErrorLabel(), "Error: Please enter a value"); + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" is empty."); + } else { + guiLogger.clearError(field.getErrorLabel()); + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" is not empty."); + field.setStatus(FieldStatus.READY); + } + } + + void validateTextFieldFileExists(ValidatableField field) { + if (field.status() == FieldStatus.UNTOUCHED) return; + validateTextFieldFilename(field); + TextField textField = getTextField(field); + String filePath = textField.getText(); + File file = new File(filePath); + if (!file.exists()) { + guiLogger.errorOccurred(field.getErrorLabel(), "Error: File does not exist"); + LOGGER.atDebug("File with path: \"" + filePath + "\" does not exist."); + field.setStatus(FieldStatus.INVALID); + } else { + guiLogger.clearError(field.getErrorLabel()); + LOGGER.atDebug("File with path: \"" + filePath + "\" exists."); + field.setStatus(FieldStatus.READY); + } + } + + void validateTextFieldFilename(ValidatableField field) { + if (field.status() == FieldStatus.UNTOUCHED) return; + validateTextFieldNotEmpty(field); + TextField textField = getTextField(field); + if (!textField.getText().endsWith(".xlsx")) { + guiLogger.errorOccurred(field.getErrorLabel(), "Error: Filename must end in .xlsx"); + LOGGER.atDebug("Filename: \"" + textField.getText() + "\" does not end in .xlsx."); + field.setStatus(FieldStatus.INVALID); + } else { + guiLogger.clearError(field.getErrorLabel()); + LOGGER.atDebug("Filename: \"" + textField.getText() + "\" ends in .xlsx."); + field.setStatus(FieldStatus.READY); + } + } + + void validateTextFieldByte(ValidatableField field) { + if (field.status() == FieldStatus.UNTOUCHED) return; + validateTextFieldNotEmpty(field); + TextField textField = getTextField(field); + try { + long longValue = Long.parseLong(textField.getText()); + if (longValue <= 0) { + guiLogger.errorOccurred(field.getErrorLabel(), "Error: Must be a value > 0"); + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" is <= 0."); + field.setStatus(FieldStatus.INVALID); + } else if (longValue > Byte.MAX_VALUE) { + guiLogger.errorOccurred(field.getErrorLabel(), "Error: Must be a value <= " + Byte.MAX_VALUE); + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" is > " + Byte.MAX_VALUE + "."); + field.setStatus(FieldStatus.INVALID); + } else { + guiLogger.clearError(field.getErrorLabel()); + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" has a valid positive byte-value."); + field.setStatus(FieldStatus.READY); + } + } catch (NumberFormatException e) { + guiLogger.errorOccurred(field.getErrorLabel(), "Error: Please enter a valid number"); + LOGGER.atDebug("Field with id: \"" + field.getControlID() + "\" is not a valid number: \"" + textField.getText() + "\""); + field.setStatus(FieldStatus.INVALID); + } + } + + TextField getTextField(ValidatableField field) { + try { + return (TextField) fields.getControlByIDAndMode(field.getControlID(), mode); + } catch (IllegalFieldAccessException e) { + LOGGER.atError("Error occurred while validating text field with id: \"" + field.getControlID() + "\"", e); + System.exit(1); + return null; + } + } + +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java new file mode 100644 index 0000000..43c5455 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java @@ -0,0 +1,135 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.event.ActionEvent; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.RadioButton; +import javafx.scene.input.KeyEvent; +import org.awdevelopment.smithlab.config.ConfigOption; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; +import org.awdevelopment.smithlab.config.SampleLabelingType; +import org.awdevelopment.smithlab.config.SortOption; + +public class EmptyInputSheetConfigUpdater extends AbstractConfigUpdater { + + private final EmptyInputSheetConfig config; + private final EmptyInputSheetFields fields; + private final EmptyInputSheetValidator validator; + + EmptyInputSheetConfigUpdater(MainApplicationController controller, EmptyInputSheetConfig config) { + super(config, controller.emptyInputSheetValidator, controller.emptyInputSheetFields); + this.config = config; + this.fields = controller.emptyInputSheetFields; + this.validator = controller.emptyInputSheetValidator; + } + + public void updateFields() { + updateNumConditions(null); + updateNumReplicates(null); + updateNumStrains(null); + updateNumTimepoints(null); + updateOutputFilename(null); + updateSampleSortingMethod(); + } + + public void updateOutputFilename(KeyEvent keyEvent) { + updateTextField(fields.getOutputFilenameTextField(), ConfigOption.OUTPUT_FILE, keyEvent); + } + + public void updateNumReplicates(KeyEvent keyEvent) { + updateTextField(fields.getNumReplicatesTextField(), ConfigOption.NUMBER_OF_REPLICATES, keyEvent); + } + + public void updateNumTimepoints(KeyEvent keyEvent) { + updateTextField(fields.getNumTimepointsTextField(), ConfigOption.NUM_DAYS, keyEvent); + } + + public void updateNumConditions(KeyEvent keyEvent) { + updateTextField(fields.getNumConditionsTextField(), ConfigOption.NUM_CONDITIONS, keyEvent); + } + + public void updateNumStrains(KeyEvent keyEvent) { + updateTextField(fields.getNumStrainsTextField(), ConfigOption.NUM_STRAINS, keyEvent); + } + + public void updateSampleLabelingRadioButtons(ActionEvent actionEvent) { + RadioButton selectedRadioButton = actionEvent.getSource() instanceof RadioButton ? (RadioButton) actionEvent.getSource() : null; + if (selectedRadioButton == null) { + RadioButton previousSelectedRadioButton = + switch (config.sampleLabelingType()) { + case CONDITION -> fields.getConditionLabelingRadioButton(); + case STRAIN -> fields.getStrainLabelingRadioButton(); + case CONDITION_AND_STRAIN -> fields.getConditionAndStrainLabelingRadioButton(); + }; + RadioButton[] selectedRadioButtons = new RadioButton[3]; + if (fields.getConditionLabelingRadioButton().isSelected()) selectedRadioButtons[0] = fields.getConditionLabelingRadioButton(); + if (fields.getStrainLabelingRadioButton().isSelected()) selectedRadioButtons[1] = fields.getStrainLabelingRadioButton(); + if (fields.getConditionAndStrainLabelingRadioButton().isSelected()) selectedRadioButtons[2] = fields.getConditionAndStrainLabelingRadioButton(); + if (selectedRadioButtons[0] == null && selectedRadioButtons[1] == null && selectedRadioButtons[2] == null) { + LOGGER().atWarn("No radio button selected, setting previous radio button to selected."); + selectedRadioButton = previousSelectedRadioButton; + } else if (!(selectedRadioButtons[0] != null ^ selectedRadioButtons[1] != null ^ selectedRadioButtons[2] != null)) { + LOGGER().atFatal("ERROR: More than one radio button selected, exiting..."); + System.exit(1); + return; + } else { + for (RadioButton radioButton : selectedRadioButtons) { + if (radioButton != null) { + selectedRadioButton = radioButton; + break; + } + } + if (selectedRadioButton == null) { + // This should never happen + LOGGER().atFatal("ERROR: No radio button selected, exiting..."); + System.exit(1); + return; + } + } + } + RadioButton[] radioButtons = new RadioButton[] { fields.getConditionLabelingRadioButton(), fields.getStrainLabelingRadioButton(), fields.getConditionAndStrainLabelingRadioButton() }; + for (RadioButton radioButton : radioButtons) { + if (radioButton.getId().equals(selectedRadioButton.getId())) { + selectedRadioButton.setSelected(true); + continue; + } + radioButton.setSelected(false); + } + switch (selectedRadioButton.getId()) { + case "conditionLabelingRadioButton" -> { + config.setSampleLabelingType(SampleLabelingType.CONDITION); + fields.getStrainsHBox().setDisable(true); + fields.getConditionsHBox().setDisable(false); + } + case "strainLabelingRadioButton" -> { + config.setSampleLabelingType(SampleLabelingType.STRAIN); + fields.getStrainsHBox().setDisable(false); + fields.getConditionsHBox().setDisable(true); + } + case "conditionAndStrainLabelingRadioButton" -> { + config.setSampleLabelingType(SampleLabelingType.CONDITION_AND_STRAIN); + fields.getStrainsHBox().setDisable(false); + fields.getConditionsHBox().setDisable(false); + } + } + validator.validateSampleLabelingRadioButtons(); + } + + @SuppressWarnings("unchecked") + public void updateSampleSortingMethod() { + String id = "sampleSortingMethodEmptyInputSheetChoiceBox"; + ChoiceBox choiceBox; + try { + choiceBox = (ChoiceBox) fields.getControlByIDAndMode(id, config.mode()); + } catch (IllegalFieldAccessException e) { + LOGGER().atFatal("Error occurred while updating sample sorting method choice box.", e); + LOGGER().atFatal(e.getMessage()); + System.exit(1); + return; + } + config.setSortOption(choiceBox.getValue()); + } + + public void handleIncludeBaselineColumn() { + config.setIncludeBaselineColumn(fields.getIncludeBaselineColumnCheckbox().isSelected()); + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java new file mode 100644 index 0000000..33dd560 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java @@ -0,0 +1,70 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import org.awdevelopment.smithlab.gui.controllers.ConditionsController; +import org.awdevelopment.smithlab.gui.controllers.StrainsController; +import org.awdevelopment.smithlab.gui.controllers.TimepointsController; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldType; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.ValidatableField; + +public class EmptyInputSheetFields extends AbstractFields { + + private final MainApplicationController controller; + + private final ValidatableField numTimepointsTextField; + private final ValidatableField numConditionsTextField; + private final ValidatableField numStrainsTextField; + private final ValidatableField numReplicatesTextField; + private final ValidatableField outputFilenameTextField; + private final ValidatableField sampleLabelingRadioButtons; + private final ValidatableField sampleSortingMethodChoiceBox; + + EmptyInputSheetFields(MainApplicationController controller) { + super(controller, new ValidatableField[] { new ValidatableField(controller.numTimepointsTextField, controller.timepointsAddedLabel, FieldType.BYTE), + new ValidatableField(controller.numConditionsTextField, controller.numConditionsErrorLabel, FieldType.BYTE), + new ValidatableField(controller.numStrainsTextField, controller.numStrainErrorLabel, FieldType.BYTE), + new ValidatableField(controller.numReplicatesEmptyInputSheetTextField, controller.numReplicatesErrorLabelEmptyInputSheet, FieldType.BYTE), + new ValidatableField(controller.outputFilenameEmptyInputSheetTextField, controller.statusLabelEmptyInputSheet, FieldType.FILENAME), + new ValidatableField(controller.sampleSortingMethodEmptyInputSheetChoiceBox, controller.statusLabelEmptyInputSheet, FieldType.CHOICE_BOX), + new ValidatableField(controller.sampleLabelingRadioButtons, controller.statusLabelEmptyInputSheet, FieldType.RADIO_BUTTONS) }); + this.numTimepointsTextField = super.getValidatableFields()[0]; + this.numConditionsTextField = super.getValidatableFields()[1]; + this.numStrainsTextField = super.getValidatableFields()[2]; + this.numReplicatesTextField = super.getValidatableFields()[3]; + this.outputFilenameTextField = super.getValidatableFields()[4]; + this.sampleSortingMethodChoiceBox = super.getValidatableFields()[5]; + this.sampleLabelingRadioButtons = super.getValidatableFields()[6]; + this.controller = controller; + + } + + public ValidatableField getNumTimepointsTextField() { return numTimepointsTextField; } + public ValidatableField getNumConditionsTextField() { return numConditionsTextField; } + public ValidatableField getNumStrainsTextField() { return numStrainsTextField; } + public ValidatableField getNumReplicatesTextField() { return numReplicatesTextField; } + public ValidatableField getOutputFilenameTextField() { return outputFilenameTextField; } + public ValidatableField getSampleSortingMethodChoiceBox() { return sampleSortingMethodChoiceBox; } + public ValidatableField getSampleLabelingRadioButtons() { return sampleLabelingRadioButtons; } + public Label getNumConditionsErrorLabel() { return controller.numConditionsErrorLabel; } + public Label getNumTimepointsErrorLabel() { return controller.numTimepointsErrorLabel; } + public Label getNumStrainErrorLabel() { return controller.numStrainErrorLabel; } + public Label getNumReplicatesErrorLabel() { return controller.numReplicatesErrorLabelEmptyInputSheet; } + public HBox getStrainsHBox() { return controller.strainsHBox; } + public HBox getConditionsHBox() { return controller.conditionsHBox; } + public RadioButton getConditionLabelingRadioButton() { return controller.conditionLabelingRadioButton; } + public RadioButton getStrainLabelingRadioButton() { return controller.strainLabelingRadioButton; } + public RadioButton getConditionAndStrainLabelingRadioButton() { return controller.conditionAndStrainLabelingRadioButton; } + public CheckBox getIncludeBaselineColumnCheckbox() { return controller.includeBaselineColumnCheckbox; } + public Label getStatusLabel() { return controller.statusLabelEmptyInputSheet; } + public Label getTimepointsAddedLabel() { return controller.timepointsAddedLabel; } + public TimepointsController getTimepointsController() { return controller.timepointsController; } + public StrainsController getStrainsController() { return controller.strainsController; } + public ConditionsController getConditionsController() { return controller.conditionsController; } + public RadioButton getSelectedRadioButton() { + String[] radioButtonIDs = new String[] { "conditionLabelingRadioButton", "strainLabelingRadioButton", "conditionAndStrainLabelingRadioButton" }; + for (String radioButtonID : radioButtonIDs) + if (((RadioButton) controller.getControlByID(radioButtonID)).isSelected()) return (RadioButton) controller.getControlByID(radioButtonID); + return null; + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java new file mode 100644 index 0000000..ba56d78 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java @@ -0,0 +1,105 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; +import org.awdevelopment.smithlab.gui.controllers.AbstractLabelController; +import org.awdevelopment.smithlab.gui.controllers.ConditionsController; +import org.awdevelopment.smithlab.gui.controllers.StrainsController; +import org.awdevelopment.smithlab.gui.controllers.TimepointsController; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldStatus; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.ValidatableField; +import org.awdevelopment.smithlab.logging.GUILogger; + +public class EmptyInputSheetValidator extends AbstractValidator { + + private final EmptyInputSheetFields fields; + private final GUILogger guiLogger; + private final EmptyInputSheetConfig config; + + public EmptyInputSheetValidator(EmptyInputSheetFields fields, GUILogger guiLogger, EmptyInputSheetConfig config) { + super(config.LOGGER(), guiLogger, fields, config.mode()); + this.fields = fields; + this.guiLogger = guiLogger; + this.config = config; + } + + void validateNumTimepoints() { validateControllerConnectedField(fields.getNumTimepointsTextField(), fields.getTimepointsController()); } + + void validateOutputFilename() { validateTextFieldFilename(fields.getOutputFilenameTextField()); } + + void validateNumReplicates() { validateTextFieldByte(fields.getNumReplicatesTextField()); } + + void validateNumConditions() { + validateRadioButtonToggledField(fields.getNumConditionsTextField(), + fields.getConditionsController(), "strainLabelingRadioButton"); + } + + void validateNumStrains() { + validateRadioButtonToggledField(fields.getNumStrainsTextField(), + fields.getStrainsController(), "conditionLabelingRadioButton"); + } + + @Override + void validateTextFieldByID(String id) { + switch (id) { + case "numTimepointsTextField" -> validateNumTimepoints(); + case "outputFilenameEmptyInputSheetTextField" -> validateOutputFilename(); + case "numReplicatesEmptyInputSheetTextField" -> validateNumReplicates(); + case "numConditionsTextField" -> validateNumConditions(); + case "numStrainsTextField" -> validateNumStrains(); + default -> throw new IllegalStateException("Unexpected value: " + id); + } + } + + void validateSampleLabelingRadioButtons() { + if (!fields.getStrainLabelingRadioButton().isSelected() + && !fields.getConditionLabelingRadioButton().isSelected() + && !fields.getConditionAndStrainLabelingRadioButton().isSelected()) { + guiLogger.errorOccurred(fields.getStatusLabel(), "Error: Please select a sample labeling option"); + fields.getSampleLabelingRadioButtons().setStatus(FieldStatus.INVALID); + } else { + guiLogger.clearError(fields.getStatusLabel()); + fields.getSampleLabelingRadioButtons().setStatus(FieldStatus.READY); + } + } + + private void validateRadioButtonToggledField(ValidatableField field, AbstractLabelController controller, String disableRadioButtonID) { + if (field.getControlID().equals(disableRadioButtonID)) { + field.setStatus(FieldStatus.READY); + guiLogger.clearError(field.getErrorLabel()); + } else validateControllerConnectedField(field, controller); + } + + private void validateControllerConnectedField(ValidatableField field, AbstractLabelController controller) { + switch (controller) { + case null -> { } + case TimepointsController timepointsController -> { + if (timepointsController.usingNumDays()) validateTextFieldByte(field); + else { + guiLogger.clearError(field.getErrorLabel()); + config.setUsingNumDays(timepointsController.usingNumDays()); + config.setDays(timepointsController.getDays()); + field.setStatus(FieldStatus.READY); + } + } + case ConditionsController conditionsController -> { + if (conditionsController.usingNumConditions()) validateTextFieldByte(field); + else { + guiLogger.clearError(field.getErrorLabel()); + config.setUsingNumConditions(conditionsController.usingNumConditions()); + config.setConditions(conditionsController.getConditions()); + field.setStatus(FieldStatus.READY); + } + } + case StrainsController strainsController -> { + if (strainsController.usingNumStrains()) validateTextFieldByte(field); + else { + guiLogger.clearError(field.getErrorLabel()); + config.setUsingNumStrains(strainsController.usingNumStrains()); + config.setStrains(strainsController.getStrains()); + field.setStatus(FieldStatus.READY); + } + } + default -> throw new IllegalStateException("Unexpected value: " + controller.getClass()); + } + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/IllegalFieldAccessException.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/IllegalFieldAccessException.java new file mode 100644 index 0000000..a5f0a4d --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/IllegalFieldAccessException.java @@ -0,0 +1,9 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import org.awdevelopment.smithlab.config.Mode; + +public class IllegalFieldAccessException extends IllegalAccessException { + public IllegalFieldAccessException(Mode mode, String id) { + super("Field with id: \"" + id + "\" is not accessible in mode: " + mode); + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java new file mode 100644 index 0000000..a5e6a6b --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java @@ -0,0 +1,306 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; +import org.awdevelopment.smithlab.config.*; +import org.awdevelopment.smithlab.data.experiment.Experiment; +import org.awdevelopment.smithlab.gui.FXMLResourceType; +import org.awdevelopment.smithlab.gui.SceneLoader; +import org.awdevelopment.smithlab.gui.controllers.AbstractController; +import org.awdevelopment.smithlab.gui.controllers.ConditionsController; +import org.awdevelopment.smithlab.gui.controllers.StrainsController; +import org.awdevelopment.smithlab.gui.controllers.TimepointsController; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldStatus; +import org.awdevelopment.smithlab.io.exceptions.InputFileException; +import org.awdevelopment.smithlab.io.exceptions.NoDaysException; +import org.awdevelopment.smithlab.io.exceptions.NoStrainsOrConditionsException; +import org.awdevelopment.smithlab.io.exceptions.OutputException; +import org.awdevelopment.smithlab.io.input.InputReader; +import org.awdevelopment.smithlab.io.output.OutputGenerator; +import org.awdevelopment.smithlab.logging.GUILogger; + +public class MainApplicationController extends AbstractController { + private ConfigManager config; + + // EMPTY INPUT SHEET + @FXML protected Label numConditionsErrorLabel; + @FXML protected Label numTimepointsErrorLabel; + @FXML protected Label numStrainErrorLabel; + @FXML protected Label numReplicatesErrorLabelEmptyInputSheet; + @FXML protected TextField numConditionsTextField; + @FXML protected TextField numStrainsTextField; + @FXML protected HBox strainsHBox; + @FXML protected HBox conditionsHBox; + @FXML protected TextField numTimepointsTextField; + @FXML protected TextField numReplicatesEmptyInputSheetTextField; + @FXML protected TextField outputFilenameEmptyInputSheetTextField; + @FXML protected RadioButton conditionLabelingRadioButton; + @FXML protected RadioButton strainLabelingRadioButton; + @FXML protected RadioButton conditionAndStrainLabelingRadioButton; + @FXML protected CheckBox includeBaselineColumnCheckbox; + @FXML protected ChoiceBox sampleSortingMethodEmptyInputSheetChoiceBox; + @FXML protected Label statusLabelEmptyInputSheet; + @FXML protected Label timepointsAddedLabel; + protected RadioButton[] sampleLabelingRadioButtons; + protected TimepointsController timepointsController; + protected StrainsController strainsController; + protected ConditionsController conditionsController; + + protected EmptyInputSheetFields emptyInputSheetFields; + protected EmptyInputSheetValidator emptyInputSheetValidator; + private EmptyInputSheetConfigUpdater emptyInputSheetConfigUpdater; + + // OUTPUT SHEETS + @FXML protected Label statusLabelOutputSheet; + @FXML protected Label replicatesErrorLabelOutputSheet; + @FXML protected Label outputStyleErrorLabel; + @FXML protected Label outputFilenameErrorLabel; + @FXML protected TabPane tabPane; + @FXML protected Tab outputSheetsTab; + @FXML protected Tab emptyInputSheetTab; + @FXML protected RadioButton outputStylePrismRadioButton; + @FXML protected TextField inputFileTextField; + @FXML protected Button inputFileBrowseButton; + @FXML protected Label inputFileExistsLabel; + @FXML protected RadioButton outputStyleTestsRadioButton; + @FXML protected RadioButton outputStyleRawRadioButton; + @FXML protected RadioButton outputStyleBothRadioButton; + @FXML protected TextField outputFileTextField; + @FXML protected CheckBox addSheetsToInputFileCheckbox; + @FXML protected TextField numReplicatesTextField; + @FXML protected ChoiceBox sampleSortingMethodChoiceBox; + protected RadioButton[] outputStyleRadioButtons; + + private Mode mode = ConfigDefaults.MODE; + GUILogger guiLogger; + + OutputSheetFields outputSheetFields; + OutputSheetValidator outputSheetValidator; + OutputSheetConfigUpdater outputSheetConfigUpdater; + + public MainApplicationController() { + super(); + } + + public void initialize() {} + + public void setup() { + config = new ConfigManager(getLogger()); + guiLogger = new GUILogger(getLogger()); + // Set up the radio buttons and choice boxes + radioButtonSetup(); + choiceBoxSetup(); + // Initialize support classes for generating output sheets + outputSheetFields = new OutputSheetFields(this); + outputSheetValidator = new OutputSheetValidator(outputSheetFields, guiLogger, config.getOutputSheetsConfig()); + outputSheetConfigUpdater = new OutputSheetConfigUpdater(this, config.getOutputSheetsConfig()); + outputSheetFields.getSampleSortingMethodChoiceBox().setStatus(FieldStatus.READY); + outputSheetFields.getRadioButtons().setStatus(FieldStatus.READY); + // Initialize support classes for generating empty input sheets + emptyInputSheetFields = new EmptyInputSheetFields(this); + emptyInputSheetValidator = new EmptyInputSheetValidator(emptyInputSheetFields, guiLogger, config.getEmptyInputSheetConfig()); + emptyInputSheetConfigUpdater = new EmptyInputSheetConfigUpdater(this, config.getEmptyInputSheetConfig()); + emptyInputSheetFields.getSampleSortingMethodChoiceBox().setStatus(FieldStatus.READY); + emptyInputSheetFields.getSampleLabelingRadioButtons().setStatus(FieldStatus.READY); + setupErrorLabelsOutputSheet(); + setupErrorLabelsEmptyInputSheet(); + } + + private void choiceBoxSetup() { + sampleSortingMethodChoiceBox.getItems().addAll(SortOption.values()); + sampleSortingMethodChoiceBox.setValue(config.getConfigValue(mode, ConfigOption.SORT_OPTION)); + sampleSortingMethodEmptyInputSheetChoiceBox.getItems().addAll(SortOption.values()); + sampleSortingMethodEmptyInputSheetChoiceBox.setValue(config.getConfigValue(mode, ConfigOption.SORT_OPTION)); + } + + private void radioButtonSetup() { + outputStyleRadioButtons = new RadioButton[] { + outputStylePrismRadioButton, + outputStyleTestsRadioButton, + outputStyleRawRadioButton, + outputStyleBothRadioButton + }; + sampleLabelingRadioButtons = new RadioButton[] { + conditionLabelingRadioButton, + strainLabelingRadioButton, + conditionAndStrainLabelingRadioButton + }; + } + + private void setupErrorLabel(Label label) { + label.setText(""); + label.setStyle(""); + } + + private void setupErrorLabels(Label... labels) { + for (Label label : labels) setupErrorLabel(label); + } + + private void setupErrorLabelsOutputSheet() { + setupErrorLabels(replicatesErrorLabelOutputSheet, outputStyleErrorLabel, outputFilenameErrorLabel, + inputFileExistsLabel, statusLabelOutputSheet); + } + + private void setupErrorLabelsEmptyInputSheet() { + setupErrorLabels(numConditionsErrorLabel, numTimepointsErrorLabel, numStrainErrorLabel, + numReplicatesErrorLabelEmptyInputSheet, statusLabelEmptyInputSheet); + } + + public void generateOutput() { + getLogger().atDebug("Mode: " + mode); + switch (mode) { + case GENERATE_OUTPUT_SHEETS -> { + guiLogger.clearError(statusLabelOutputSheet); + if (!outputSheetValidator.fieldsValid()) return; + generateOutputSheets(); + } + case GENERATE_EMPTY_INPUT_SHEET -> { + guiLogger.clearError(statusLabelEmptyInputSheet); + if (!emptyInputSheetValidator.fieldsValid()) return; + generateEmptyInputSheet(); + } + case IMAGE_RECOGNITION -> {} + } + } + + private void generateOutputSheets() { + getLogger().atDebug("FROM GUI: USER CLICKED GENERATE BUTTON"); + getLogger().atDebug("Generating output..."); + getLogger().atDebug(new String[] { + "Input file: " + config.getConfigValue(mode, ConfigOption.INPUT_FILE), + "Output file: " + config.getConfigValue(mode, ConfigOption.OUTPUT_FILE), + "Output type: " + config.getConfigValue(mode, ConfigOption.OUTPUT_TYPE), + "Write to different file: " + config.getConfigValue(mode, ConfigOption.WRITE_TO_DIFFERENT_FILE), + "Sort option: " + config.getConfigValue(mode, ConfigOption.SORT_OPTION), + "Number of replicates: " + config.getConfigValue(mode, ConfigOption.NUMBER_OF_REPLICATES), + }); + InputReader reader = new InputReader(config.getOutputSheetsConfig(), getLogger()); + getLogger().atDebug("Successfully initialized InputReader - reading experiment data..."); + Experiment experiment; + try { + experiment = reader.readExperimentData(); + } catch (InputFileException e) { + guiLogger.errorOccurred(statusLabelOutputSheet, e.getMessage()); + return; + } + getLogger().atDebug("Successfully read experiment data - Initializing OutputGenerator..."); + OutputGenerator outputGenerator = new OutputGenerator(config.getOutputSheetsConfig()); + getLogger().atDebug("Successfully initialized OutputGenerator - generating output..."); + try { + outputGenerator.generateOutput(experiment); + } catch (OutputException e) { + guiLogger.errorOccurred(statusLabelOutputSheet, e.getMessage()); + return; + } + getLogger().atInfo("Successfully generated output!"); + setupErrorLabelsOutputSheet(); + statusLabelOutputSheet.setText("Successfully generated output!"); + statusLabelOutputSheet.setStyle("-fx-text-fill: green"); + } + public void updateInputFile(KeyEvent keyEvent) { outputSheetConfigUpdater.updateInputFile(keyEvent); } + + public void browseForInputFile() { outputSheetConfigUpdater.browseForInputFile(); } + + public void handleRadioButtonPressOutputSheets(ActionEvent actionEvent) { outputSheetConfigUpdater.handleRadioButtonPressOutputSheets(actionEvent); } + + public void updateOutputFilename(KeyEvent keyEvent) { outputSheetConfigUpdater.updateOutputFilename(keyEvent); } + + public void handleAddSheetsCheckbox() { outputSheetConfigUpdater.handleAddSheetsCheckbox(); } + + public void updateNumReplicates(KeyEvent keyEvent) { outputSheetConfigUpdater.updateNumReplicates(keyEvent); } + + public void updateSampleSortingMethod() { if (outputSheetConfigUpdater != null) outputSheetConfigUpdater.updateSampleSortingMethod(); } + + public void updateOutputReplicatesFields() { + outputSheetConfigUpdater.updateNumReplicates(null); + outputSheetConfigUpdater.updateOutputFilename(null); + } + + public void updateInputReplicatesFields() { + outputSheetConfigUpdater.updateInputFile(null); + outputSheetConfigUpdater.updateNumReplicates(null); + } + + public void updateInputOutputFields() { + outputSheetConfigUpdater.updateInputFile(null); + outputSheetConfigUpdater.updateOutputFilename(null); + } + + public void updateMode() { + if (tabPane.getSelectionModel().getSelectedItem() == outputSheetsTab) { + mode = Mode.GENERATE_OUTPUT_SHEETS; + } else if (tabPane.getSelectionModel().getSelectedItem() == emptyInputSheetTab) { + mode = Mode.GENERATE_EMPTY_INPUT_SHEET; } +// else if (tabPane.getSelectionModel().getSelectedItem() == imageRecognitionTab) { +// mode = Mode.IMAGE_RECOGNITION; +// } + switch (mode) { + case GENERATE_OUTPUT_SHEETS -> { if (outputSheetConfigUpdater != null) updateFieldsOutputSheets();} + case GENERATE_EMPTY_INPUT_SHEET -> { if (emptyInputSheetConfigUpdater != null) updateFieldsEmptyInputSheet(); } + case IMAGE_RECOGNITION -> {} + } + } + + private void generateEmptyInputSheet() { + OutputGenerator outputGenerator; + try { + outputGenerator = new OutputGenerator(config.getEmptyInputSheetConfig()); + } catch (NoDaysException | NoStrainsOrConditionsException e) { + guiLogger.errorOccurred(statusLabelEmptyInputSheet, e.getMessage()); + return; + } + try { + outputGenerator.generateEmptyInputSheet(); + } catch (OutputException e) { + guiLogger.errorOccurred(statusLabelEmptyInputSheet, e.getMessage()); + return; + } + statusLabelEmptyInputSheet.setText("Successfully generated output!"); + statusLabelEmptyInputSheet.setStyle("-fx-text-fill: green"); + } + + public void updateFieldsEmptyInputSheet() { + emptyInputSheetConfigUpdater.updateFields(); + emptyInputSheetValidator.preliminaryFieldsValid(); + } + + public void updateFieldsOutputSheets() { + outputSheetConfigUpdater.updateFields(); + outputSheetValidator.preliminaryFieldsValid(); + } + + public void openConditionsFXML() { + updateFieldsEmptyInputSheet(); + conditionsController = (ConditionsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.CONDITIONS, getLogger(), config.getEmptyInputSheetConfig()); + } + + public void openStrainsFXML() { + updateFieldsEmptyInputSheet(); + strainsController = (StrainsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.STRAINS, getLogger(), config.getEmptyInputSheetConfig()); + } + + public void openTimepointsFXML() { + updateFieldsEmptyInputSheet(); + timepointsController = (TimepointsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.TIMEPOINTS, getLogger(), config.getEmptyInputSheetConfig()); + } + + public void updateSampleLabelingRadioButtons(ActionEvent actionEvent) { emptyInputSheetConfigUpdater.updateSampleLabelingRadioButtons(actionEvent); } + + public void updateNumConditionsEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateNumConditions(keyEvent); } + + public void updateNumStrainsEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateNumStrains(keyEvent); } + + public void updateNumReplicateEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateNumReplicates(keyEvent); } + + public void updateNumTimepointsEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateNumTimepoints(keyEvent); } + + public void handleIncludeBaselineColumn() { emptyInputSheetConfigUpdater.handleIncludeBaselineColumn(); } + + public void updateOutputFilenameEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateOutputFilename(keyEvent); } + + public Control getControlByID(String id) { return (Control) tabPane.getScene().lookup("#" + id); } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java new file mode 100644 index 0000000..bdbb40f --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java @@ -0,0 +1,102 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.event.ActionEvent; +import javafx.scene.control.ChoiceBox; +import javafx.scene.control.RadioButton; +import javafx.scene.input.KeyEvent; +import javafx.stage.FileChooser; +import org.awdevelopment.smithlab.config.ConfigOption; +import org.awdevelopment.smithlab.config.OutputSheetsConfig; +import org.awdevelopment.smithlab.config.SortOption; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldStatus; +import org.awdevelopment.smithlab.io.output.formats.OutputType; +import org.awdevelopment.smithlab.logging.GUILogger; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +import java.io.File; + +public class OutputSheetConfigUpdater extends AbstractConfigUpdater { + + MainApplicationController controller; + private final OutputSheetsConfig config; + private final OutputSheetFields fields; + private final GUILogger guiLogger; + private final LoggerHelper LOGGER; + + OutputSheetConfigUpdater(MainApplicationController controller, OutputSheetsConfig config) { + super(config, controller.outputSheetValidator, controller.outputSheetFields); + this.controller = controller; + this.config = config; + this.fields = controller.outputSheetFields; + this.guiLogger = controller.guiLogger; + this.LOGGER = config.LOGGER(); + } + + public void browseForInputFile() { + LOGGER.atDebug("Browsing for input file..."); + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Select Input File (ending in .xlsx)"); + File inputFile = fileChooser.showOpenDialog(fields.getInputFileBrowseButton().getScene().getWindow()); + if (inputFile != null) { + LOGGER.atDebug("Selected input file with path: \"" + inputFile.getAbsolutePath() + "\""); + getTextField(fields.getInputFileTextField()).setText(inputFile.getAbsolutePath()); + fields.getInputFileTextField().setStatus(FieldStatus.EDITED_NOT_VALIDATED); + updateInputFile(null); + } + } + + public void handleRadioButtonPressOutputSheets(ActionEvent actionEvent) { + updateFields(); + RadioButton radioButton = (RadioButton) actionEvent.getSource(); + RadioButton[] radioButtons = new RadioButton[] { fields.getOutputStylePrismRadioButton(), fields.getOutputStyleTestsRadioButton(), fields.getOutputStyleRawRadioButton(), fields.getOutputStyleBothRadioButton() }; + for (RadioButton otherRadioButton : radioButtons) if (!otherRadioButton.equals(radioButton)) otherRadioButton.setSelected(false); + OutputType oldOutputType = config.outputType(); + if (fields.getOutputStylePrismRadioButton().isSelected()) config.setOutputType(OutputType.PRISM); + else if (fields.getOutputStyleTestsRadioButton().isSelected()) config.setOutputType(OutputType.OASIS); + else if (fields.getOutputStyleRawRadioButton().isSelected()) config.setOutputType(OutputType.RAW); + else if (fields.getOutputStyleBothRadioButton().isSelected()) config.setOutputType(OutputType.BOTH); + LOGGER.atDebug("Radio button press ActionEvent caught: Switched from OutputType \"" + + oldOutputType +"\" to new OutputType \"" + radioButton.getText() + "\"."); + } + + public void handleAddSheetsCheckbox() { + updateFields(); + if (fields.getAddSheetsToInputFileCheckbox().isSelected()) { + getTextField(fields.getOutputFileTextField()).setDisable(true); + config.setWriteToDifferentFile(false); + config.setOutputFilename(getTextField(fields.getInputFileTextField()).getText()); + if (!fields.getOutputFilenameErrorLabel().getText().isEmpty()) + guiLogger.clearError(fields.getOutputFilenameErrorLabel()); + LOGGER.atDebug("Add sheets to input file checkbox selected - output file text field disabled."); + } else { + getTextField(fields.getOutputFileTextField()).setDisable(false); + config.setWriteToDifferentFile(true); + config.setOutputFilename(getTextField(fields.getOutputFileTextField()).getText()); + LOGGER.atDebug("Add sheets to input file checkbox deselected - output file text field enabled."); + } + } + + public void updateNumReplicates(KeyEvent keyEvent) { updateTextField(fields.getNumReplicatesTextField(), ConfigOption.NUMBER_OF_REPLICATES, keyEvent); } + + public void updateOutputFilename(KeyEvent keyEvent) { updateTextField(fields.getOutputFileTextField(), ConfigOption.OUTPUT_FILE, keyEvent); } + + public void updateInputFile(KeyEvent keyEvent) { updateTextField(fields.getInputFileTextField(), ConfigOption.INPUT_FILE, keyEvent); } + + @SuppressWarnings("unchecked") + public void updateSampleSortingMethod() { + try { + config.set(ConfigOption.SORT_OPTION, ((ChoiceBox) fields.getControlByIDAndMode(fields.getSampleSortingMethodChoiceBox().getControlID(), config.mode())).getValue()); + } catch (IllegalFieldAccessException e) { + LOGGER.atFatal("Error occurred while updating sample sorting method choice box.", e); + LOGGER.atFatal(e.getMessage()); + System.exit(1); + } + } + + public void updateFields() { + updateNumReplicates(null); + updateOutputFilename(null); + updateInputFile(null); + updateSampleSortingMethod(); + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetFields.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetFields.java new file mode 100644 index 0000000..fe13be7 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetFields.java @@ -0,0 +1,105 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.*; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldType; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.ValidatableField; + +public class OutputSheetFields extends AbstractFields{ + + private final ValidatableField inputFileTextField; + private final ValidatableField outputFileTextField; + private final ValidatableField numReplicatesTextField; + private final ValidatableField sampleSortingMethodChoiceBox; + private final ValidatableField outputStyleRadioButtons; + + private final MainApplicationController controller; + + OutputSheetFields(MainApplicationController controller) { + super(controller, new ValidatableField[]{ new ValidatableField(controller.inputFileTextField, controller.inputFileExistsLabel, FieldType.EXISTING_FILE), + new ValidatableField(controller.outputFileTextField, controller.outputFilenameErrorLabel, FieldType.FILENAME), + new ValidatableField(controller.numReplicatesTextField, controller.replicatesErrorLabelOutputSheet, FieldType.BYTE), + new ValidatableField(controller.sampleSortingMethodChoiceBox, controller.statusLabelOutputSheet, FieldType.CHOICE_BOX), + new ValidatableField(controller.outputStyleRadioButtons, controller.outputStyleErrorLabel, FieldType.RADIO_BUTTONS)}); + this.controller = controller; + this.inputFileTextField = super.getValidatableFields()[0]; + this.outputFileTextField = super.getValidatableFields()[1]; + this.numReplicatesTextField = super.getValidatableFields()[2]; + this.sampleSortingMethodChoiceBox = super.getValidatableFields()[3]; + this.outputStyleRadioButtons = super.getValidatableFields()[4]; + } + + public Label getStatusLabelOutputSheet() { + return controller.statusLabelOutputSheet; + } + + public Label getReplicatesErrorLabelOutputSheet() { + return controller.replicatesErrorLabelOutputSheet; + } + + public Label getOutputStyleErrorLabel() { + return controller.outputStyleErrorLabel; + } + + public Label getOutputFilenameErrorLabel() { + return controller.outputFilenameErrorLabel; + } + + public RadioButton getOutputStylePrismRadioButton() { return controller.outputStylePrismRadioButton; } + + public ValidatableField getInputFileTextField() { return inputFileTextField; } + + public Button getInputFileBrowseButton() { + return controller.inputFileBrowseButton; + } + + public Label getInputFileExistsLabel() { + return controller.inputFileExistsLabel; + } + + public RadioButton getOutputStyleTestsRadioButton() { + return controller.outputStyleTestsRadioButton; + } + + public RadioButton getOutputStyleRawRadioButton() { + return controller.outputStyleRawRadioButton; + } + + public RadioButton getOutputStyleBothRadioButton() { + return controller.outputStyleBothRadioButton; + } + + public ValidatableField getOutputFileTextField() { + return outputFileTextField; + } + + public CheckBox getAddSheetsToInputFileCheckbox() { + return controller.addSheetsToInputFileCheckbox; + } + + public ValidatableField getNumReplicatesTextField() { + return numReplicatesTextField; + } + + public ValidatableField getSampleSortingMethodChoiceBox() { + return sampleSortingMethodChoiceBox; + } + + public ValidatableField getRadioButtons() { + return outputStyleRadioButtons; + } + + public RadioButton getSelectedRadioButton() { + String[] radioButtons = {"outputStylePrismRadioButton", "outputStyleTestsRadioButton", "outputStyleRawRadioButton", "outputStyleBothRadioButton"}; + RadioButton[] radioButtonArray = new RadioButton[radioButtons.length]; + for (int i = 0; i < radioButtons.length; i++) { + radioButtonArray[i] = (RadioButton) controller.getControlByID(radioButtons[i]); + } + for (RadioButton radioButton : radioButtonArray) { + if (radioButton.isSelected()) { + return radioButton; + } + } + return null; + } +} + diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java new file mode 100644 index 0000000..e2dfe40 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java @@ -0,0 +1,62 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.ChoiceBox; +import org.awdevelopment.smithlab.config.OutputSheetsConfig; +import org.awdevelopment.smithlab.config.SortOption; +import org.awdevelopment.smithlab.gui.controllers.main.validatable_field.FieldStatus; +import org.awdevelopment.smithlab.logging.GUILogger; + +public class OutputSheetValidator extends AbstractValidator { + + private final OutputSheetFields fields; + private final OutputSheetsConfig config; + + protected OutputSheetValidator(OutputSheetFields fields, GUILogger guiLogger, OutputSheetsConfig config) { + super(config.LOGGER(), guiLogger, fields, config.mode()); + this.fields = fields; + this.config = config; + } + + void inputFileTextFieldValid() { validateTextFieldFileExists(fields.getInputFileTextField()); } + + void outputFilenameTextFieldValid() { + if (fields.getAddSheetsToInputFileCheckbox().isSelected()) fields.getOutputFileTextField().setStatus(FieldStatus.READY); + else validateTextFieldFilename(fields.getOutputFileTextField()); + } + + void numReplicatesTextFieldValid() { validateTextFieldByte(fields.getNumReplicatesTextField()); } + + @SuppressWarnings("unchecked") + void sampleSortingValid() { + try { + if (((ChoiceBox) fields.getControlByIDAndMode("sampleSortingMethodChoiceBox", config.mode())).getValue() != null) { + fields.getSampleSortingMethodChoiceBox().setStatus(FieldStatus.READY); + } else fields.getSampleSortingMethodChoiceBox().setStatus(FieldStatus.INVALID); + } catch (IllegalFieldAccessException e) { + LOGGER().atError("Error occurred while validating sample sorting method choice box."); + LOGGER().atError(e.getMessage()); + System.exit(1); + } + } + + void outputTypeValid() { + if (fields.getOutputStylePrismRadioButton().isSelected() + ^ fields.getOutputStyleTestsRadioButton().isSelected() + ^ fields.getOutputStyleRawRadioButton().isSelected() + ^ fields.getOutputStyleBothRadioButton().isSelected()) { + fields.getRadioButtons().setStatus(FieldStatus.READY); + } else fields.getRadioButtons().setStatus(FieldStatus.INVALID); + } + + @Override + void validateTextFieldByID(String id) { + switch (id) { + case "inputFileTextField" -> inputFileTextFieldValid(); + case "outputFileTextField" -> outputFilenameTextFieldValid(); + case "numReplicatesTextField" -> numReplicatesTextFieldValid(); + case "sampleSortingMethodChoiceBox" -> sampleSortingValid(); + case "radioButtons" -> outputTypeValid(); + default -> throw new IllegalStateException("Unexpected value: " + id); + } + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/FieldStatus.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/FieldStatus.java new file mode 100644 index 0000000..6e21037 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/FieldStatus.java @@ -0,0 +1,9 @@ +package org.awdevelopment.smithlab.gui.controllers.main.validatable_field; + +public enum FieldStatus { + READY, + INVALID, + UNTOUCHED, + EMPTY, + EDITED_NOT_VALIDATED +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/FieldType.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/FieldType.java new file mode 100644 index 0000000..d46aed9 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/FieldType.java @@ -0,0 +1,10 @@ +package org.awdevelopment.smithlab.gui.controllers.main.validatable_field; + +public enum FieldType { + BYTE, + FILENAME, + EXISTING_FILE, + STRING, + CHOICE_BOX, + RADIO_BUTTONS; +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/ValidatableField.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/ValidatableField.java new file mode 100644 index 0000000..b7666ea --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/validatable_field/ValidatableField.java @@ -0,0 +1,45 @@ +package org.awdevelopment.smithlab.gui.controllers.main.validatable_field; + +import javafx.scene.Node; +import javafx.scene.control.Control; +import javafx.scene.control.Label; + +import java.util.Arrays; + +public class ValidatableField { + + private final String controlID; + private final Label errorLabel; + private final String[] controlIDs; + private final FieldType fieldType; + private FieldStatus fieldStatus; + + private ValidatableField(String controlID, String[] controlIDs, Label errorLabel, FieldType fieldType, FieldStatus fieldStatus) { + this.controlID = controlID; + this.controlIDs = controlIDs; + this.errorLabel = errorLabel; + this.fieldType = fieldType; + this.fieldStatus = fieldStatus; + } + + public ValidatableField(Control control, Label errorLabel, FieldType fieldType) { + this(control.getId(), null, errorLabel, fieldType, FieldStatus.UNTOUCHED); + } + + public ValidatableField(Control[] controls, Label errorLabel, FieldType fieldType) { + this(null, Arrays.stream(controls).map(Node::getId).toArray(String[]::new), errorLabel, fieldType, FieldStatus.UNTOUCHED); + } + + public String getControlID() { return controlID; } + + public String[] getControlIDs() { return controlIDs; } + + public Label getErrorLabel() { return errorLabel; } + + public FieldType getFieldType() { return fieldType; } + + public FieldStatus status() { return fieldStatus; } + + public void setStatus(FieldStatus fieldStatus) { this.fieldStatus = fieldStatus; } + +} diff --git a/src/main/java/org/awdevelopment/smithlab/image_recognition/ImageReader.java b/src/main/java/org/awdevelopment/smithlab/image_recognition/ImageReader.java new file mode 100644 index 0000000..8f8abd1 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/image_recognition/ImageReader.java @@ -0,0 +1,5 @@ +package org.awdevelopment.smithlab.image_recognition; + +public class ImageReader { + +} diff --git a/src/main/java/org/awdevelopment/smithlab/image_recognition/ImageSetReader.java b/src/main/java/org/awdevelopment/smithlab/image_recognition/ImageSetReader.java new file mode 100644 index 0000000..00df9fa --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/image_recognition/ImageSetReader.java @@ -0,0 +1,4 @@ +package org.awdevelopment.smithlab.image_recognition; + +public class ImageSetReader { +} diff --git a/src/main/java/org/awdevelopment/smithlab/io/input/InputReader.java b/src/main/java/org/awdevelopment/smithlab/io/input/InputReader.java index 360ceab..3aa8a70 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/input/InputReader.java +++ b/src/main/java/org/awdevelopment/smithlab/io/input/InputReader.java @@ -1,17 +1,17 @@ package org.awdevelopment.smithlab.io.input; -import org.awdevelopment.smithlab.config.Config; +import org.awdevelopment.smithlab.config.OutputSheetsConfig; import org.awdevelopment.smithlab.data.experiment.Experiment; import org.awdevelopment.smithlab.io.exceptions.InputFileException; import org.awdevelopment.smithlab.logging.LoggerHelper; public class InputReader { - private final Config config; + private final OutputSheetsConfig config; private final LoggerHelper LOGGER; - public InputReader(Config config, LoggerHelper logger) { + public InputReader(OutputSheetsConfig config, LoggerHelper logger) { this.config = config; this.LOGGER = logger; } diff --git a/src/main/java/org/awdevelopment/smithlab/io/input/XlsxInputReader.java b/src/main/java/org/awdevelopment/smithlab/io/input/XlsxInputReader.java index fa337c7..ef9cdfe 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/input/XlsxInputReader.java +++ b/src/main/java/org/awdevelopment/smithlab/io/input/XlsxInputReader.java @@ -249,6 +249,7 @@ private Strain readStrain(XSSFCell cell) throws InvalidStrainValueException { } public Experiment readExperimentData() throws InputFileException { + LOGGER.atDebug("Reading in experiment data from file: "+xlsxFile); XSSFSheet sheet = getWorkbook().getSheetAt(INPUT_SHEET_INDEX); Headers headers = readHeaders(sheet); Experiment experiment = new Experiment(); diff --git a/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java index 2c13152..2026ea3 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java @@ -1,7 +1,8 @@ package org.awdevelopment.smithlab.io.output; -import org.awdevelopment.smithlab.config.Config; -import org.awdevelopment.smithlab.config.Mode; +import org.awdevelopment.smithlab.config.ConfigDefaults; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; +import org.awdevelopment.smithlab.config.OutputSheetsConfig; import org.awdevelopment.smithlab.data.experiment.EmptyExperiment; import org.awdevelopment.smithlab.io.exceptions.NoDaysException; import org.awdevelopment.smithlab.io.exceptions.NoStrainsOrConditionsException; @@ -24,44 +25,38 @@ public class OutputGenerator { private final boolean includeBaselineColumn; private final EmptyExperiment emptyExperiment; - public OutputGenerator(Config config, LoggerHelper logger) throws NoDaysException { - LOGGER = logger; - switch (config.outputType()) { - case PRISM: - outputStyle = new PrismOutputStyle(config.sortOption()); - break; - case STATISTICAL_TESTS: - outputStyle = new StatisticalTestsOutputStyle(config.sortOption(), config.numReplicates()); - break; - case RAW: - outputStyle = new RawOutputStyle(config.sortOption()); - break; - case BOTH: - outputStyle = new BothOutputStyle(config.sortOption(), config.numReplicates()); - break; - default: - // This is redundant because the compiler thinks that outputStyle is not initialized - outputStyle = null; - LOGGER.atError("Output type not recognized"); - System.exit(0); - } + public OutputGenerator(OutputSheetsConfig config) { + LOGGER = config.LOGGER(); + outputStyle = switch (config.outputType()) { + case PRISM -> new PrismOutputStyle(config.sortOption()); + case OASIS -> new StatisticalTestsOutputStyle(config.sortOption(), config.numberOfReplicates()); + case RAW -> new RawOutputStyle(config.sortOption()); + case BOTH -> new BothOutputStyle(config.sortOption(), config.numberOfReplicates()); + }; if (config.writeToDifferentFile()) this.outputFileName = config.outputFilename(); else this.outputFileName = config.inputFile().getPath(); this.writeToDifferentFile = config.writeToDifferentFile(); this.inputFile = config.inputFile(); - this.emptyInputSheetName = config.emptyInputSheetName(); this.GUI = config.GUI(); - this.includeBaselineColumn = config.includeBaselineColumn(); - if (config.mode() == Mode.GENERATE_EMPTY_INPUT_SHEET) { - Byte[] dayByteObjects = config.days().toArray(new Byte[0]); - byte[] days = new byte[dayByteObjects.length]; - for (int i = 0; i < days.length; i++) { days[i] = dayByteObjects[i]; } - this.emptyExperiment = new EmptyExperiment(config.strains(), config.conditions(), config.numReplicates(), - days, config.numDays()); - } else { - this.emptyExperiment = null; - } + // these are not used in this constructor + emptyInputSheetName = null; + includeBaselineColumn = false; + emptyExperiment = null; + } + + public OutputGenerator(EmptyInputSheetConfig config) throws NoDaysException, NoStrainsOrConditionsException { + LOGGER = config.LOGGER(); + outputFileName = ConfigDefaults.EMPTY_INPUT_SHEET_FILENAME; + emptyInputSheetName = config.emptyInputSheetName(); + includeBaselineColumn = config.includeBaselineColumn(); + emptyExperiment = generateEmptyExperiment(config); + GUI = false; + // these are not used in this constructor + this.outputStyle = null; + this.writeToDifferentFile = false; + this.inputFile = null; } + public void generateOutput(Experiment experiment) throws OutputException { XlsxOutputWriter writer = new XlsxOutputWriter(outputStyle, writeToDifferentFile, inputFile); try { @@ -87,4 +82,19 @@ else if (this.emptyExperiment.usingNumDays()) { emptyInputSheetWriter = new XlsxEmptyInputSheetWriter(emptyInputSheetName, LOGGER, includeBaselineColumn, emptyExperiment); emptyInputSheetWriter.writeEmptyInputSheet(); } + + private EmptyExperiment generateEmptyExperiment(EmptyInputSheetConfig config) throws NoDaysException { + Byte[] days = config.days().toArray(new Byte[0]); + byte[] daysArray = new byte[days.length]; + for (int i = 0; i < days.length; i++) { daysArray[i] = days[i];} + if (config.usingNumConditions() && config.usingNumStrains()) { + return new EmptyExperiment(config.numStrains(), config.numConditions(), config.numReplicates(), daysArray, config.numDays()); + } else if (config.usingNumConditions()) { + return new EmptyExperiment(config.strains(), config.numConditions(), config.numReplicates(), daysArray, config.numDays()); + } else if (config.usingNumStrains()) { + return new EmptyExperiment(config.numStrains(), config.conditions(), config.numReplicates(), daysArray, config.numDays()); + } else { + return new EmptyExperiment(config.strains(), config.conditions(), config.numReplicates(), daysArray, config.numDays()); + } + } } diff --git a/src/main/java/org/awdevelopment/smithlab/io/output/XlsxOutputWriter.java b/src/main/java/org/awdevelopment/smithlab/io/output/XlsxOutputWriter.java index 360aa0a..f8c2403 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/XlsxOutputWriter.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/XlsxOutputWriter.java @@ -28,7 +28,7 @@ public XlsxOutputWriter(OutputStyle outputStyle, boolean writeToDifferentFile, F private int getNumberOfSheets() { return switch (outputStyle.getOutputType()) { - case PRISM, RAW, STATISTICAL_TESTS -> 1; + case PRISM, RAW, OASIS -> 1; case BOTH -> 2; }; } @@ -59,7 +59,7 @@ private void renameGeneratedOutputSheets(XSSFSheet[] sheets) { setSheetName(sheets[lastSheetIndex - 1].getWorkbook(), sheets[lastSheetIndex - 1], "PRISM"); setSheetName(sheets[lastSheetIndex].getWorkbook(), sheets[lastSheetIndex], "OTHER"); } - case STATISTICAL_TESTS -> setSheetName(sheets[lastSheetIndex].getWorkbook(), sheets[lastSheetIndex], "OTHER"); + case OASIS -> setSheetName(sheets[lastSheetIndex].getWorkbook(), sheets[lastSheetIndex], "OASIS"); } } diff --git a/src/main/java/org/awdevelopment/smithlab/io/output/formats/OutputType.java b/src/main/java/org/awdevelopment/smithlab/io/output/formats/OutputType.java index d197de3..3f5c679 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/formats/OutputType.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/formats/OutputType.java @@ -1,7 +1,7 @@ package org.awdevelopment.smithlab.io.output.formats; public enum OutputType { - PRISM, STATISTICAL_TESTS, RAW, BOTH; + PRISM, OASIS, RAW, BOTH; @Override public String toString() { return formatString(this.name()); } diff --git a/src/main/java/org/awdevelopment/smithlab/io/output/formats/StatisticalTestsOutputStyle.java b/src/main/java/org/awdevelopment/smithlab/io/output/formats/StatisticalTestsOutputStyle.java index e9d4a1e..a6d6696 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/formats/StatisticalTestsOutputStyle.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/formats/StatisticalTestsOutputStyle.java @@ -14,7 +14,7 @@ public class StatisticalTestsOutputStyle extends OutputStyle { private final short numberOfReplicates; public StatisticalTestsOutputStyle(SortOption sortOption, short numberOfReplicates) { - super(OutputType.STATISTICAL_TESTS, sortOption); + super(OutputType.OASIS, sortOption); this.numberOfReplicates = numberOfReplicates; } diff --git a/src/main/java/org/awdevelopment/smithlab/logging/GUILogger.java b/src/main/java/org/awdevelopment/smithlab/logging/GUILogger.java new file mode 100644 index 0000000..0f637d0 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/logging/GUILogger.java @@ -0,0 +1,19 @@ +package org.awdevelopment.smithlab.logging; + +import javafx.scene.control.Label; + +public class GUILogger { + final LoggerHelper LOGGER; + + public GUILogger(LoggerHelper logger) { + this.LOGGER = logger; + } + + public void errorOccurred(Label label, String message) { + LOGGER.atError(message); + label.setText(message); + label.setStyle("-fx-text-fill: red;"); + } + + public void clearError(Label label) { label.setText(""); } +} diff --git a/src/main/resources/fxml/application.fxml b/src/main/resources/fxml/application.fxml index 36bb789..f4f2cec 100644 --- a/src/main/resources/fxml/application.fxml +++ b/src/main/resources/fxml/application.fxml @@ -13,22 +13,22 @@ - + - - - - + + + + - + - + -