From 77bf735022e5a27ce8ec834310e4741716c11bae Mon Sep 17 00:00:00 2001 From: Adam W Date: Fri, 7 Feb 2025 16:07:17 -0500 Subject: [PATCH 1/9] Start work on migrating to AbstractConfig class --- src/main/java/module-info.java | 1 + .../smithlab/MainApplication.java | 7 +- .../smithlab/config/AbstractConfig.java | 85 +++++++++ .../awdevelopment/smithlab/config/Config.java | 173 ------------------ .../smithlab/config/ConfigDefault.java | 1 + .../config/EmptyInputSheetConfig.java | 63 +++++++ .../config/ImageRecognitionConfig.java | 4 + .../smithlab/config/OutputSheetsConfig.java | 49 +++++ .../config/exceptions/ConfigException.java | 7 + .../ConfigOptionNotFoundException.java | 10 + .../exceptions/WrongObjectTypeException.java | 10 + .../image_recognition/ImageReader.java | 5 + .../image_recognition/ImageSetReader.java | 4 + .../smithlab/io/output/OutputGenerator.java | 16 ++ 14 files changed, 259 insertions(+), 176 deletions(-) create mode 100644 src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java delete mode 100644 src/main/java/org/awdevelopment/smithlab/config/Config.java create mode 100644 src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java create mode 100644 src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java create mode 100644 src/main/java/org/awdevelopment/smithlab/config/OutputSheetsConfig.java create mode 100644 src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigException.java create mode 100644 src/main/java/org/awdevelopment/smithlab/config/exceptions/ConfigOptionNotFoundException.java create mode 100644 src/main/java/org/awdevelopment/smithlab/config/exceptions/WrongObjectTypeException.java create mode 100644 src/main/java/org/awdevelopment/smithlab/image_recognition/ImageReader.java create mode 100644 src/main/java/org/awdevelopment/smithlab/image_recognition/ImageSetReader.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8cc0ac3..2f064e4 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -19,4 +19,5 @@ 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; } \ 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..7a94f40 100644 --- a/src/main/java/org/awdevelopment/smithlab/MainApplication.java +++ b/src/main/java/org/awdevelopment/smithlab/MainApplication.java @@ -5,6 +5,7 @@ import org.awdevelopment.smithlab.args.Arguments; import org.awdevelopment.smithlab.config.Config; 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,12 +22,12 @@ 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) { + if (arguments.getMode() == Mode.GENERATE_EMPTY_INPUT_SHEET) { + OutputSheetsConfig config = new OutputSheetsConfig(arguments, LOGGER); OutputGenerator outputGenerator = new OutputGenerator(config, LOGGER); outputGenerator.generateEmptyInputSheet(); - } else if (config.mode() == Mode.GENERATE_OUTPUT_SHEETS) { + } else if (arguments.getMode() == Mode.GENERATE_OUTPUT_SHEETS) { InputReader reader = new InputReader(config, LOGGER); Experiment experiment = reader.readExperimentData(); OutputGenerator outputGenerator = new OutputGenerator(config, LOGGER); 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..1a9005f --- /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, ConfigDefault.GUI)); + } + + protected T get(ConfigOption option) { + try { + return getConfigEntryValue(option); + } catch (ConfigException e) { + LOGGER().atFatal(e.getMessage()); + System.exit(1); + } + return null; + } + + protected 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/ConfigDefault.java index c7de056..f19a3cb 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java +++ b/src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java @@ -11,6 +11,7 @@ public class ConfigDefault { 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 OutputType OUTPUT_TYPE = OutputType.BOTH; public static final String OUTPUT_FILENAME = DEFAULT_FILENAME; 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..56e2893 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java @@ -0,0 +1,63 @@ +package org.awdevelopment.smithlab.config; + +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, ConfigDefault.SORT_OPTION)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefault.NUMBER_OF_REPLICATES)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.EMPTY_INPUT_SHEET_NAME, ConfigDefault.EMPTY_INPUT_SHEET_NAME)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.CONDITIONS, ConfigDefault.CONDITIONS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.STRAINS, ConfigDefault.STRAINS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.DAYS, ConfigDefault.DAYS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.INCLUDE_BASELINE_COLUMN, ConfigDefault.INCLUDE_BASELINE_COLUMN)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.SAMPLE_LABELING_TYPE, ConfigDefault.SAMPLE_LABELING_TYPE)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_DAYS, ConfigDefault.NUM_DAYS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_CONDITIONS, ConfigDefault.NUM_CONDITIONS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_STRAINS, ConfigDefault.NUM_STRAINS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_DAYS, ConfigDefault.USING_NUM_DAYS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_CONDITIONS, ConfigDefault.USING_NUM_CONDITIONS)); + super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_STRAINS, ConfigDefault.USING_NUM_STRAINS)); + } + + 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..8ccb451 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java @@ -0,0 +1,4 @@ +package org.awdevelopment.smithlab.config; + +public class ImageRecognitionConfig { +} 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..e5a64be --- /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, ConfigDefault.INPUT_FILE)); + addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_FILE, ConfigDefault.OUTPUT_FILENAME)); + addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_TYPE, ConfigDefault.OUTPUT_TYPE)); + addConfigEntry(new ConfigEntry<>(ConfigOption.WRITE_TO_DIFFERENT_FILE, ConfigDefault.WRITE_TO_DIFFERENT_FILE)); + addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, ConfigDefault.SORT_OPTION)); + addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefault.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/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/output/OutputGenerator.java b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java index 2c13152..1e33c5a 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java @@ -2,6 +2,7 @@ import org.awdevelopment.smithlab.config.Config; import org.awdevelopment.smithlab.config.Mode; +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; @@ -62,6 +63,21 @@ public OutputGenerator(Config config, LoggerHelper logger) throws NoDaysExceptio this.emptyExperiment = null; } } + + public OutputGenerator(OutputSheetsConfig config) throws NoDaysException { + LOGGER = config.LOGGER(); + outputStyle = switch (config.outputType()) { + case PRISM -> new PrismOutputStyle(config.sortOption()); + case STATISTICAL_TESTS -> 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.GUI = config.GUI(); + } public void generateOutput(Experiment experiment) throws OutputException { XlsxOutputWriter writer = new XlsxOutputWriter(outputStyle, writeToDifferentFile, inputFile); try { From e648ca573de366ed499a29f5929ab7a9ce7940ca Mon Sep 17 00:00:00 2001 From: Adam Wojciak Date: Fri, 7 Feb 2025 20:21:04 -0500 Subject: [PATCH 2/9] Continue refactor --- src/main/java/module-info.java | 3 + .../smithlab/args/Arguments.java | 40 +- .../smithlab/config/AbstractConfig.java | 6 +- ...ConfigDefault.java => ConfigDefaults.java} | 3 +- .../smithlab/config/ConfigManager.java | 39 + .../config/EmptyInputSheetConfig.java | 28 +- .../config/ImageRecognitionConfig.java | 14 +- .../awdevelopment/smithlab/config/Mode.java | 2 +- .../smithlab/config/OutputSheetsConfig.java | 12 +- .../smithlab/gui/SceneLoader.java | 4 +- .../controllers/AbstractLabelController.java | 3 +- .../MainApplicationController.java | 852 ------------------ .../gui/controllers/main/AbstractFields.java | 4 + .../controllers/main/AbstractValidator.java | 61 ++ .../main/EmptyInputSheetFields.java | 34 + .../main/EmptyInputSheetValidator.java | 190 ++++ .../main/EmptyInputSheetsConfigUpdater.java | 4 + .../gui/controllers/main/GUILogger.java | 20 + .../main/MainApplicationController.java | 460 ++++++++++ .../main/OutputSheetConfigUpdater.java | 131 +++ .../controllers/main/OutputSheetFields.java | 90 ++ .../main/OutputSheetValidator.java | 88 ++ .../smithlab/io/input/InputReader.java | 3 +- .../smithlab/io/output/OutputGenerator.java | 15 +- src/main/resources/fxml/application.fxml | 10 +- 25 files changed, 1208 insertions(+), 908 deletions(-) rename src/main/java/org/awdevelopment/smithlab/config/{ConfigDefault.java => ConfigDefaults.java} (92%) create mode 100644 src/main/java/org/awdevelopment/smithlab/config/ConfigManager.java delete mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/MainApplicationController.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/GUILogger.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetFields.java create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 2f064e4..a3faa81 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -20,4 +20,7 @@ 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; } \ No newline at end of file diff --git a/src/main/java/org/awdevelopment/smithlab/args/Arguments.java b/src/main/java/org/awdevelopment/smithlab/args/Arguments.java index c42f9b5..ad7c62b 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; diff --git a/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java b/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java index 1a9005f..89ae578 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java +++ b/src/main/java/org/awdevelopment/smithlab/config/AbstractConfig.java @@ -17,10 +17,10 @@ public abstract class AbstractConfig { protected AbstractConfig(LoggerHelper logger) { this.config = new HashSet<>(); this.LOGGER = logger; - config.add(new ConfigEntry<>(ConfigOption.GUI, ConfigDefault.GUI)); + config.add(new ConfigEntry<>(ConfigOption.GUI, ConfigDefaults.GUI)); } - protected T get(ConfigOption option) { + public T get(ConfigOption option) { try { return getConfigEntryValue(option); } catch (ConfigException e) { @@ -30,7 +30,7 @@ protected T get(ConfigOption option) { return null; } - protected void set(ConfigOption option, T value) { + public void set(ConfigOption option, T value) { try { setConfigEntry(option, value); } catch (ConfigException e) { diff --git a/src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java b/src/main/java/org/awdevelopment/smithlab/config/ConfigDefaults.java similarity index 92% rename from src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java rename to src/main/java/org/awdevelopment/smithlab/config/ConfigDefaults.java index f19a3cb..dbdc94b 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/ConfigDefault.java +++ b/src/main/java/org/awdevelopment/smithlab/config/ConfigDefaults.java @@ -8,11 +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 index 56e2893..c26e04d 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java +++ b/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java @@ -11,20 +11,20 @@ public class EmptyInputSheetConfig extends AbstractConfig { public EmptyInputSheetConfig(LoggerHelper logger) { super(logger); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, ConfigDefault.SORT_OPTION)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefault.NUMBER_OF_REPLICATES)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.EMPTY_INPUT_SHEET_NAME, ConfigDefault.EMPTY_INPUT_SHEET_NAME)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.CONDITIONS, ConfigDefault.CONDITIONS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.STRAINS, ConfigDefault.STRAINS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.DAYS, ConfigDefault.DAYS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.INCLUDE_BASELINE_COLUMN, ConfigDefault.INCLUDE_BASELINE_COLUMN)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.SAMPLE_LABELING_TYPE, ConfigDefault.SAMPLE_LABELING_TYPE)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_DAYS, ConfigDefault.NUM_DAYS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_CONDITIONS, ConfigDefault.NUM_CONDITIONS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.NUM_STRAINS, ConfigDefault.NUM_STRAINS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_DAYS, ConfigDefault.USING_NUM_DAYS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_CONDITIONS, ConfigDefault.USING_NUM_CONDITIONS)); - super.addConfigEntry(new ConfigEntry<>(ConfigOption.USING_NUM_STRAINS, ConfigDefault.USING_NUM_STRAINS)); + 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.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 Set conditions() { return (Set) get(ConfigOption.CONDITIONS); } diff --git a/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java b/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java index 8ccb451..b6a7278 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java +++ b/src/main/java/org/awdevelopment/smithlab/config/ImageRecognitionConfig.java @@ -1,4 +1,16 @@ package org.awdevelopment.smithlab.config; -public class ImageRecognitionConfig { +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 index e5a64be..e75e76c 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/OutputSheetsConfig.java +++ b/src/main/java/org/awdevelopment/smithlab/config/OutputSheetsConfig.java @@ -22,12 +22,12 @@ public OutputSheetsConfig(Arguments args, LoggerHelper logger) { public OutputSheetsConfig(LoggerHelper logger) { super(logger); - addConfigEntry(new ConfigEntry<>(ConfigOption.INPUT_FILE, ConfigDefault.INPUT_FILE)); - addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_FILE, ConfigDefault.OUTPUT_FILENAME)); - addConfigEntry(new ConfigEntry<>(ConfigOption.OUTPUT_TYPE, ConfigDefault.OUTPUT_TYPE)); - addConfigEntry(new ConfigEntry<>(ConfigOption.WRITE_TO_DIFFERENT_FILE, ConfigDefault.WRITE_TO_DIFFERENT_FILE)); - addConfigEntry(new ConfigEntry<>(ConfigOption.SORT_OPTION, ConfigDefault.SORT_OPTION)); - addConfigEntry(new ConfigEntry<>(ConfigOption.NUMBER_OF_REPLICATES, ConfigDefault.NUMBER_OF_REPLICATES)); + 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); } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java b/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java index 485355d..67e6b30 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; @@ -15,7 +15,7 @@ public static void loadScene(Stage stage, FXMLResourceType fxmlResourceType, Log 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); 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..193b495 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java @@ -1,6 +1,7 @@ package org.awdevelopment.smithlab.gui.controllers; import org.awdevelopment.smithlab.config.Config; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; public class AbstractLabelController extends AbstractController { @@ -8,7 +9,7 @@ public class AbstractLabelController extends AbstractController { public AbstractLabelController() { super(); } - public void setConfig(Config config) { + public void setConfig(EmptyInputSheetConfig config) { this.config = config; } 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/main/AbstractFields.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java new file mode 100644 index 0000000..606da29 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java @@ -0,0 +1,4 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +public class AbstractFields { +} 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..9b4f1f3 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java @@ -0,0 +1,61 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import org.awdevelopment.smithlab.config.AbstractConfig; + +public abstract class AbstractValidator { + + private final AbstractFields fields; + private final GUILogger guiLogger; + private final AbstractConfig config; + + protected AbstractValidator(AbstractFields fields, GUILogger guiLogger, AbstractConfig config) { + this.fields = fields; + this.guiLogger = guiLogger; + this.config = config; + } + + public abstract boolean preliminaryFieldsValid(); + + public abstract boolean fieldsValid(); + + abstract boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolean preventEmpty); + + protected boolean validateTextFieldFilename(TextField textField, Label statusLabel, boolean preventEmpty) { + if (!validateTextFieldNotEmpty(textField, statusLabel, preventEmpty)) return false; + if (!textField.getText().endsWith(".xlsx")) { + guiLogger.errorOccurred(statusLabel, "Error: Filename must end in .xlsx"); + return false; + } else { + guiLogger.clearError(statusLabel); + return true; + } + } + + protected boolean validateTextFieldByte(TextField textField, Label statusLabel, boolean preventEmpty) { + if (!validateTextFieldNotEmpty(textField, statusLabel, preventEmpty)) return false; + try { + long longValue = Long.parseLong(textField.getText()); + if (longValue <= 0) { + guiLogger.errorOccurred(statusLabel, "Error: Must be a value > 0"); + return false; + } else if (longValue > Byte.MAX_VALUE) { + guiLogger.errorOccurred(statusLabel, "Error: Must be a value <= " + Byte.MAX_VALUE); + return false; + } else { + guiLogger.clearError(statusLabel); + return true; + } + } catch (NumberFormatException e) { + guiLogger.errorOccurred(statusLabel, "Error: Please enter a valid number"); + return false; + } + } + + GUILogger guiLogger() { return guiLogger; } + + AbstractFields fields() { return fields; } + + AbstractConfig config() { return config; } +} 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..53d64ab --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java @@ -0,0 +1,34 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import org.awdevelopment.smithlab.config.SortOption; +import org.awdevelopment.smithlab.gui.controllers.ConditionsController; +import org.awdevelopment.smithlab.gui.controllers.StrainsController; +import org.awdevelopment.smithlab.gui.controllers.TimepointsController; + +public class EmptyInputSheetFields extends AbstractFields { + + private final MainApplicationController controller; + EmptyInputSheetFields(MainApplicationController controller) { this.controller = controller; } + public TextField getNumTimepointsTextField() { return controller.numTimepointsTextField; } + public Label getNumConditionsErrorLabel() { return controller.numConditionsErrorLabel; } + public Label getNumTimepointsErrorLabel() { return controller.numTimepointsErrorLabel; } + public Label getNumStrainErrorLabel() { return controller.numStrainErrorLabel; } + public TextField getNumConditionsTextField() { return controller.numConditionsTextField; } + public TextField getNumStrainsTextField() { return controller.numStrainsTextField; } + public HBox getStrainsHBox() { return controller.strainsHBox; } + public HBox getConditionsHBox() { return controller.conditionsHBox; } + public TextField getNumReplicatesTextField() { return controller.numReplicatesEmptyInputSheetTextField; } + public TextField getOutputFilenameTextField() { return controller.outputFilenameEmptyInputSheetsTextField; } + 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 ChoiceBox getSampleSortingMethodChoiceBox() { return controller.sampleSortingMethodEmptyInputSheetChoiceBox; } + public Label getStatusLabel() { return controller.statusLabelEmptyInputSheets; } + public Label getTimepointsAddedLabel() { return controller.timepointsAddedLabel; } + public TimepointsController getTimepointsController() { return controller.timepointsController; } + public StrainsController getStrainsController() { return controller.strainsController; } + public ConditionsController getConditionsController() { return controller.conditionsController; } +} 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..3043c7d --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java @@ -0,0 +1,190 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; + +public class EmptyInputSheetValidator extends AbstractValidator { + + private final EmptyInputSheetFields fields; + private final GUILogger guiLogger; + private final EmptyInputSheetConfig config; + private boolean failedEmptyNumTimepointsEmptyInputSheet = false; + private boolean failedEmptyNumReplicatesEmptyInputSheet = false; + private boolean failedEmptyOutputFilenameEmptyInputSheet = false; + private boolean failedEmptyConditionsEmptyInputSheet = false; + private boolean failedEmptyStrainsEmptyInputSheet = false; + + public EmptyInputSheetValidator(EmptyInputSheetFields fields, GUILogger guiLogger, EmptyInputSheetConfig config) { + super(fields, guiLogger, config); + this.fields = fields; + this.guiLogger = guiLogger; + this.config = config; + } + + @Override + public boolean preliminaryFieldsValid() { + return false; + } + + @Override + public boolean fieldsValid() { + return false; + } + + private boolean validateNumTimepoints(boolean preventEmpty) { + if (fields.getTimepointsController() == null || fields.getTimepointsController().usingNumDays()) { + return validateTextFieldByte(fields.getNumTimepointsTextField(), fields.getNumTimepointsErrorLabel(), preventEmpty); + } else { + guiLogger.clearError(fields.getNumTimepointsErrorLabel()); + config.setUsingNumDays(fields.getTimepointsController().usingNumDays()); + config.setDays(fields.getTimepointsController().getDays()); + return true; + } + } + + private boolean validateOutputFilename(boolean preventEmpty) { + return validateTextFieldFilename(fields.getOutputFilenameTextField(), fields.getStatusLabel(), preventEmpty); + } + + private boolean checkOutputFilename(TextField textField, boolean failedEmptyBoolean, Label errorLabel) { + if (outputFilenameEmptyInputSheetsTextField.getText().isEmpty() && !failedEmptyOutputFilenameEmptyInputSheet) { + guiLogger.clearError(statusLabelEmptyInputSheets); + return true; + } else if (outputFilenameEmptyInputSheetsTextField.getText().isEmpty()){ + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter an output filename"); + return false; + } else if (!outputFilenameEmptyInputSheetsTextField.getText().endsWith(".xlsx")) { + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Output filename must end in .xlsx"); + return false; + } else { + guiLogger.clearError(statusLabelEmptyInputSheets); + return true; + } + } + + private boolean checkNumReplicatesEmptyInputSheet() { + if (numReplicatesEmptyInputSheetTextField.getText().isEmpty() && !failedEmptyNumReplicatesEmptyInputSheet) { + guiLogger.clearError(statusLabelEmptyInputSheets); + return true; + } + else if (numReplicatesEmptyInputSheetTextField.getText().isEmpty()) { + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter a number of replicates"); + return false; + } + try { + long numReplicates = Long.parseLong(numReplicatesEmptyInputSheetTextField.getText()); + if (numReplicates < 1){ + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Number must be > 0"); + return false; + } + else if (numReplicates > 127) { + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Number must be <= 127"); + return false; + } + else { + guiLogger.clearError(statusLabelEmptyInputSheets); + return true; + } + } catch (NumberFormatException e) { + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Invalid number: \"" + numReplicatesEmptyInputSheetTextField.getText() + "\""); + return false; + } + } + + private boolean checkNumConditionsEmptyInputSheet() { + if (conditionsController == null || conditionsController.usingNumConditions()) { + if (numConditionsTextField.getText().isEmpty() && !failedEmptyConditionsEmptyInputSheet) { + guiLogger.clearError(numConditionsErrorLabel); + return true; + } else if (numConditionsTextField.getText().isEmpty()) { + guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Please enter a number of conditions"); + return false; + } else { + try { + long numConditions = Long.parseLong(numConditionsTextField.getText()); + if (numConditions < 1) { + guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Number of conditions must be > 0"); + return false; + } else if (numConditions > 127) { + guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Number of conditions must be <= 127"); + return false; + } else { + guiLogger.clearError(numConditionsErrorLabel); + config.setNumConditions((byte) numConditions); + return true; + } + } catch (NumberFormatException e) { + guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Invalid number: \"" + numConditionsTextField.getText() + "\""); + return false; + } + } + } else { + guiLogger.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) { + guiLogger.clearError(numStrainErrorLabel); + return true; + } else if (numStrainsTextField.getText().isEmpty()) { + guiLogger.errorOccurred(numStrainErrorLabel, "Error: Please enter a number of strains"); + return false; + } else { + try { + long numStrains = Long.parseLong(numStrainsTextField.getText()); + if (numStrains < 1) { + guiLogger.errorOccurred(numStrainErrorLabel, "Error: Number of strains must be > 0"); + return false; + } else if (numStrains > 127) { + guiLogger.errorOccurred(numStrainErrorLabel, "Error: Number of strains must be <= 127"); + return false; + } else { + guiLogger.clearError(numStrainErrorLabel); + config.setNumStrains((byte) numStrains); + return true; + } + } catch (NumberFormatException e) { + guiLogger.errorOccurred(numStrainErrorLabel, "Error: Invalid number: \"" + numStrainsTextField.getText() + "\""); + return false; + } + } + } else { + guiLogger.clearError(numStrainErrorLabel); + config.setUsingNumStrains(strainsController.usingNumStrains()); + config.setStrains(strainsController.getStrains()); + return true; + } + } + + private boolean checkSampleLabelingRadioButtons() { + if (!conditionLabelingRadioButton.isSelected() && !strainLabelingRadioButton.isSelected() && !conditionAndStrainLabelingRadioButton.isSelected()) { + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please select a sample labeling method"); + return false; + } + return true; + } + + @Override + boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolean preventEmpty) { + boolean failedBoolean = switch (textField.getId()) { + case "numReplicatesEmptyInputSheetTextField" -> failedEmptyNumReplicatesEmptyInputSheet; + case "outputFilenameEmptyInputSheetsTextField" -> failedEmptyOutputFilenameEmptyInputSheet; + case "numConditionsTextField" -> failedEmptyConditionsEmptyInputSheet; + case "numStrainsTextField" -> failedEmptyStrainsEmptyInputSheet; + case "numTimepointsTextField" -> failedEmptyNumTimepointsEmptyInputSheet; + default -> false; + }; + if (textField.getText().isEmpty() && (preventEmpty || failedBoolean)) { + guiLogger.errorOccurred(statusLabel, "Error: Please enter a value"); + return false; + } else { + guiLogger.clearError(statusLabel); + return true; + } + } +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java new file mode 100644 index 0000000..8813028 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java @@ -0,0 +1,4 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +public class EmptyInputSheetsConfigUpdater { +} diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/GUILogger.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/GUILogger.java new file mode 100644 index 0000000..cbb117b --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/GUILogger.java @@ -0,0 +1,20 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.Label; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +public class GUILogger { + final LoggerHelper LOGGER; + + GUILogger(LoggerHelper logger) { + this.LOGGER = logger; + } + + void errorOccurred(Label label, String message) { + LOGGER.atError(message); + label.setText(message); + label.setStyle("-fx-text-fill: red;"); + } + + void clearError(Label label) { label.setText(""); } +} 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..5969896 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java @@ -0,0 +1,460 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +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.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.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; + +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 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 outputFilenameEmptyInputSheetsTextField; + @FXML protected RadioButton conditionLabelingRadioButton; + @FXML protected RadioButton strainLabelingRadioButton; + @FXML protected RadioButton conditionAndStrainLabelingRadioButton; + @FXML protected CheckBox includeBaselineColumnCheckbox; + @FXML protected ChoiceBox sampleSortingMethodEmptyInputSheetChoiceBox; + @FXML protected Label statusLabelEmptyInputSheets; + @FXML protected Label timepointsAddedLabel; + protected RadioButton[] sampleLabelingRadioButtons; + protected TimepointsController timepointsController; + protected StrainsController strainsController; + protected ConditionsController conditionsController; + + // 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; + public RadioButton[] radioButtons; + + private Mode mode = ConfigDefaults.MODE; + GUILogger guiLogger; + OutputSheetFields outputSheetFields; + OutputSheetValidator outputSheetValidator; + OutputSheetConfigUpdater outputSheetConfigUpdater; + + public MainApplicationController() { + super(); + } + + public void initialize() { + config = new ConfigManager(getLogger()); + guiLogger = new GUILogger(getLogger()); + // Set up the radio buttons and choice boxes + radioButtonSetup(); + choiceBoxSetup(); + setupErrorLabelsOutputSheet(); + updateFields(); + // Initialize support classes for generating output sheets + outputSheetFields = new OutputSheetFields(this); + outputSheetValidator = new OutputSheetValidator(outputSheetFields, guiLogger, config.getOutputSheetsConfig()); + outputSheetConfigUpdater = new OutputSheetConfigUpdater(this, config.getOutputSheetsConfig()); + } + + 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() { + radioButtons = new RadioButton[] { + outputStylePrismRadioButton, + outputStyleTestsRadioButton, + outputStyleRawRadioButton, + outputStyleBothRadioButton + }; + sampleLabelingRadioButtons = new RadioButton[] { + conditionLabelingRadioButton, + strainLabelingRadioButton, + conditionAndStrainLabelingRadioButton + }; + } + + 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() { + switch (mode) { + case GENERATE_OUTPUT_SHEETS -> { + if (!outputSheetValidator.fieldsValid()) return; + generateOutputSheets(); + } + case GENERATE_EMPTY_INPUT_SHEET -> { + if (notReadyForOutputEmptyInputSheet()) return; + generateEmptyInputSheet(); + } + case IMAGE_RECOGNITION -> { + return; + } + } + } + + private void generateOutputSheets() { + getLogger().atDebug("FROM GUI: USER CLICKED GENERATE BUTTON"); + getLogger().atInfo("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), + "Empty input sheet name: " + config.getConfigValue(mode, ConfigOption.EMPTY_INPUT_SHEET_NAME) + }); + 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(ActionEvent actionEvent) { 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 updateFields() { + outputSheetConfigUpdater.updateFields(); + } + + 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; +// } + } + + 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; + guiLogger.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; + guiLogger.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; + guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter an output filename"); + return true; + } else if (!outputFilename.endsWith(".xlsx")) { + guiLogger.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; + guiLogger.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; + guiLogger.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) { + guiLogger.errorOccurred(statusLabelEmptyInputSheets, e.getMessage()); + return; + } + try { + outputGenerator.generateEmptyInputSheet(); + } catch (OutputException e) { + guiLogger.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(); + } + } + } + + + 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) guiLogger.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) + guiLogger.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) guiLogger.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) guiLogger.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/main/OutputSheetConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java new file mode 100644 index 0000000..7075079 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java @@ -0,0 +1,131 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.event.ActionEvent; +import javafx.scene.control.Label; +import javafx.scene.control.RadioButton; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +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.io.output.formats.OutputType; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +import java.io.File; + +public class OutputSheetConfigUpdater { + + MainApplicationController controller; + private final OutputSheetsConfig config; + private final OutputSheetFields fields; + private final OutputSheetValidator validator; + private final GUILogger guiLogger; + private final LoggerHelper LOGGER; + + OutputSheetConfigUpdater(MainApplicationController controller, OutputSheetsConfig config) { + this.controller = controller; + this.config = config; + this.fields = controller.outputSheetFields; + this.validator = controller.outputSheetValidator; + 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: " + inputFile.getAbsolutePath()); + fields.getInputFileTextField().setText(inputFile.getAbsolutePath()); + updateInputFile(null); + } + } + + public void handleRadioButtonPressOutputSheets(ActionEvent actionEvent) { + controller.updateFields(); + RadioButton radioButton = (RadioButton) actionEvent.getSource(); + for (RadioButton otherRadioButton : fields.getRadioButtons()) 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.STATISTICAL_TESTS); + else if (fields.getOutputStyleRawRadioButton().isSelected()) config.setOutputType(OutputType.RAW); + else if (fields.getOutputStyleBothRadioButton().isSelected()) config.setOutputType(OutputType.BOTH); + LOGGER.atTrace("Radio button press ActionEvent caught: Switched from OutputType \"" + + oldOutputType +"\" to new OutputType \"" + radioButton.getText() + "\"."); + } + + public void handleAddSheetsCheckbox() { + controller.updateFields(); + if (fields.getAddSheetsToInputFileCheckbox().isSelected()) { + fields.getOutputFileTextField().setDisable(true); + config.setWriteToDifferentFile(false); + config.setOutputFilename(fields.getInputFileTextField().getText()); + if (!fields.getOutputFilenameErrorLabel().getText().isEmpty()) + guiLogger.clearError(fields.getOutputFilenameErrorLabel()); + } else { + fields.getOutputFileTextField().setDisable(false); + config.setWriteToDifferentFile(true); + config.setOutputFilename(fields.getOutputFileTextField().getText()); + } + } + + private void updateTextField(TextField textField, ConfigOption option, KeyEvent keyEvent, Label errorLabel, boolean failedBoolean, boolean byteParse, boolean fileExists) { + if (!byteParse && !fileExists) config.set(option, textField.getText()); + if (textField.getText().isEmpty() && !failedBoolean) guiLogger.clearError(errorLabel); + if ( keyEvent == null + || !(textField.getScene().focusOwnerProperty().get().getId().equals(textField.getId())) + || keyEvent.getCode() == KeyCode.ENTER + || keyEvent.getCode() == KeyCode.TAB + ) { + switch (textField.getId()) { + case "numReplicatesTextField" -> validator.numReplicatesTextFieldValid(failedBoolean); + case "outputFileTextField" -> validator.outputFilenameTextFieldValid(failedBoolean); + case "inputFileTextField" -> validator.inputFileTextFieldValid(failedBoolean); + } + if (byteParse) config.set(option, Byte.parseByte(textField.getText())); + if (fileExists) { + File file = new File(textField.getText()); + if (file.exists()) { + guiLogger.clearError(errorLabel); + config.set(option, textField.getText()); + } else { + guiLogger.errorOccurred(errorLabel, "Error: File does not exist"); + } + } + } + } + + public void updateNumReplicates(KeyEvent keyEvent) { + updateTextField(fields.getNumReplicatesTextField(), ConfigOption.NUMBER_OF_REPLICATES, keyEvent, + fields.getReplicatesErrorLabelOutputSheet(), validator.failedEmptyReplicates(), + true, false); + } + + + public void updateOutputFilename(KeyEvent keyEvent) { + updateTextField(fields.getOutputFileTextField(), ConfigOption.OUTPUT_FILE, keyEvent, + fields.getOutputFilenameErrorLabel(), validator.failedEmptyOutputFilename(), + false, false); + } + + public void updateInputFile(KeyEvent keyEvent) { + updateTextField(fields.getInputFileTextField(), ConfigOption.INPUT_FILE, keyEvent, + fields.getInputFileExistsLabel(), validator.failedEmptyInputFile(), + false, true); + } + + + public void updateSampleSortingMethod() { + config.set(ConfigOption.SORT_OPTION, fields.getSampleSortingMethodChoiceBox().getValue()); + } + + 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..40e9c81 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetFields.java @@ -0,0 +1,90 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.*; +import org.awdevelopment.smithlab.config.SortOption; + +public class OutputSheetFields extends AbstractFields{ + + private final MainApplicationController controller; + + OutputSheetFields(MainApplicationController controller) { + this.controller = controller; + } + + 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 TabPane getTabPane() { + return controller.tabPane; + } + + public Tab getOutputSheetsTab() { + return controller.outputSheetsTab; + } + + public Tab getEmptyInputSheetTab() { + return controller.emptyInputSheetTab; + } + + public RadioButton getOutputStylePrismRadioButton() { + return controller.outputStylePrismRadioButton; + } + + public TextField getInputFileTextField() { + return controller.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 TextField getOutputFileTextField() { + return controller.outputFileTextField; + } + + public CheckBox getAddSheetsToInputFileCheckbox() { + return controller.addSheetsToInputFileCheckbox; + } + + public TextField getNumReplicatesTextField() { + return controller.numReplicatesTextField; + } + + public ChoiceBox getSampleSortingMethodChoiceBox() { + return controller.sampleSortingMethodChoiceBox; + } + + public RadioButton[] getRadioButtons() { + return controller.radioButtons; + } +} + 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..d200cdf --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java @@ -0,0 +1,88 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import org.awdevelopment.smithlab.config.OutputSheetsConfig; + +public class OutputSheetValidator extends AbstractValidator { + + private final OutputSheetFields fields; + private final GUILogger guiLogger; + private final OutputSheetsConfig config; + private boolean failedEmptyReplicates = false; + private boolean failedEmptyOutputFilename = false; + private boolean failedEmptyInputFile = false; + + protected OutputSheetValidator(OutputSheetFields fields, GUILogger guiLogger, OutputSheetsConfig config) { + super(fields, guiLogger, config); + this.fields = fields; + this.guiLogger = guiLogger; + this.config = config; + } + + @Override + public boolean preliminaryFieldsValid() { return fieldsValid(false); } + + @Override + public boolean fieldsValid() { return fieldsValid(true); } + + private boolean fieldsValid(boolean preventEmpty) { + return numReplicatesTextFieldValid(preventEmpty) + && outputFilenameTextFieldValid(preventEmpty) + && inputFileTextFieldValid(preventEmpty) + && sampleSortingValid() + && outputTypeValid(); + } + + @Override + boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolean preventEmpty) { + boolean failedBoolean = switch (textField.getId()) { + case "numReplicatesTextField" -> failedEmptyReplicates; + case "outputFilenameTextField" -> failedEmptyOutputFilename; + case "outputFilenameEmptyInputSheetsTextField" -> failedEmptyInputFile; + default -> false; + }; + if (textField.getText().isEmpty() && (preventEmpty || failedBoolean)) { + guiLogger.errorOccurred(statusLabel, "Error: Please enter a value"); + return false; + } else { + guiLogger.clearError(statusLabel); + return true; + } + } + + boolean inputFileTextFieldValid(boolean preventEmpty) { + return validateTextFieldFilename(fields.getInputFileTextField(), + fields.getInputFileExistsLabel(), preventEmpty); + } + + boolean outputFilenameTextFieldValid(boolean preventEmpty) { + if (fields.getAddSheetsToInputFileCheckbox().isSelected()) return true; + else return validateTextFieldFilename(fields.getOutputFileTextField(), + fields.getOutputFilenameErrorLabel(), preventEmpty); + } + + boolean numReplicatesTextFieldValid(boolean preventEmpty) { + if (validateTextFieldByte(fields.getNumReplicatesTextField(), + fields.getReplicatesErrorLabelOutputSheet(), preventEmpty)) { + config.setNumberOfReplicates(Byte.parseByte(fields.getNumReplicatesTextField().getText())); + return true; + } else return false; + } + + boolean sampleSortingValid() { + return fields.getSampleSortingMethodChoiceBox().getValue() != null; + } + + boolean outputTypeValid() { + return fields.getOutputStylePrismRadioButton().isSelected() + ^ fields.getOutputStyleTestsRadioButton().isSelected() + ^ fields.getOutputStyleRawRadioButton().isSelected() + ^ fields.getOutputStyleBothRadioButton().isSelected(); + } + + boolean failedEmptyReplicates() { return failedEmptyReplicates; } + boolean failedEmptyOutputFilename() { return failedEmptyOutputFilename; } + boolean failedEmptyInputFile() { return failedEmptyInputFile; } + +} 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..7b8615a 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/input/InputReader.java +++ b/src/main/java/org/awdevelopment/smithlab/io/input/InputReader.java @@ -1,6 +1,7 @@ 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; @@ -11,7 +12,7 @@ public class InputReader { 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/output/OutputGenerator.java b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java index 1e33c5a..73e9494 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java @@ -1,6 +1,8 @@ package org.awdevelopment.smithlab.io.output; import org.awdevelopment.smithlab.config.Config; +import org.awdevelopment.smithlab.config.ConfigDefaults; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; import org.awdevelopment.smithlab.config.Mode; import org.awdevelopment.smithlab.config.OutputSheetsConfig; import org.awdevelopment.smithlab.data.experiment.EmptyExperiment; @@ -64,7 +66,7 @@ public OutputGenerator(Config config, LoggerHelper logger) throws NoDaysExceptio } } - public OutputGenerator(OutputSheetsConfig config) throws NoDaysException { + public OutputGenerator(OutputSheetsConfig config) { LOGGER = config.LOGGER(); outputStyle = switch (config.outputType()) { case PRISM -> new PrismOutputStyle(config.sortOption()); @@ -78,6 +80,17 @@ public OutputGenerator(OutputSheetsConfig config) throws NoDaysException { this.inputFile = config.inputFile(); this.GUI = config.GUI(); } + + public OutputGenerator(EmptyInputSheetConfig config) { + LOGGER = config.LOGGER(); + outputFileName = ConfigDefaults.EMPTY_INPUT_SHEET_FILENAME; + emptyInputSheetName = config.emptyInputSheetName(); + includeBaselineColumn = config.includeBaselineColumn(); + emptyExperiment = new EmptyExperiment(config.strains(), config.conditions(), config.numReplicates(), + config.days(), config.numDays()); + GUI = false; + } + public void generateOutput(Experiment experiment) throws OutputException { XlsxOutputWriter writer = new XlsxOutputWriter(outputStyle, writeToDifferentFile, inputFile); try { diff --git a/src/main/resources/fxml/application.fxml b/src/main/resources/fxml/application.fxml index 36bb789..42b54d5 100644 --- a/src/main/resources/fxml/application.fxml +++ b/src/main/resources/fxml/application.fxml @@ -13,7 +13,7 @@ - + @@ -52,10 +52,10 @@ - - - - + + + + From 82746531f6924090a0ff1c4a27b4a8c676b011b3 Mon Sep 17 00:00:00 2001 From: Adam W Date: Sat, 8 Feb 2025 20:58:56 -0500 Subject: [PATCH 3/9] Finished refactor --- .../smithlab/MainApplication.java | 9 +- .../config/EmptyInputSheetConfig.java | 19 ++ .../controllers/AbstractLabelController.java | 5 +- .../main/EmptyInputSheetConfigUpdater.java | 108 ++++++++ .../main/EmptyInputSheetFields.java | 1 + .../main/EmptyInputSheetValidator.java | 231 +++++++++--------- .../main/EmptyInputSheetsConfigUpdater.java | 4 - .../main/MainApplicationController.java | 228 +++-------------- .../smithlab/io/input/InputReader.java | 3 +- .../smithlab/io/output/OutputGenerator.java | 69 ++---- 10 files changed, 316 insertions(+), 361 deletions(-) create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java delete mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java diff --git a/src/main/java/org/awdevelopment/smithlab/MainApplication.java b/src/main/java/org/awdevelopment/smithlab/MainApplication.java index 7a94f40..159c750 100644 --- a/src/main/java/org/awdevelopment/smithlab/MainApplication.java +++ b/src/main/java/org/awdevelopment/smithlab/MainApplication.java @@ -3,7 +3,7 @@ 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; @@ -24,13 +24,14 @@ public static void main(String[] args) { Arguments arguments = new Arguments(args, LOGGER); try { if (arguments.getMode() == Mode.GENERATE_EMPTY_INPUT_SHEET) { - OutputSheetsConfig config = new OutputSheetsConfig(arguments, LOGGER); - OutputGenerator outputGenerator = new OutputGenerator(config, LOGGER); + EmptyInputSheetConfig config = new EmptyInputSheetConfig(arguments, LOGGER); + OutputGenerator outputGenerator = new OutputGenerator(config); outputGenerator.generateEmptyInputSheet(); } 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/config/EmptyInputSheetConfig.java b/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java index c26e04d..d4bf95e 100644 --- a/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java +++ b/src/main/java/org/awdevelopment/smithlab/config/EmptyInputSheetConfig.java @@ -1,5 +1,6 @@ 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; @@ -27,6 +28,24 @@ public EmptyInputSheetConfig(LoggerHelper logger) { 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); } 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 193b495..40176c5 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java @@ -1,11 +1,10 @@ package org.awdevelopment.smithlab.gui.controllers; -import org.awdevelopment.smithlab.config.Config; import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; public class AbstractLabelController extends AbstractController { - private Config config; + private EmptyInputSheetConfig config; public AbstractLabelController() { super(); } @@ -13,7 +12,7 @@ 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/main/EmptyInputSheetConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java new file mode 100644 index 0000000..1eebd48 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java @@ -0,0 +1,108 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.event.ActionEvent; +import javafx.scene.control.RadioButton; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; +import org.awdevelopment.smithlab.config.SampleLabelingType; +import org.awdevelopment.smithlab.logging.LoggerHelper; + +public class EmptyInputSheetConfigUpdater { + + private final EmptyInputSheetConfig config; + private final MainApplicationController controller; + private final EmptyInputSheetFields fields; + private final EmptyInputSheetValidator validator; + private final GUILogger guiLogger; + + + + EmptyInputSheetConfigUpdater(MainApplicationController controller, EmptyInputSheetConfig config) { + this.controller = controller; + this.config = config; + this.fields = controller.emptyInputSheetFields; + this.validator = controller.emptyInputSheetValidator; + this.guiLogger = controller.guiLogger; + } + + public void updateNumReplicates(KeyEvent keyEvent) { + controller.emptyInputValidateFields(); + if (fields.getNumReplicatesTextField().getText().isEmpty()&& !validator.failedEmptyNumReplicates()) + guiLogger.clearError(fields.getStatusLabel()); + if ( keyEvent == null + || !(fields.getNumReplicatesTextField().getScene().focusOwnerProperty().get().getId().equals("numReplicatesEmptyInputSheetTextField")) + || keyEvent.getCode() == KeyCode.ENTER + || keyEvent.getCode() == KeyCode.TAB + ) { + validator.validateNumReplicates(false); + } + } + + public void updateNumTimepoints(KeyEvent keyEvent) { + controller.emptyInputValidateFields(); + if (fields.getNumTimepointsTextField().getText().isEmpty() && !validator.failedEmptyNumTimepoints()) + guiLogger.clearError(fields.getStatusLabel()); + if ( keyEvent == null + || !(fields.getNumTimepointsTextField().getScene().focusOwnerProperty().get().getId().equals("numTimepointsEmptyInputSheetTextField")) + || keyEvent.getCode() == KeyCode.ENTER + || keyEvent.getCode() == KeyCode.TAB + ) { + validator.validateNumTimepoints(false); + } + } + + public void updateNumConditions(KeyEvent keyEvent) { + controller.emptyInputValidateFields(); + if(fields.getNumConditionsTextField().getText().isEmpty() && !validator.failedEmptyConditions()) guiLogger.clearError(fields.getStatusLabel()); + if ( keyEvent == null + || !(fields.getNumConditionsTextField().getScene().focusOwnerProperty().get().getId().equals("numConditionsTextField")) + || keyEvent.getCode() == KeyCode.ENTER + || keyEvent.getCode() == KeyCode.TAB + ) { + validator.validateNumConditions(false); + } + } + + public void updateNumStrains(KeyEvent keyEvent) { + controller.emptyInputValidateFields(); + if(fields.getNumStrainsTextField().getText().isEmpty() && !validator.failedEmptyStrains()) guiLogger.clearError(fields.getStatusLabel()); + if ( keyEvent == null + || !(fields.getNumStrainsTextField().getScene().focusOwnerProperty().get().getId().equals("numStrainsTextField")) + || keyEvent.getCode() == KeyCode.ENTER + || keyEvent.getCode() == KeyCode.TAB + ) { + validator.validateNumStrains(false); + } + } + + public void updateSampleLabelingRadioButtons(ActionEvent actionEvent) { + controller.emptyInputValidateFields(); + RadioButton selectedRadioButton = (RadioButton) actionEvent.getSource(); + for (RadioButton radioButton : fields.getSampleLabelingRadioButtons()) + if (!radioButton.equals(selectedRadioButton)) 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(); + } + + public void handleIncludeBaselineColumn() { + controller.emptyInputValidateFields(); + 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 index 53d64ab..75c3be4 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetFields.java @@ -31,4 +31,5 @@ public class EmptyInputSheetFields extends AbstractFields { public TimepointsController getTimepointsController() { return controller.timepointsController; } public StrainsController getStrainsController() { return controller.strainsController; } public ConditionsController getConditionsController() { return controller.conditionsController; } + public RadioButton[] getSampleLabelingRadioButtons() { return controller.sampleLabelingRadioButtons; } } 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 index 3043c7d..f55bf86 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetValidator.java @@ -9,11 +9,11 @@ public class EmptyInputSheetValidator extends AbstractValidator { private final EmptyInputSheetFields fields; private final GUILogger guiLogger; private final EmptyInputSheetConfig config; - private boolean failedEmptyNumTimepointsEmptyInputSheet = false; - private boolean failedEmptyNumReplicatesEmptyInputSheet = false; - private boolean failedEmptyOutputFilenameEmptyInputSheet = false; - private boolean failedEmptyConditionsEmptyInputSheet = false; - private boolean failedEmptyStrainsEmptyInputSheet = false; + private boolean failedEmptyNumTimepoints = false; + private boolean failedEmptyNumReplicates = false; + private boolean failedEmptyOutputFilename = false; + private boolean failedEmptyConditions = false; + private boolean failedEmptyStrains = false; public EmptyInputSheetValidator(EmptyInputSheetFields fields, GUILogger guiLogger, EmptyInputSheetConfig config) { super(fields, guiLogger, config); @@ -23,18 +23,70 @@ public EmptyInputSheetValidator(EmptyInputSheetFields fields, GUILogger guiLogge } @Override - public boolean preliminaryFieldsValid() { - return false; + public boolean preliminaryFieldsValid() { return fieldsValid(false); } + @Override + public boolean fieldsValid() { return fieldsValid(true); } + + boolean fieldsValid(boolean preventEmpty) { + return validateNumTimepoints(preventEmpty) + && validateNumReplicates(preventEmpty) + && validateOutputFilename(preventEmpty) + && validateNumConditions(preventEmpty) + && validateNumStrains(preventEmpty) + && validateSampleLabelingRadioButtons(); } - @Override - public boolean fieldsValid() { - return false; + protected boolean fieldsValidExceptNumTimepoints() { + return validateNumReplicates(false) + && validateOutputFilename(false) + && validateNumConditions(false) + && validateNumStrains(false) + && validateSampleLabelingRadioButtons(); + } + + protected boolean fieldsValidExceptNumReplicates() { + return validateNumTimepoints(false) + && validateOutputFilename(false) + && validateNumConditions(false) + && validateNumStrains(false) + && validateSampleLabelingRadioButtons(); + } + + protected boolean fieldsValidExceptOutputFilename() { + return validateNumTimepoints(false) + && validateNumReplicates(false) + && validateNumConditions(false) + && validateNumStrains(false) + && validateSampleLabelingRadioButtons(); + } + + protected boolean fieldsValidExceptNumConditions() { + return validateNumTimepoints(false) + && validateNumReplicates(false) + && validateOutputFilename(false) + && validateNumStrains(false) + && validateSampleLabelingRadioButtons(); + } + + protected boolean fieldsValidExceptNumStrains() { + return validateNumTimepoints(false) + && validateNumReplicates(false) + && validateOutputFilename(false) + && validateNumConditions(false) + && validateSampleLabelingRadioButtons(); } - private boolean validateNumTimepoints(boolean preventEmpty) { + boolean validateNumTimepoints(boolean preventEmpty) { if (fields.getTimepointsController() == null || fields.getTimepointsController().usingNumDays()) { - return validateTextFieldByte(fields.getNumTimepointsTextField(), fields.getNumTimepointsErrorLabel(), preventEmpty); + if (validateTextFieldByte(fields.getNumTimepointsTextField(), fields.getNumTimepointsErrorLabel(), preventEmpty)) { + failedEmptyNumTimepoints = false; + return true; + } else { + if (preventEmpty && !validateTextFieldNotEmpty(fields.getNumTimepointsTextField(), fields.getNumTimepointsErrorLabel(), true)) { + failedEmptyNumTimepoints = true; + } + return false; + } } else { guiLogger.clearError(fields.getNumTimepointsErrorLabel()); config.setUsingNumDays(fields.getTimepointsController().usingNumDays()); @@ -43,140 +95,87 @@ private boolean validateNumTimepoints(boolean preventEmpty) { } } - private boolean validateOutputFilename(boolean preventEmpty) { - return validateTextFieldFilename(fields.getOutputFilenameTextField(), fields.getStatusLabel(), preventEmpty); - } - - private boolean checkOutputFilename(TextField textField, boolean failedEmptyBoolean, Label errorLabel) { - if (outputFilenameEmptyInputSheetsTextField.getText().isEmpty() && !failedEmptyOutputFilenameEmptyInputSheet) { - guiLogger.clearError(statusLabelEmptyInputSheets); + boolean validateOutputFilename(boolean preventEmpty) { + if (validateTextFieldFilename(fields.getOutputFilenameTextField(), fields.getStatusLabel(), preventEmpty)) { + failedEmptyOutputFilename = false; return true; - } else if (outputFilenameEmptyInputSheetsTextField.getText().isEmpty()){ - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter an output filename"); - return false; - } else if (!outputFilenameEmptyInputSheetsTextField.getText().endsWith(".xlsx")) { - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Output filename must end in .xlsx"); - return false; } else { - guiLogger.clearError(statusLabelEmptyInputSheets); - return true; + if (preventEmpty && !validateTextFieldNotEmpty(fields.getOutputFilenameTextField(), fields.getStatusLabel(), true)) { + failedEmptyOutputFilename = true; + } + return false; } } - private boolean checkNumReplicatesEmptyInputSheet() { - if (numReplicatesEmptyInputSheetTextField.getText().isEmpty() && !failedEmptyNumReplicatesEmptyInputSheet) { - guiLogger.clearError(statusLabelEmptyInputSheets); + boolean validateNumReplicates(boolean preventEmpty) { + if (validateTextFieldByte(fields.getNumReplicatesTextField(), fields.getStatusLabel(), preventEmpty)) { + failedEmptyNumReplicates = false; return true; - } - else if (numReplicatesEmptyInputSheetTextField.getText().isEmpty()) { - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter a number of replicates"); - return false; - } - try { - long numReplicates = Long.parseLong(numReplicatesEmptyInputSheetTextField.getText()); - if (numReplicates < 1){ - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Number must be > 0"); - return false; - } - else if (numReplicates > 127) { - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Number must be <= 127"); - return false; - } - else { - guiLogger.clearError(statusLabelEmptyInputSheets); - return true; + } else { + if (preventEmpty && !validateTextFieldNotEmpty(fields.getNumReplicatesTextField(), fields.getStatusLabel(), true)) { + failedEmptyNumReplicates = true; } - } catch (NumberFormatException e) { - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Invalid number: \"" + numReplicatesEmptyInputSheetTextField.getText() + "\""); return false; } } - private boolean checkNumConditionsEmptyInputSheet() { - if (conditionsController == null || conditionsController.usingNumConditions()) { - if (numConditionsTextField.getText().isEmpty() && !failedEmptyConditionsEmptyInputSheet) { - guiLogger.clearError(numConditionsErrorLabel); + boolean validateNumConditions(boolean preventEmpty) { + if (fields.getConditionsController() == null || fields.getConditionsController().usingNumConditions()) { + if (validateTextFieldByte(fields.getNumConditionsTextField(), fields.getNumConditionsErrorLabel(), preventEmpty)) { + failedEmptyConditions = false; return true; - } else if (numConditionsTextField.getText().isEmpty()) { - guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Please enter a number of conditions"); - return false; } else { - try { - long numConditions = Long.parseLong(numConditionsTextField.getText()); - if (numConditions < 1) { - guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Number of conditions must be > 0"); - return false; - } else if (numConditions > 127) { - guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Number of conditions must be <= 127"); - return false; - } else { - guiLogger.clearError(numConditionsErrorLabel); - config.setNumConditions((byte) numConditions); - return true; - } - } catch (NumberFormatException e) { - guiLogger.errorOccurred(numConditionsErrorLabel, "Error: Invalid number: \"" + numConditionsTextField.getText() + "\""); - return false; + if (preventEmpty && !validateTextFieldNotEmpty(fields.getNumConditionsTextField(), fields.getNumConditionsErrorLabel(), true)) { + failedEmptyConditions = true; } + return false; } } else { - guiLogger.clearError(numConditionsErrorLabel); - config.setUsingNumConditions(conditionsController.usingNumConditions()); - config.setConditions(conditionsController.getConditions()); + guiLogger.clearError(fields.getNumConditionsErrorLabel()); + config.setUsingNumConditions(fields.getConditionsController().usingNumConditions()); + config.setConditions(fields.getConditionsController().getConditions()); return true; } } - private boolean checkNumStrainsEmptyInputSheet() { - if (strainsController == null || strainsController.usingNumStrains()) { - if (numStrainsTextField.getText().isEmpty() && !failedEmptyStrainsEmptyInputSheet) { - guiLogger.clearError(numStrainErrorLabel); + boolean validateNumStrains(boolean preventEmpty) { + if (fields.getStrainsController() == null || fields.getStrainsController().usingNumStrains()) { + if (validateTextFieldByte(fields.getNumStrainsTextField(), fields.getNumStrainErrorLabel(), preventEmpty)) { + failedEmptyStrains = false; return true; - } else if (numStrainsTextField.getText().isEmpty()) { - guiLogger.errorOccurred(numStrainErrorLabel, "Error: Please enter a number of strains"); - return false; } else { - try { - long numStrains = Long.parseLong(numStrainsTextField.getText()); - if (numStrains < 1) { - guiLogger.errorOccurred(numStrainErrorLabel, "Error: Number of strains must be > 0"); - return false; - } else if (numStrains > 127) { - guiLogger.errorOccurred(numStrainErrorLabel, "Error: Number of strains must be <= 127"); - return false; - } else { - guiLogger.clearError(numStrainErrorLabel); - config.setNumStrains((byte) numStrains); - return true; - } - } catch (NumberFormatException e) { - guiLogger.errorOccurred(numStrainErrorLabel, "Error: Invalid number: \"" + numStrainsTextField.getText() + "\""); - return false; + if (preventEmpty && !validateTextFieldNotEmpty(fields.getNumStrainsTextField(), fields.getNumStrainErrorLabel(), true)) { + failedEmptyStrains = true; } + return false; } } else { - guiLogger.clearError(numStrainErrorLabel); - config.setUsingNumStrains(strainsController.usingNumStrains()); - config.setStrains(strainsController.getStrains()); + guiLogger.clearError(fields.getNumStrainErrorLabel()); + config.setUsingNumStrains(fields.getStrainsController().usingNumStrains()); + config.setStrains(fields.getStrainsController().getStrains()); return true; } } - private boolean checkSampleLabelingRadioButtons() { - if (!conditionLabelingRadioButton.isSelected() && !strainLabelingRadioButton.isSelected() && !conditionAndStrainLabelingRadioButton.isSelected()) { - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please select a sample labeling method"); + boolean validateSampleLabelingRadioButtons() { + if (!fields.getStrainLabelingRadioButton().isSelected() + && !fields.getConditionLabelingRadioButton().isSelected() + && !fields.getConditionAndStrainLabelingRadioButton().isSelected()) { + guiLogger.errorOccurred(fields.getStatusLabel(), "Error: Please select a sample labeling option"); return false; + } else { + guiLogger.clearError(fields.getStatusLabel()); + return true; } - return true; } @Override boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolean preventEmpty) { boolean failedBoolean = switch (textField.getId()) { - case "numReplicatesEmptyInputSheetTextField" -> failedEmptyNumReplicatesEmptyInputSheet; - case "outputFilenameEmptyInputSheetsTextField" -> failedEmptyOutputFilenameEmptyInputSheet; - case "numConditionsTextField" -> failedEmptyConditionsEmptyInputSheet; - case "numStrainsTextField" -> failedEmptyStrainsEmptyInputSheet; - case "numTimepointsTextField" -> failedEmptyNumTimepointsEmptyInputSheet; + case "numReplicatesEmptyInputSheetTextField" -> failedEmptyNumReplicates; + case "outputFilenameEmptyInputSheetsTextField" -> failedEmptyOutputFilename; + case "numConditionsTextField" -> failedEmptyConditions; + case "numStrainsTextField" -> failedEmptyStrains; + case "numTimepointsTextField" -> failedEmptyNumTimepoints; default -> false; }; if (textField.getText().isEmpty() && (preventEmpty || failedBoolean)) { @@ -187,4 +186,14 @@ boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolea return true; } } + + boolean failedEmptyNumTimepoints() { return failedEmptyNumTimepoints; } + + boolean failedEmptyNumReplicates() { return failedEmptyNumReplicates; } + + boolean failedEmptyOutputFilename() { return failedEmptyOutputFilename; } + + boolean failedEmptyConditions() { return failedEmptyConditions; } + + boolean failedEmptyStrains() { return failedEmptyStrains; } } diff --git a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java deleted file mode 100644 index 8813028..0000000 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetsConfigUpdater.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.awdevelopment.smithlab.gui.controllers.main; - -public class EmptyInputSheetsConfigUpdater { -} 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 index 5969896..d55d73d 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java @@ -3,7 +3,6 @@ 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.Stage; @@ -17,6 +16,7 @@ import org.awdevelopment.smithlab.gui.controllers.TimepointsController; 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; @@ -48,6 +48,10 @@ public class MainApplicationController extends AbstractController { 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; @@ -71,6 +75,7 @@ public class MainApplicationController extends AbstractController { private Mode mode = ConfigDefaults.MODE; GUILogger guiLogger; + OutputSheetFields outputSheetFields; OutputSheetValidator outputSheetValidator; OutputSheetConfigUpdater outputSheetConfigUpdater; @@ -91,6 +96,10 @@ public void initialize() { outputSheetFields = new OutputSheetFields(this); outputSheetValidator = new OutputSheetValidator(outputSheetFields, guiLogger, config.getOutputSheetsConfig()); outputSheetConfigUpdater = new OutputSheetConfigUpdater(this, config.getOutputSheetsConfig()); + // 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()); } private void choiceBoxSetup() { @@ -134,7 +143,7 @@ public void generateOutput() { generateOutputSheets(); } case GENERATE_EMPTY_INPUT_SHEET -> { - if (notReadyForOutputEmptyInputSheet()) return; + if (!emptyInputSheetValidator.fieldsValid()) return; generateEmptyInputSheet(); } case IMAGE_RECOGNITION -> { @@ -190,7 +199,7 @@ private void generateOutputSheets() { public void updateNumReplicates(KeyEvent keyEvent) { outputSheetConfigUpdater.updateNumReplicates(keyEvent); } - public void updateSampleSortingMethod(ActionEvent actionEvent) { outputSheetConfigUpdater.updateSampleSortingMethod(); } + public void updateSampleSortingMethod() { outputSheetConfigUpdater.updateSampleSortingMethod(); } public void updateOutputReplicatesFields() { outputSheetConfigUpdater.updateNumReplicates(null); @@ -221,79 +230,11 @@ public void updateMode() { // } } - 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; - guiLogger.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; - guiLogger.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; - guiLogger.errorOccurred(statusLabelEmptyInputSheets, "Error: Please enter an output filename"); - return true; - } else if (!outputFilename.endsWith(".xlsx")) { - guiLogger.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; - guiLogger.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; - guiLogger.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) { + outputGenerator = new OutputGenerator(config.getEmptyInputSheetConfig()); + } catch (NoDaysException | NoStrainsOrConditionsException e) { guiLogger.errorOccurred(statusLabelEmptyInputSheets, e.getMessage()); return; } @@ -310,151 +251,52 @@ private void generateEmptyInputSheet() { public void emptyInputValidateFields() { String focusID = (outputFilenameEmptyInputSheetsTextField.getScene().getFocusOwner().getId()); if (timepointsController != null) { - config.setUsingNumDays(timepointsController.usingNumDays()); - config.setDays(timepointsController.getDays()); + config.getEmptyInputSheetConfig().setUsingNumDays(timepointsController.usingNumDays()); + config.getEmptyInputSheetConfig().setDays(timepointsController.getDays()); } if (strainsController != null) { - config.setUsingNumStrains(strainsController.usingNumStrains()); - config.setStrains(strainsController.getStrains()); + config.getEmptyInputSheetConfig().setUsingNumStrains(strainsController.usingNumStrains()); + config.getEmptyInputSheetConfig().setStrains(strainsController.getStrains()); } if (conditionsController != null) { - config.setUsingNumConditions(conditionsController.usingNumConditions()); - config.setConditions(conditionsController.getConditions()); + config.getEmptyInputSheetConfig().setUsingNumConditions(conditionsController.usingNumConditions()); + config.getEmptyInputSheetConfig().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(); - } - } - } - - - 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); - } + case "numReplicatesEmptyInputSheetTextField" -> emptyInputSheetValidator.fieldsValidExceptNumReplicates(); + case "numTimepointsTextField" -> emptyInputSheetValidator.fieldsValidExceptNumTimepoints(); + case "outputFilenameEmptyInputSheetsTextField" -> emptyInputSheetValidator.fieldsValidExceptOutputFilename(); + case "numConditionsTextField" -> emptyInputSheetValidator.fieldsValidExceptNumConditions(); + case "numStrainsTextField" -> emptyInputSheetValidator.fieldsValidExceptNumStrains(); + default -> emptyInputSheetValidator.fieldsValid(); } - checkSampleLabelingRadioButtons(); } public void openConditionsFXML() { emptyInputValidateFields(); - SceneLoader.loadScene(new Stage(), FXMLResourceType.CONDITIONS, getLogger(), config); + conditionsController = (ConditionsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.CONDITIONS, getLogger(), config.getEmptyInputSheetConfig()); } public void openStrainsFXML() { emptyInputValidateFields(); - SceneLoader.loadScene(new Stage(), FXMLResourceType.STRAINS, getLogger(), config); + strainsController = (StrainsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.STRAINS, getLogger(), config.getEmptyInputSheetConfig()); } public void openTimepointsFXML() { emptyInputValidateFields(); - Stage stage = new Stage(); - timepointsController = (TimepointsController) SceneLoader.loadScene(stage, FXMLResourceType.TIMEPOINTS, getLogger(), config); + timepointsController = (TimepointsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.TIMEPOINTS, getLogger(), config.getEmptyInputSheetConfig()); } - public void updateNumReplicateEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if (numReplicatesEmptyInputSheetTextField.getText().isEmpty() && !failedEmptyNumReplicatesEmptyInputSheet) guiLogger.clearError(statusLabelEmptyInputSheets); - if ( keyEvent == null - || !(numReplicatesEmptyInputSheetTextField.getScene().focusOwnerProperty().get().getId().equals("numReplicatesEmptyInputSheetTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumReplicatesEmptyInputSheet(); - } - } + public void updateSampleLabelingRadioButtons(ActionEvent actionEvent) { emptyInputSheetConfigUpdater.updateSampleLabelingRadioButtons(actionEvent); } - public void updateNumTimepointsEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if (numTimepointsTextField.getText().isEmpty() && !failedEmptyNumTimepointsEmptyInputSheet) - guiLogger.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) { emptyInputSheetConfigUpdater.updateNumConditions(keyEvent); } - public void updateNumConditionsEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if(numConditionsTextField.getText().isEmpty() && !failedEmptyConditionsEmptyInputSheet) guiLogger.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) { emptyInputSheetConfigUpdater.updateNumStrains(keyEvent); } - public void updateNumStrainsEmptyInputSheet(KeyEvent keyEvent) { - emptyInputValidateFields(); - if(numStrainsTextField.getText().isEmpty() && !failedEmptyStrainsEmptyInputSheet) guiLogger.clearError(statusLabelEmptyInputSheets); - if ( keyEvent == null - || !(numStrainsTextField.getScene().focusOwnerProperty().get().getId().equals("numStrainsTextField")) - || keyEvent.getCode() == KeyCode.ENTER - || keyEvent.getCode() == KeyCode.TAB - ) { - checkNumStrainsEmptyInputSheet(); - } - } + public void updateNumReplicateEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateNumReplicates(keyEvent); } - public void handleIncludeBaselineColumn() { - emptyInputValidateFields(); - config.setIncludeBaselineColumn(includeBaselineColumnCheckbox.isSelected()); } + public void updateNumTimepointsEmptyInputSheet(KeyEvent keyEvent) { emptyInputSheetConfigUpdater.updateNumTimepoints(keyEvent); } + public void handleIncludeBaselineColumn() { emptyInputSheetConfigUpdater.handleIncludeBaselineColumn(); } } 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 7b8615a..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,6 +1,5 @@ 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; @@ -8,7 +7,7 @@ public class InputReader { - private final Config config; + private final OutputSheetsConfig config; private final LoggerHelper LOGGER; 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 73e9494..ec70d2b 100644 --- a/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java +++ b/src/main/java/org/awdevelopment/smithlab/io/output/OutputGenerator.java @@ -1,9 +1,7 @@ package org.awdevelopment.smithlab.io.output; -import org.awdevelopment.smithlab.config.Config; import org.awdevelopment.smithlab.config.ConfigDefaults; import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; -import org.awdevelopment.smithlab.config.Mode; import org.awdevelopment.smithlab.config.OutputSheetsConfig; import org.awdevelopment.smithlab.data.experiment.EmptyExperiment; import org.awdevelopment.smithlab.io.exceptions.NoDaysException; @@ -27,45 +25,6 @@ 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); - } - 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; - } - } - public OutputGenerator(OutputSheetsConfig config) { LOGGER = config.LOGGER(); outputStyle = switch (config.outputType()) { @@ -79,16 +38,23 @@ public OutputGenerator(OutputSheetsConfig config) { this.writeToDifferentFile = config.writeToDifferentFile(); this.inputFile = config.inputFile(); this.GUI = config.GUI(); + // these are not used in this constructor + emptyInputSheetName = null; + includeBaselineColumn = false; + emptyExperiment = null; } - public OutputGenerator(EmptyInputSheetConfig config) { + public OutputGenerator(EmptyInputSheetConfig config) throws NoDaysException, NoStrainsOrConditionsException { LOGGER = config.LOGGER(); outputFileName = ConfigDefaults.EMPTY_INPUT_SHEET_FILENAME; emptyInputSheetName = config.emptyInputSheetName(); includeBaselineColumn = config.includeBaselineColumn(); - emptyExperiment = new EmptyExperiment(config.strains(), config.conditions(), config.numReplicates(), - config.days(), config.numDays()); + 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 { @@ -116,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()); + } + } } From 03206706706ba4df594c7a2858b3c3ab3dab01e6 Mon Sep 17 00:00:00 2001 From: Adam W Date: Mon, 10 Feb 2025 10:56:32 -0500 Subject: [PATCH 4/9] Debugging refactor --- .../smithlab/gui/FXMLResourceLoader.java | 2 +- .../gui/FailedToLoadFXMLException.java | 14 +++---- .../smithlab/gui/SceneLoader.java | 2 + .../gui/controllers/AbstractController.java | 4 +- .../controllers/AbstractLabelController.java | 2 +- .../gui/controllers/ConditionsController.java | 3 ++ .../gui/controllers/StrainsController.java | 3 ++ .../gui/controllers/TimepointsController.java | 3 ++ .../main/AbstractConfigUpdater.java | 15 +++++++ .../gui/controllers/main/AbstractFields.java | 2 +- .../controllers/main/AbstractValidator.java | 15 +++++++ .../main/EmptyInputSheetConfigUpdater.java | 40 ++++++++++++++++++- .../main/MainApplicationController.java | 19 +++++---- .../main/OutputSheetConfigUpdater.java | 29 +++++--------- .../main/OutputSheetValidator.java | 2 +- src/main/resources/fxml/application.fxml | 40 +++++++++---------- 16 files changed, 133 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractConfigUpdater.java 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 67e6b30..12ed49d 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/SceneLoader.java @@ -10,6 +10,7 @@ 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(); @@ -19,6 +20,7 @@ public static AbstractLabelController loadScene(Stage stage, FXMLResourceType fx 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 40176c5..f627bae 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/AbstractLabelController.java @@ -2,7 +2,7 @@ import org.awdevelopment.smithlab.config.EmptyInputSheetConfig; -public class AbstractLabelController extends AbstractController { +public abstract class AbstractLabelController extends AbstractController { private EmptyInputSheetConfig 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/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..0400430 --- /dev/null +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractConfigUpdater.java @@ -0,0 +1,15 @@ +package org.awdevelopment.smithlab.gui.controllers.main; + +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyEvent; +import org.awdevelopment.smithlab.config.ConfigOption; + +public abstract class AbstractConfigUpdater { + + public abstract void updateFields(); + + public abstract void updateSampleSortingMethod(); + + abstract void updateTextField(TextField textField, ConfigOption option, KeyEvent keyEvent, Label errorLabel, boolean failedBoolean, boolean byteParse, boolean fileExists); +} 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 index 606da29..3dea51f 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractFields.java @@ -1,4 +1,4 @@ package org.awdevelopment.smithlab.gui.controllers.main; -public class AbstractFields { +public abstract class AbstractFields { } 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 index 9b4f1f3..8e892f8 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/AbstractValidator.java @@ -4,6 +4,8 @@ import javafx.scene.control.TextField; import org.awdevelopment.smithlab.config.AbstractConfig; +import java.io.File; + public abstract class AbstractValidator { private final AbstractFields fields; @@ -22,6 +24,19 @@ protected AbstractValidator(AbstractFields fields, GUILogger guiLogger, Abstract abstract boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolean preventEmpty); + protected boolean validateTextFieldFileExists(TextField textField, Label statusLabel, boolean preventEmpty) { + if (!validateTextFieldFilename(textField, statusLabel, preventEmpty)) return false; + String filePath = textField.getText(); + File file = new File(filePath); + if (!file.exists()) { + guiLogger.errorOccurred(statusLabel, "Error: File does not exist"); + return false; + } else { + guiLogger.clearError(statusLabel); + return true; + } + } + protected boolean validateTextFieldFilename(TextField textField, Label statusLabel, boolean preventEmpty) { if (!validateTextFieldNotEmpty(textField, statusLabel, preventEmpty)) return false; if (!textField.getText().endsWith(".xlsx")) { 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 index 1eebd48..2a085bd 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/EmptyInputSheetConfigUpdater.java @@ -1,14 +1,16 @@ package org.awdevelopment.smithlab.gui.controllers.main; import javafx.event.ActionEvent; +import javafx.scene.control.Label; import javafx.scene.control.RadioButton; +import javafx.scene.control.TextField; import javafx.scene.input.KeyCode; 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.logging.LoggerHelper; -public class EmptyInputSheetConfigUpdater { +public class EmptyInputSheetConfigUpdater extends AbstractConfigUpdater { private final EmptyInputSheetConfig config; private final MainApplicationController controller; @@ -26,6 +28,14 @@ public class EmptyInputSheetConfigUpdater { this.guiLogger = controller.guiLogger; } + public void updateFields() { + updateNumConditions(null); + updateNumReplicates(null); + updateNumStrains(null); + updateNumTimepoints(null); + updateSampleSortingMethod(); + } + public void updateNumReplicates(KeyEvent keyEvent) { controller.emptyInputValidateFields(); if (fields.getNumReplicatesTextField().getText().isEmpty()&& !validator.failedEmptyNumReplicates()) @@ -101,6 +111,32 @@ public void updateSampleLabelingRadioButtons(ActionEvent actionEvent) { validator.validateSampleLabelingRadioButtons(); } + public void updateSampleSortingMethod() { + config.setSortOption(fields.getSampleSortingMethodChoiceBox().getValue()); + } + + @Override + void updateTextField(TextField textField, ConfigOption option, KeyEvent keyEvent, Label errorLabel, boolean failedBoolean, boolean byteParse, boolean fileExists) { + if (!byteParse && !fileExists) config.set(option, textField.getText()); + if (textField.getText().isEmpty() && !failedBoolean) guiLogger.clearError(errorLabel); + if (keyEvent == null + || !(textField.getScene().focusOwnerProperty().get().getId().equals(textField.getId())) + || keyEvent.getCode() == KeyCode.ENTER + || keyEvent.getCode() == KeyCode.TAB + ) { + boolean validatedSuccessfully = switch (textField.getId()) { + case "numReplicatesEmptyInputSheetTextField" -> validator.validateNumReplicates(failedBoolean); + case "outputFilenameEmptyInputSheetsTextField" -> validator.validateOutputFilename(failedBoolean); + case "numConditionsTextField" -> validator.validateNumConditions(failedBoolean); + case "numStrainsTextField" -> validator.validateNumStrains(failedBoolean); + case "numTimepointsTextField" -> validator.validateNumTimepoints(failedBoolean); + default -> throw new IllegalStateException("Unexpected value: " + textField.getId()); + }; + if (byteParse && validatedSuccessfully) config.set(option, Byte.parseByte(textField.getText())); + if (fileExists && validatedSuccessfully) config.set(option, textField.getText()); + } + } + public void handleIncludeBaselineColumn() { controller.emptyInputValidateFields(); config.setIncludeBaselineColumn(fields.getIncludeBaselineColumnCheckbox().isSelected()); 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 index d55d73d..632777a 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/MainApplicationController.java @@ -84,14 +84,11 @@ public MainApplicationController() { super(); } - public void initialize() { + public void initialize() {} + + public void setup() { config = new ConfigManager(getLogger()); guiLogger = new GUILogger(getLogger()); - // Set up the radio buttons and choice boxes - radioButtonSetup(); - choiceBoxSetup(); - setupErrorLabelsOutputSheet(); - updateFields(); // Initialize support classes for generating output sheets outputSheetFields = new OutputSheetFields(this); outputSheetValidator = new OutputSheetValidator(outputSheetFields, guiLogger, config.getOutputSheetsConfig()); @@ -100,6 +97,10 @@ public void initialize() { emptyInputSheetFields = new EmptyInputSheetFields(this); emptyInputSheetValidator = new EmptyInputSheetValidator(emptyInputSheetFields, guiLogger, config.getEmptyInputSheetConfig()); emptyInputSheetConfigUpdater = new EmptyInputSheetConfigUpdater(this, config.getEmptyInputSheetConfig()); + // Set up the radio buttons and choice boxes + radioButtonSetup(); + choiceBoxSetup(); + setupErrorLabelsOutputSheet(); } private void choiceBoxSetup() { @@ -216,10 +217,6 @@ public void updateInputOutputFields() { outputSheetConfigUpdater.updateOutputFilename(null); } - public void updateFields() { - outputSheetConfigUpdater.updateFields(); - } - public void updateMode() { if (tabPane.getSelectionModel().getSelectedItem() == outputSheetsTab) { mode = Mode.GENERATE_OUTPUT_SHEETS; @@ -273,6 +270,8 @@ public void emptyInputValidateFields() { } } + public void updateFieldsOutputSheets() { outputSheetConfigUpdater.updateFields(); } + public void openConditionsFXML() { emptyInputValidateFields(); conditionsController = (ConditionsController) SceneLoader.loadScene(new Stage(), FXMLResourceType.CONDITIONS, getLogger(), config.getEmptyInputSheetConfig()); 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 index 7075079..7c0cb2e 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetConfigUpdater.java @@ -14,7 +14,7 @@ import java.io.File; -public class OutputSheetConfigUpdater { +public class OutputSheetConfigUpdater extends AbstractConfigUpdater { MainApplicationController controller; private final OutputSheetsConfig config; @@ -45,7 +45,7 @@ public void browseForInputFile() { } public void handleRadioButtonPressOutputSheets(ActionEvent actionEvent) { - controller.updateFields(); + updateFields(); RadioButton radioButton = (RadioButton) actionEvent.getSource(); for (RadioButton otherRadioButton : fields.getRadioButtons()) if (!otherRadioButton.equals(radioButton)) otherRadioButton.setSelected(false); OutputType oldOutputType = config.outputType(); @@ -58,7 +58,7 @@ public void handleRadioButtonPressOutputSheets(ActionEvent actionEvent) { } public void handleAddSheetsCheckbox() { - controller.updateFields(); + updateFields(); if (fields.getAddSheetsToInputFileCheckbox().isSelected()) { fields.getOutputFileTextField().setDisable(true); config.setWriteToDifferentFile(false); @@ -72,29 +72,22 @@ public void handleAddSheetsCheckbox() { } } - private void updateTextField(TextField textField, ConfigOption option, KeyEvent keyEvent, Label errorLabel, boolean failedBoolean, boolean byteParse, boolean fileExists) { + void updateTextField(TextField textField, ConfigOption option, KeyEvent keyEvent, Label errorLabel, boolean failedBoolean, boolean byteParse, boolean fileExists) { if (!byteParse && !fileExists) config.set(option, textField.getText()); - if (textField.getText().isEmpty() && !failedBoolean) guiLogger.clearError(errorLabel); - if ( keyEvent == null + if (textField.getText().isEmpty() && !failedBoolean) guiLogger.clearError(errorLabel); + if (keyEvent == null || !(textField.getScene().focusOwnerProperty().get().getId().equals(textField.getId())) || keyEvent.getCode() == KeyCode.ENTER || keyEvent.getCode() == KeyCode.TAB ) { - switch (textField.getId()) { + boolean validatedSuccessfully = switch (textField.getId()) { case "numReplicatesTextField" -> validator.numReplicatesTextFieldValid(failedBoolean); case "outputFileTextField" -> validator.outputFilenameTextFieldValid(failedBoolean); case "inputFileTextField" -> validator.inputFileTextFieldValid(failedBoolean); - } - if (byteParse) config.set(option, Byte.parseByte(textField.getText())); - if (fileExists) { - File file = new File(textField.getText()); - if (file.exists()) { - guiLogger.clearError(errorLabel); - config.set(option, textField.getText()); - } else { - guiLogger.errorOccurred(errorLabel, "Error: File does not exist"); - } - } + default -> throw new IllegalStateException("Unexpected value: " + textField.getId()); + }; + if (byteParse && validatedSuccessfully) config.set(option, Byte.parseByte(textField.getText())); + if (fileExists && validatedSuccessfully) config.set(option, textField.getText()); } } 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 index d200cdf..3867d76 100644 --- a/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java +++ b/src/main/java/org/awdevelopment/smithlab/gui/controllers/main/OutputSheetValidator.java @@ -52,7 +52,7 @@ boolean validateTextFieldNotEmpty(TextField textField, Label statusLabel, boolea } boolean inputFileTextFieldValid(boolean preventEmpty) { - return validateTextFieldFilename(fields.getInputFileTextField(), + return validateTextFieldFileExists(fields.getInputFileTextField(), fields.getInputFileExistsLabel(), preventEmpty); } diff --git a/src/main/resources/fxml/application.fxml b/src/main/resources/fxml/application.fxml index 42b54d5..7a0d384 100644 --- a/src/main/resources/fxml/application.fxml +++ b/src/main/resources/fxml/application.fxml @@ -15,20 +15,20 @@ - + - - + + - + - + -