From 5f53bb86c639e663aee6daba0f85a476da01d725 Mon Sep 17 00:00:00 2001 From: David Guler Date: Mon, 17 Oct 2022 17:01:50 +0200 Subject: [PATCH 01/83] #11 Very basic implementation with dummy data --- build.gradle | 3 +- .../gartenverwaltung/HelloApplication.java | 9 +++++ .../io/JsonPlantDatabase.java | 37 +++++++++++++++++++ .../gartenverwaltung/io/PlantDatabase.java | 5 ++- .../json/GrowthPhaseTypeDeserializer.java | 28 ++++++++++++++ .../json/HardinessZoneDeserializer.java | 29 +++++++++++++++ .../gartenverwaltung/types/GrowthPhase.java | 14 +++++-- .../ch/zhaw/gartenverwaltung/types/Plant.java | 5 +++ src/main/java/module-info.java | 5 +++ .../ch/zhaw/gartenverwaltung/io/plantdb.json | 31 ++++++++++++++++ 10 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/json/HardinessZoneDeserializer.java create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json diff --git a/build.gradle b/build.gradle index 5268664..41f89ac 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'org.beryx.jlink' version '2.25.0' } -group 'ch.zhaw.pm3' +group 'ch.zhaw.gartenverwaltung' version '1.0-SNAPSHOT' repositories { @@ -37,6 +37,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4' } test { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java index 0d70b44..7877221 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java @@ -1,5 +1,8 @@ package ch.zhaw.gartenverwaltung; +import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; +import ch.zhaw.gartenverwaltung.io.PlantDatabase; +import ch.zhaw.gartenverwaltung.types.HardinessZone; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; @@ -18,6 +21,12 @@ public class HelloApplication extends Application { } public static void main(String[] args) { + PlantDatabase db = new JsonPlantDatabase(); + try { + System.out.println(db.getPlantList(HardinessZone.ZONE_8A)); + } catch (IOException e) { + e.printStackTrace(); + } launch(); } } \ No newline at end of file diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java new file mode 100644 index 0000000..c889195 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -0,0 +1,37 @@ +package ch.zhaw.gartenverwaltung.io; + +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import ch.zhaw.gartenverwaltung.types.Plant; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.net.URL; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class JsonPlantDatabase implements PlantDatabase { + private final URL dataSource = getClass().getResource("plantdb.json"); + + @Override + public List getPlantList(HardinessZone zone) throws IOException { + List result = Collections.emptyList(); + + if (dataSource != null) { + ObjectMapper mapper = new ObjectMapper(); + DateFormat dateFormat = new SimpleDateFormat("MM-dd"); + mapper.setDateFormat(dateFormat); + + result = mapper.readerForListOf(Plant.class).readValue(dataSource); + } + + return result; + } + + @Override + public Optional getPlantById(long id) { + return Optional.empty(); + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java index f0d9bf9..2a38e3b 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java @@ -3,10 +3,11 @@ package ch.zhaw.gartenverwaltung.io; import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.HardinessZone; +import java.io.IOException; import java.util.List; import java.util.Optional; public interface PlantDatabase { - List getPlantList(HardinessZone zone); - Optional getPlantById(long id); + List getPlantList(HardinessZone zone) throws IOException; + Optional getPlantById(long id) throws IOException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java b/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java new file mode 100644 index 0000000..f3bb60f --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/json/GrowthPhaseTypeDeserializer.java @@ -0,0 +1,28 @@ +package ch.zhaw.gartenverwaltung.json; + +import ch.zhaw.gartenverwaltung.types.GrowthPhaseType; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +public class GrowthPhaseTypeDeserializer extends StdDeserializer { + public GrowthPhaseTypeDeserializer(Class vc) { + super(vc); + } + + public GrowthPhaseTypeDeserializer() { this(null); } + + @Override + public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException { + GrowthPhaseType result = null; + try { + result = GrowthPhaseType.valueOf(parser.getText().toUpperCase()); + } catch (IllegalArgumentException e) { + // TODO: Log + System.err.println("bad growth phase type"); + } + return result; + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/json/HardinessZoneDeserializer.java b/src/main/java/ch/zhaw/gartenverwaltung/json/HardinessZoneDeserializer.java new file mode 100644 index 0000000..cf89d6a --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/json/HardinessZoneDeserializer.java @@ -0,0 +1,29 @@ +package ch.zhaw.gartenverwaltung.json; + +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +public class HardinessZoneDeserializer extends StdDeserializer { + public HardinessZoneDeserializer(Class vc) { + super(vc); + } + public HardinessZoneDeserializer() { + this(null); + } + + @Override + public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException { + HardinessZone result = null; + try { + result = HardinessZone.valueOf(parser.getText().toUpperCase()); + } catch (IllegalArgumentException e) { + // TODO: Log + System.err.println("bad growth phase type"); + } + return result; + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java index 665b895..da33b2a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java @@ -1,9 +1,15 @@ package ch.zhaw.gartenverwaltung.types; +import ch.zhaw.gartenverwaltung.json.GrowthPhaseTypeDeserializer; +import ch.zhaw.gartenverwaltung.json.HardinessZoneDeserializer; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + import java.util.Date; -public record GrowthPhase(Date startDate, - Date endDate, - GrowthPhaseType type, - HardinessZone zone) { + +public record GrowthPhase( + Date startDate, + Date endDate, + @JsonDeserialize(using = GrowthPhaseTypeDeserializer.class) GrowthPhaseType type, + @JsonDeserialize(using = HardinessZoneDeserializer.class) HardinessZone zone) { } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 99336e7..612679c 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -7,5 +7,10 @@ public record Plant( String name, String description, int spacing, + Object water, + int light, + List maintenance, + List specialTasks, + List pests, List lifecycle) { } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4bffe36..2bb0935 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,8 +1,13 @@ module ch.zhaw.gartenverwaltung { requires javafx.controls; requires javafx.fxml; + requires com.fasterxml.jackson.databind; opens ch.zhaw.gartenverwaltung to javafx.fxml; + opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; +// opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; exports ch.zhaw.gartenverwaltung; + exports ch.zhaw.gartenverwaltung.types; + exports ch.zhaw.gartenverwaltung.json; } \ No newline at end of file diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json new file mode 100644 index 0000000..7eb3366 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -0,0 +1,31 @@ +[ + { + "name": "Potato", + "description": "Tasty tubers.", + "water": { + "amount": "27-55", + "interval": 7 + }, + "light": 6, + "maintenance": [], + "specialTasks": [], + "lifecycle": [ + { + "startDate": "04-01", + "endDate": "05-31", + "type": "SOW", + "zone": "ZONE_8A" + }, + { + "startDate": "07-01", + "endDate": "08-31", + "type": "HARVEST", + "zone": "ZONE_8A" + } + ], + "spacing": 35, + "pests": [ + "Potato beetle" + ] + } +] \ No newline at end of file From 7dd157b9d50bdf48ac8ec3e8b61d880c48846183 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Thu, 20 Oct 2022 21:26:30 +0200 Subject: [PATCH 02/83] implemented Methods to sort and filter PlantList --- .../plantList/PlantListModel.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java new file mode 100644 index 0000000..0327e59 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -0,0 +1,45 @@ +package ch.zhaw.gartenverwaltung.plantList; + +import ch.zhaw.gartenverwaltung.types.Plant; + +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +public class PlantListModel { + private List plantList; + + /** + * Comparators to create sorted Plant List + */ + public final Comparator sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name()); + public final Comparator getSortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); + public final Comparator sortBySpacing = (Plant o1, Plant o2) -> o1.spacing() - o2.spacing(); + + /** + * Functions to get Plant Attribute as String + */ + public final Function filterByName = Plant::name; + public final Function filterById = plant -> Long.toString(plant.id()); + + public PlantListModel(List plantList) { + setPlantList(plantList); + } + + public void setPlantList(List plantList) { + this.plantList = plantList; + } + + public List getPlantList() { + return getSortedPlantList(sortByName); + } + + public List getSortedPlantList(Comparator comparator) { + return plantList.stream().sorted(comparator).toList(); + } + + public List getFilteredList(String filterString, Function filterFunction) { + return plantList.stream().filter(plant -> filterFunction.apply(plant).contains(filterString)).toList(); + } +} From 5b0e472ec73978fa50e23682d2fb31561889c990 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Thu, 20 Oct 2022 21:33:42 +0200 Subject: [PATCH 03/83] implemented Methods to sort and filter PlantList --- .../plantList/PlantListModel.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index 0327e59..913e2a5 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -23,6 +23,7 @@ public class PlantListModel { public final Function filterByName = Plant::name; public final Function filterById = plant -> Long.toString(plant.id()); + public PlantListModel(List plantList) { setPlantList(plantList); } @@ -31,15 +32,30 @@ public class PlantListModel { this.plantList = plantList; } + /** + * Method to get actual Plant List in alphabetic Order + * @return actual Plant List in alphabetic Order + */ public List getPlantList() { return getSortedPlantList(sortByName); } + /** + * Method to get actual Plant List sorted in custom order + * @param comparator comparator Object which is used to sort list + * @return actual Plant List in custom order + */ public List getSortedPlantList(Comparator comparator) { return plantList.stream().sorted(comparator).toList(); } - public List getFilteredList(String filterString, Function filterFunction) { + /** + * Method to get filtered Plant List with custom filter + * @param filterString String to search for plants + * @param filterFunction Function Object to get Plant attribute as String which must contain filter String + * @return filtered Plant List + */ + public List getFilteredPlantListbyString(String filterString, Function filterFunction) { return plantList.stream().filter(plant -> filterFunction.apply(plant).contains(filterString)).toList(); } } From 6d13bede7a5d91b00716a90cb415aaf7c0df4065 Mon Sep 17 00:00:00 2001 From: David Guler Date: Thu, 20 Oct 2022 21:46:00 +0200 Subject: [PATCH 04/83] Minimum viable deserialization Added TaskTemplate type Added dummy data with (probably) usable format Used the java.time classes instead of the legacy util.Date --- build.gradle | 1 + .../io/JsonPlantDatabase.java | 16 +++- .../gartenverwaltung/types/GrowthPhase.java | 12 ++- .../types/GrowthPhaseType.java | 2 +- .../ch/zhaw/gartenverwaltung/types/Plant.java | 6 +- .../ch/zhaw/gartenverwaltung/types/Task.java | 24 +++-- .../gartenverwaltung/types/TaskTemplate.java | 57 +++++++++++ src/main/java/module-info.java | 2 +- .../ch/zhaw/gartenverwaltung/io/plantdb.json | 96 +++++++++++++++---- 9 files changed, 173 insertions(+), 43 deletions(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/types/TaskTemplate.java diff --git a/build.gradle b/build.gradle index 41f89ac..122bba0 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4' } test { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index c889195..8882e8e 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -3,11 +3,13 @@ package ch.zhaw.gartenverwaltung.io; import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Plant; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer; import java.io.IOException; import java.net.URL; -import java.text.DateFormat; -import java.text.SimpleDateFormat; +import java.time.MonthDay; +import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -15,14 +17,20 @@ import java.util.Optional; public class JsonPlantDatabase implements PlantDatabase { private final URL dataSource = getClass().getResource("plantdb.json"); + private final static JavaTimeModule timeModule = new JavaTimeModule(); + static { + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); + MonthDayDeserializer dateDeserializer = new MonthDayDeserializer(dateFormat); + timeModule.addDeserializer(MonthDay.class, dateDeserializer); + } + @Override public List getPlantList(HardinessZone zone) throws IOException { List result = Collections.emptyList(); if (dataSource != null) { ObjectMapper mapper = new ObjectMapper(); - DateFormat dateFormat = new SimpleDateFormat("MM-dd"); - mapper.setDateFormat(dateFormat); + mapper.registerModule(timeModule); result = mapper.readerForListOf(Plant.class).readValue(dataSource); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java index da33b2a..71b4705 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java @@ -4,12 +4,16 @@ import ch.zhaw.gartenverwaltung.json.GrowthPhaseTypeDeserializer; import ch.zhaw.gartenverwaltung.json.HardinessZoneDeserializer; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.Date; +import java.time.MonthDay; +import java.util.List; public record GrowthPhase( - Date startDate, - Date endDate, + MonthDay startDate, + MonthDay endDate, + int group, + Object wateringCycle, @JsonDeserialize(using = GrowthPhaseTypeDeserializer.class) GrowthPhaseType type, - @JsonDeserialize(using = HardinessZoneDeserializer.class) HardinessZone zone) { + @JsonDeserialize(using = HardinessZoneDeserializer.class) HardinessZone zone, + List taskTemplates) { } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhaseType.java b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhaseType.java index 99e5d2b..96609cc 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhaseType.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhaseType.java @@ -1,5 +1,5 @@ package ch.zhaw.gartenverwaltung.types; public enum GrowthPhaseType { - SOW, PLANT, HARVEST + SOW, PLANT, REPLANT, HARVEST } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 612679c..27b1944 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -7,10 +7,8 @@ public record Plant( String name, String description, int spacing, - Object water, int light, - List maintenance, - List specialTasks, - List pests, + String soil, + List pests, List lifecycle) { } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java index d85435b..90e00f4 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java @@ -1,6 +1,7 @@ package ch.zhaw.gartenverwaltung.types; -import java.util.Date; + +import java.time.LocalDate; import java.util.Optional; /** @@ -11,14 +12,21 @@ public class Task { private long id; private final String name; private final String description; - private final Date startDate; + private final LocalDate startDate; private Integer interval; - private Date endDate; + private LocalDate endDate; - public Task(String name, String description, Date startDate) { + public Task(String name, String description, LocalDate startDate) { this.name = name; this.description = description; - this.startDate = endDate; + this.startDate = startDate; + } + public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval) { + this.name = name; + this.description = description; + this.startDate = startDate; + this.endDate = endDate; + this.interval = interval; } // Builder-pattern-style setters @@ -30,7 +38,7 @@ public class Task { this.interval = interval; return this; } - public Task withEndDate(Date endDate) { + public Task withEndDate(LocalDate endDate) { this.endDate = endDate; return this; } @@ -39,12 +47,12 @@ public class Task { public long getId() { return id; } public String getName() { return name; } public String getDescription() { return description; } - public Date getStartDate() { return startDate; } + public LocalDate getStartDate() { return startDate; } public Optional getInterval() { return Optional.ofNullable(interval); } - public Optional getEndDate() { + public Optional getEndDate() { return Optional.ofNullable(endDate); } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/TaskTemplate.java b/src/main/java/ch/zhaw/gartenverwaltung/types/TaskTemplate.java new file mode 100644 index 0000000..290175e --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/TaskTemplate.java @@ -0,0 +1,57 @@ +package ch.zhaw.gartenverwaltung.types; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.time.LocalDate; + +public class TaskTemplate { + @JsonProperty + private final String name; + @JsonProperty + private final String description; + @JsonProperty + private final int relativeStartDate; + @JsonProperty + private Integer relativeEndDate; + @JsonProperty + private Integer interval; + + // TODO: reconsider if we need this + @JsonProperty + private boolean isOptional = false; + + /** + * Default constructor + * (Used by deserializer) + */ + public TaskTemplate() { + this.name = ""; + this.description = ""; + this.relativeStartDate = 0; + } + + // Setters + public void setRelativeEndDate(Integer relativeEndDate) { + this.relativeEndDate = relativeEndDate; + } + public void setInterval(Integer interval) { + this.interval = interval; + } + + public TaskTemplate(String name, String description, int relativeStartDate) { + this.name = name; + this.description = description; + this.relativeStartDate = relativeStartDate; + } + + public Task generateTask(LocalDate realStartDate) { + Task task = new Task(name, description, realStartDate.plusDays(relativeStartDate)); + if (relativeEndDate != null) { + task.withEndDate(realStartDate.plusDays(relativeEndDate)); + } + if (interval != null) { + task.withInterval(interval); + } + return task; + } +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 2bb0935..8e02fc7 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -2,7 +2,7 @@ module ch.zhaw.gartenverwaltung { requires javafx.controls; requires javafx.fxml; requires com.fasterxml.jackson.databind; - + requires com.fasterxml.jackson.datatype.jsr310; opens ch.zhaw.gartenverwaltung to javafx.fxml; opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json index 7eb3366..50b74f1 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -1,31 +1,85 @@ [ { + "id": 0, "name": "Potato", - "description": "Tasty tubers.", - "water": { - "amount": "27-55", - "interval": 7 - }, + "description": "The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.", "light": 6, - "maintenance": [], - "specialTasks": [], - "lifecycle": [ + "spacing": "35", + "soil": "sandy", + "pests": [ { - "startDate": "04-01", - "endDate": "05-31", - "type": "SOW", - "zone": "ZONE_8A" - }, - { - "startDate": "07-01", - "endDate": "08-31", - "type": "HARVEST", - "zone": "ZONE_8A" + "name": "Rot", + "description": "Rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.", + "measures": "Less water." } ], - "spacing": 35, - "pests": [ - "Potato beetle" + "lifecycle": [ + { + "startDate": "03-10", + "endDate": "04-10", + "type": "SOW", + "zone": "ZONE_8A", + "group": 0, + "wateringCycle": { + "litersPerSqM": 0, + "interval": null, + "notes": [] + }, + "taskTemplates": [ + { + "name": "Germinate", + "relativeStartDate": -14, + "relativeEndDate": null, + "description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"", + "interval": null, + "isOptional": false + } + ] + }, + { + "startDate": "04-10", + "endDate": "07-10", + "type": "PLANT", + "zone": "ZONE_8A", + "group": 0, + "wateringCycle": { + "litersPerSqM": 25, + "interval": 7, + "notes": [] + }, + "taskTemplates": [ + { + "name": "hilling", + "relativeStartDate": 0, + "relativeEndDate": null, + "description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"", + "interval": 21, + "isOptional": false + } + ] + }, + { + "startDate": "06-10", + "endDate": "08-10", + "type": "HARVEST", + "zone": "ZONE_8A", + "group": 0, + "wateringCycle": { + "litersPerSqM": 0, + "interval": null, + "notes": [] + }, + "taskTemplates": [ + { + "name": "Harvest", + "relativeStartDate": 0, + "relativeEndDate": null, + "description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.", + "interval": null, + "isOptional": false + } + ] + } ] } ] \ No newline at end of file From 96b5dba36ce8638a2c739c168decc9ef88934562 Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Thu, 20 Oct 2022 21:59:18 +0200 Subject: [PATCH 05/83] #17 first version of tasks --- .../ch/zhaw/gartenverwaltung/io/taskdb.json | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json b/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json new file mode 100644 index 0000000..049b341 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json @@ -0,0 +1,32 @@ +[ + { + "name" : "water plant", + "description": "water the plant, that the soil is wet around the plant.", + "tools": "watering can" + }, + { + "name" : "sow plant", + "description": "", + "tools": "crops, small shovel, gardening gloves, soil" + }, + { + "name" : "harvest plant", + "description": "", + "tools": "box or bag for the harvested vegetable" + }, + { + "name" : "fertilize plant", + "description": "", + "tools": "Fertilizer, small shovel, gardening gloves" + }, + { + "name" : "covering plant", + "description": "", + "tools": "" + }, + { + "name" : "look for plant", + "description": "look for pest or illness at the leaves", + "tools": "" + } +] \ No newline at end of file From bf4d56e7593cb0c0c2f05a54abf7961e8a3b24c2 Mon Sep 17 00:00:00 2001 From: David Guler Date: Fri, 21 Oct 2022 12:16:15 +0200 Subject: [PATCH 06/83] removed test code --- .../java/ch/zhaw/gartenverwaltung/HelloApplication.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java index 7877221..0d70b44 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java @@ -1,8 +1,5 @@ package ch.zhaw.gartenverwaltung; -import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; -import ch.zhaw.gartenverwaltung.io.PlantDatabase; -import ch.zhaw.gartenverwaltung.types.HardinessZone; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; @@ -21,12 +18,6 @@ public class HelloApplication extends Application { } public static void main(String[] args) { - PlantDatabase db = new JsonPlantDatabase(); - try { - System.out.println(db.getPlantList(HardinessZone.ZONE_8A)); - } catch (IOException e) { - e.printStackTrace(); - } launch(); } } \ No newline at end of file From a2008450e649ddcf68e48817c9977fa96a76776b Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Fri, 21 Oct 2022 21:18:29 +0200 Subject: [PATCH 07/83] #17 implementation of an example task database --- .../ch/zhaw/gartenverwaltung/io/taskdb.json | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json b/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json index 049b341..1155b9a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json @@ -1,32 +1,50 @@ [ { + "id" : "1", + "name" : "sow plant", + "description": "Plant the seeds/ crops in de bed.", + "startDate" : "01.05.2022", + "endDate" : "01.05.2022", + "interval" : "null" + }, + { + "id" : "2", "name" : "water plant", - "description": "water the plant, that the soil is wet around the plant.", - "tools": "watering can" - }, - { - "name" : "sow plant", - "description": "", - "tools": "crops, small shovel, gardening gloves, soil" - }, - { - "name" : "harvest plant", - "description": "", - "tools": "box or bag for the harvested vegetable" + "description": "water the plant, so that the soil is wet around the plant.", + "startDate" : "01.05.2022", + "endDate" : "01.09.2022", + "interval" : "2" }, { + "id" : "3", "name" : "fertilize plant", - "description": "", - "tools": "Fertilizer, small shovel, gardening gloves" + "description": "The fertilizer has to be mixed with water. Then fertilize the plant's soil with the mixture", + "startDate" : "01.06.2022", + "endDate" : "01.08.2022", + "interval" : "28" }, { + "id" : "4", "name" : "covering plant", - "description": "", - "tools": "" + "description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage", + "startDate" : "15.07.2022", + "endDate" : "15.07.2022", + "interval" : "null" }, { - "name" : "look for plant", - "description": "look for pest or illness at the leaves", - "tools": "" + "id" : "5", + "name" : "look after plant", + "description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil", + "startDate" : "01.05.2022", + "endDate" : "01.09.2022", + "interval" : "5" + }, + { + "id" : "6", + "name" : "harvest plant", + "description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ", + "startDate" : "01.09.2022", + "endDate" : "01.09.2022", + "interval" : "null" } ] \ No newline at end of file From e228b9019db52bfa40dfbae80e9fc8a7642b3a8c Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Sat, 22 Oct 2022 11:52:06 +0200 Subject: [PATCH 08/83] #17 implementation JsonTaskDatabase.java, gradle import for jackson --- build.gradle | 2 + .../gartenverwaltung/io/JsonTaskDatabase.java | 67 +++++++++++++++++++ .../gartenverwaltung/io/TaskDatabase.java | 2 +- src/main/java/module-info.java | 2 + 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java diff --git a/build.gradle b/build.gradle index 5268664..ad6e0a5 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,8 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") + implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4' } test { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java new file mode 100644 index 0000000..c6a52a2 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -0,0 +1,67 @@ +package ch.zhaw.gartenverwaltung.io; + +import ch.zhaw.gartenverwaltung.types.Task; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.time.MonthDay; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +public class JsonTaskDatabase implements TaskDatabase{ + private final URL dataSource = getClass().getResource("taskdb.json"); + + private final static JavaTimeModule timeModule = new JavaTimeModule(); + static { + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); + MonthDayDeserializer dateDeserializer = new MonthDayDeserializer(dateFormat); + timeModule.addDeserializer(MonthDay.class, dateDeserializer); + } + + @Override + public List getTaskList(Date start, Date end) throws IOException{ + List taskList = Collections.emptyList(); + + if (dataSource != null) { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(timeModule); + + taskList = mapper.readerForListOf(Task.class).readValue(dataSource); + } + + return taskList; + } + + @Override + public void saveTask(Task task) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + if(dataSource != null) { + mapper.writeValue(new File(dataSource.getFile()), task); + } else { + mapper.writeValue(new File("taskdb.json"),task); + } + } + + @Override + public void removeTask(Task task) throws IOException { + List taskList; + + if(dataSource != null) { + ObjectMapper mapper = new ObjectMapper(); + taskList = mapper.readerForListOf(Task.class).readValue(dataSource); + for (int index = 0; index < taskList.size(); index++){ + if(task.equals(taskList.get(index))){ + taskList.remove(task); + break; + } + } + } + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java index 73cf8e0..4c71397 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java @@ -7,7 +7,7 @@ import java.util.Date; import java.util.List; public interface TaskDatabase { - List getTaskList(Date start, Date end); + List getTaskList(Date start, Date end) throws IOException; void saveTask(Task task) throws IOException; void removeTask(Task task) throws IOException; } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4bffe36..fce2f6b 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,8 @@ module ch.zhaw.gartenverwaltung { requires javafx.controls; requires javafx.fxml; + requires com.fasterxml.jackson.databind; + requires com.fasterxml.jackson.datatype.jsr310; opens ch.zhaw.gartenverwaltung to javafx.fxml; From 429ac16d98b52cf95f266d9ae329a03bcdbd342e Mon Sep 17 00:00:00 2001 From: David Guler Date: Sun, 23 Oct 2022 09:49:22 +0200 Subject: [PATCH 09/83] filtering lifecycle based on hardiness-zone - Added 2 more plants to test db - Db filters plant lifecycles based on hardiness-zones - getPlantById implemented - Added methods to Plant for calculating sowDates and accounting for multiple lifecycles --- .../io/JsonPlantDatabase.java | 9 +- .../gartenverwaltung/io/PlantDatabase.java | 2 +- .../ch/zhaw/gartenverwaltung/types/Plant.java | 34 +++- .../ch/zhaw/gartenverwaltung/io/plantdb.json | 169 ++++++++++++++++++ 4 files changed, 210 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index 8882e8e..e589bc8 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -34,12 +34,17 @@ public class JsonPlantDatabase implements PlantDatabase { result = mapper.readerForListOf(Plant.class).readValue(dataSource); } + for (Plant plant : result) { + plant.inZone(zone); + } return result; } @Override - public Optional getPlantById(long id) { - return Optional.empty(); + public Optional getPlantById(long id, HardinessZone zone) throws IOException { + return getPlantList(zone).stream() + .filter(plant -> plant.id() != id) + .findFirst(); } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java index 2a38e3b..4b8fe85 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java @@ -9,5 +9,5 @@ import java.util.Optional; public interface PlantDatabase { List getPlantList(HardinessZone zone) throws IOException; - Optional getPlantById(long id) throws IOException; + Optional getPlantById(long id, HardinessZone zone) throws IOException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 27b1944..46ff155 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -1,14 +1,46 @@ package ch.zhaw.gartenverwaltung.types; +import java.time.LocalDate; import java.util.List; +import static java.time.temporal.ChronoUnit.DAYS; + public record Plant( long id, String name, String description, - int spacing, + String spacing, int light, String soil, List pests, List lifecycle) { + + public void inZone(HardinessZone zone) { + lifecycle.removeIf(growthPhase -> !growthPhase.zone().equals(zone)); + } + + public List lifecycleForGroup(int group) { + return lifecycle.stream() + .filter(growthPhase -> growthPhase.group() != group) + .toList(); + } + + public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) { + return harvestDate.minusDays(timeToHarvest(group)); + } + + public int timeToHarvest(int group) { + List activeLifecycle = lifecycleForGroup(group); + GrowthPhase sow = activeLifecycle.stream() + .filter(growthPhase -> !growthPhase.type().equals(GrowthPhaseType.SOW)) + .findFirst() + .orElseThrow(); + GrowthPhase harvest = activeLifecycle.stream() + .filter(growthPhase -> !growthPhase.type().equals(GrowthPhaseType.HARVEST)) + .findFirst() + .orElseThrow(); + + int currentYear = LocalDate.now().getYear(); + return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear)); + } } diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json index 50b74f1..8ec3394 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -81,5 +81,174 @@ ] } ] + }, + { + "id": 1, + "name": "Early Carrot", + "description": "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.", + "lifecycle": [ + { + "startDate": "02-20", + "endDate": "03-10", + "zone": "ZONE_8A", + "type": "SOW", + "wateringCycle": { + "litersPerSqM": 15, + "interval": 3, + "notes": [] + }, + "taskTemplates": [ + { + "name": "hilling", + "relativeStartDate": 0, + "relativeEndDate": 0, + "description": "Mound up the soil around the plant until just the top few leaves show above the soil. ", + "interval": null, + "isOptional": false + } + ] + }, + { + "startDate": "03-10", + "endDate": "05-10", + "zone": "ZONE_8A", + "type": "PLANT", + "wateringCycle": { + "litersPerSqM": 25, + "interval": 3, + "notes": [ + "Be careful not to pour water over the leaves, as this will lead to sunburn." + ] + }, + "taskTemplates": [ + { + "name": "hilling", + "relativeStartDate": 0, + "relativeEndDate": null, + "description": "Mound up the soil around the plant until just the top few leaves show above the soil. ", + "interval": 15, + "isOptional": true + } + ] + }, + { + "startDate": "05-10", + "endDate": "05-20", + "zone": "ZONE_8A", + "type": "HARVEST", + "wateringCycle": { + "litersPerSqM": 0, + "interval": null, + "notes": [] + }, + "taskTemplates": [ + { + "name": "Harvesting", + "relativeStartDate": 0, + "relativeEndDate": 14, + "description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.", + "interval": null, + "isOptional": false + } + ] + } + ], + "soil": "sandy to loamy, loose soil, free of stones", + "spacing": "5,35,2.5", + "pests": [ + { + "name": "Rot", + "description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.", + "measurement": "less water" + } + ] + }, + { + "id": 2, + "name": "summertime onion", + "description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.", + "lifecycle": [ + { + "startDate": "03-15", + "endDate": "04-10", + "type": "SOW", + "zone": "ZONE_8A", + "group": 0, + "wateringCycle": { + "litersPerSqM": 15, + "interval": 4, + "notes": [ + + ] + }, + "taskTemplates": [ + { + "name": "hilling", + "relativeStartDate": 0, + "relativeEndDate": 0, + "description": "Mound up the soil around the plant until just the top few leaves show above the soil. ", + "interval": null, + "isOptional": false + } + ] + }, + { + "startDate": "04-10", + "endDate": "07-10", + "type": "PLANT", + "zone": "ZONE_8A", + "group": 0, + "wateringCycle": { + "litersPerSqM": 25, + "interval": 3, + "notes": [ + "" + ] + }, + "taskTemplates": [ + { + "name": "hilling", + "relativeStartDate": 0, + "relativeEndDate": null, + "description": "Mound up the soil around the plant until just the top few leaves show above the soil. ", + "interval": 15, + "isOptional": true + } + ] + }, + { + "startDate": "07-10", + "endDate": "09-20", + "type": "HARVEST", + "zone": "ZONE_8A", + "group": 0, + "wateringCycle": { + "litersPerSqM": 0, + "interval": null, + "notes": [ + + ] + }, + "taskTemplates": [ + { + "name": "Harvesting", + "relativeStartDate": 0, + "relativeEndDate": 14, + "description": "When ready for harvest, the leaves on your onion plants will start to flop over. This happens at the \"neck\" of the onion and it signals that the plant has stopped growing and is ready for storage. Onions should be harvested soon thereafter", + "interval": null, + "isOptional": false + } + ] + } + ], + "soil": "sandy to loamy, loose soil, free of stones", + "spacing": "15,30,2", + "pests": [ + { + "name": "Rot", + "description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.", + "measurement": "less water" + } + ] } ] \ No newline at end of file From 7355ce563f2771355e06c4e1c7e4b653fc6b88f9 Mon Sep 17 00:00:00 2001 From: David Guler Date: Sun, 23 Oct 2022 09:54:56 +0200 Subject: [PATCH 10/83] Added WateringCycle and Pest types --- .../java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java | 2 +- src/main/java/ch/zhaw/gartenverwaltung/types/Pest.java | 4 ++++ src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java | 2 +- .../ch/zhaw/gartenverwaltung/types/WateringCycle.java | 8 ++++++++ .../resources/ch/zhaw/gartenverwaltung/io/plantdb.json | 4 ++-- 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/types/Pest.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/types/WateringCycle.java diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java index 71b4705..9348f2b 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/GrowthPhase.java @@ -12,7 +12,7 @@ public record GrowthPhase( MonthDay startDate, MonthDay endDate, int group, - Object wateringCycle, + WateringCycle wateringCycle, @JsonDeserialize(using = GrowthPhaseTypeDeserializer.class) GrowthPhaseType type, @JsonDeserialize(using = HardinessZoneDeserializer.class) HardinessZone zone, List taskTemplates) { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Pest.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Pest.java new file mode 100644 index 0000000..cfbcb81 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Pest.java @@ -0,0 +1,4 @@ +package ch.zhaw.gartenverwaltung.types; + +public record Pest(String name, String description, String measures) { +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 46ff155..13652a6 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -12,7 +12,7 @@ public record Plant( String spacing, int light, String soil, - List pests, + List pests, List lifecycle) { public void inZone(HardinessZone zone) { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/WateringCycle.java b/src/main/java/ch/zhaw/gartenverwaltung/types/WateringCycle.java new file mode 100644 index 0000000..9d7c7d0 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/WateringCycle.java @@ -0,0 +1,8 @@ +package ch.zhaw.gartenverwaltung.types; + +public record WateringCycle( + int litersPerSqM, + int interval, + String[] notes +) { +} diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json index 8ec3394..f513570 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -159,7 +159,7 @@ { "name": "Rot", "description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.", - "measurement": "less water" + "measures": "less water" } ] }, @@ -247,7 +247,7 @@ { "name": "Rot", "description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.", - "measurement": "less water" + "measures": "less water" } ] } From 2c61cd33930e35fcb01618a0ba9cc11d70041c52 Mon Sep 17 00:00:00 2001 From: David Guler Date: Sun, 23 Oct 2022 11:11:52 +0200 Subject: [PATCH 11/83] Implemented simple caching for JsonPlantDatabase Also added Javadoc for the PlantDatabase interface --- .../io/HardinessZoneNotSetException.java | 7 ++ .../io/JsonPlantDatabase.java | 74 +++++++++++++++---- .../gartenverwaltung/io/PlantDatabase.java | 25 ++++++- 3 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/HardinessZoneNotSetException.java diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/HardinessZoneNotSetException.java b/src/main/java/ch/zhaw/gartenverwaltung/io/HardinessZoneNotSetException.java new file mode 100644 index 0000000..cbb7015 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/HardinessZoneNotSetException.java @@ -0,0 +1,7 @@ +package ch.zhaw.gartenverwaltung.io; + +public class HardinessZoneNotSetException extends Exception { + public HardinessZoneNotSetException() { + super("HardinessZone must be set to retrieve plants!"); + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index e589bc8..4ae2435 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -11,12 +11,25 @@ import java.net.URL; import java.time.MonthDay; import java.time.format.DateTimeFormatter; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; +/** + * Implements the {@link PlantDatabase} interface for loading {@link Plant} objects + * from a JSON file. + * The reads are cached to minimize file-io operations. + */ public class JsonPlantDatabase implements PlantDatabase { private final URL dataSource = getClass().getResource("plantdb.json"); + private HardinessZone currentZone; + private Map plantMap = Collections.emptyMap(); + + /** + * Creating constant objects required to deserialize the {@link MonthDay} classes + */ private final static JavaTimeModule timeModule = new JavaTimeModule(); static { DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); @@ -24,27 +37,62 @@ public class JsonPlantDatabase implements PlantDatabase { timeModule.addDeserializer(MonthDay.class, dateDeserializer); } + /** + * If no data is currently loaded, or the specified zone differs + * from the {@link #currentZone}, data is loaded from {@link #dataSource}. + * In any case, the values of {@link #plantMap} are returned. + * + * @see PlantDatabase#getPlantList(HardinessZone) + */ @Override - public List getPlantList(HardinessZone zone) throws IOException { - List result = Collections.emptyList(); + public List getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException { + if (zone == null) { + throw new HardinessZoneNotSetException(); + } + if (plantMap.isEmpty() || zone != currentZone) { + loadPlantList(zone); + } + return plantMap.values().stream().toList(); + } + /** + * @see PlantDatabase#getPlantById(long) + */ + @Override + public Optional getPlantById(long id) throws HardinessZoneNotSetException, IOException { + if (currentZone == null) { + throw new HardinessZoneNotSetException(); + } + if (plantMap.isEmpty()) { + loadPlantList(currentZone); + } + return Optional.ofNullable(plantMap.get(id)); + } + + /** + * Loads the database from {@link #dataSource} and updates the cached data. + * + * @param zone The {@link HardinessZone} for which data is to be loaded + * @throws IOException If the database cannot be accessed + */ + private void loadPlantList(HardinessZone zone) throws IOException { if (dataSource != null) { + currentZone = zone; ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(timeModule); + List result; result = mapper.readerForListOf(Plant.class).readValue(dataSource); - } - for (Plant plant : result) { - plant.inZone(zone); - } - return result; - } + for (Plant plant : result) { + plant.inZone(currentZone); + } - @Override - public Optional getPlantById(long id, HardinessZone zone) throws IOException { - return getPlantList(zone).stream() - .filter(plant -> plant.id() != id) - .findFirst(); + // Turn list into a HashMap with structure id => Plant + plantMap = result.stream() + .collect(HashMap::new, + (res, plant) -> res.put(plant.id(), plant), + (existing, replacement) -> { }); + } } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java index 4b8fe85..0b6908d 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java @@ -7,7 +7,28 @@ import java.io.IOException; import java.util.List; import java.util.Optional; +/** + * A database of {@link Plant}s. + * The interface specifies the minimal required operations. + */ public interface PlantDatabase { - List getPlantList(HardinessZone zone) throws IOException; - Optional getPlantById(long id, HardinessZone zone) throws IOException; + /** + * Yields a list of all {@link Plant}s in the database with only data relevant to the specfied {@link HardinessZone} + * + * @param zone The zone for which data should be fetched + * @return A list of {@link Plant}s with data for the specified zone + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + */ + List getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException; + + /** + * Attempts to retrieve the {@link Plant} with the specified id. + * + * @param id The {@link Plant#id()} to look for + * @return {@link Optional} of the found {@link Plant}, {@link Optional#empty()} if no entry matched the criteria + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + */ + Optional getPlantById(long id) throws IOException, HardinessZoneNotSetException; } From 0d24bcc2adb58723f367a16dacdf12cfe7bfd73c Mon Sep 17 00:00:00 2001 From: David Guler Date: Mon, 24 Oct 2022 12:32:28 +0200 Subject: [PATCH 12/83] Moved null-check to loading method --- .../zhaw/gartenverwaltung/io/JsonPlantDatabase.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index 4ae2435..5591acd 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -46,9 +46,6 @@ public class JsonPlantDatabase implements PlantDatabase { */ @Override public List getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException { - if (zone == null) { - throw new HardinessZoneNotSetException(); - } if (plantMap.isEmpty() || zone != currentZone) { loadPlantList(zone); } @@ -60,9 +57,6 @@ public class JsonPlantDatabase implements PlantDatabase { */ @Override public Optional getPlantById(long id) throws HardinessZoneNotSetException, IOException { - if (currentZone == null) { - throw new HardinessZoneNotSetException(); - } if (plantMap.isEmpty()) { loadPlantList(currentZone); } @@ -75,7 +69,10 @@ public class JsonPlantDatabase implements PlantDatabase { * @param zone The {@link HardinessZone} for which data is to be loaded * @throws IOException If the database cannot be accessed */ - private void loadPlantList(HardinessZone zone) throws IOException { + private void loadPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException { + if (zone == null) { + throw new HardinessZoneNotSetException(); + } if (dataSource != null) { currentZone = zone; ObjectMapper mapper = new ObjectMapper(); From 904041afc0fb5ae0f21f59eb6602c78bb64143ae Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Mon, 24 Oct 2022 12:43:50 +0200 Subject: [PATCH 13/83] #17 implementation JsonTaskDatabase.java, gradle import for jackson --- .../zhaw/gartenverwaltung/io/JsonTaskDatabase.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java index c6a52a2..1889706 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -4,10 +4,12 @@ import ch.zhaw.gartenverwaltung.types.Task; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer; +import javafx.util.converter.LocalDateStringConverter; import java.io.File; import java.io.IOException; import java.net.URL; +import java.time.LocalDate; import java.time.MonthDay; import java.time.format.DateTimeFormatter; import java.util.Collections; @@ -18,6 +20,18 @@ import java.util.Optional; public class JsonTaskDatabase implements TaskDatabase{ private final URL dataSource = getClass().getResource("taskdb.json"); + public static void main(String[] args) { + Date date = new Date(); + Date yesterday = new Date(-1); + Task testTask = new Task("water", "apply water", date); + JsonTaskDatabase jsonTaskDatabase = new JsonTaskDatabase(); + try { + jsonTaskDatabase.saveTask(testTask); + System.out.println(jsonTaskDatabase.getTaskList(yesterday,date)); + } catch (Exception e){ + System.out.println("Task saving failed!"); + } + } private final static JavaTimeModule timeModule = new JavaTimeModule(); static { DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); From e51da071bc4a0ff471e5c140016c01cb23b2c809 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Mon, 24 Oct 2022 12:44:11 +0200 Subject: [PATCH 14/83] updated method getFilteredPlantList --- .../gartenverwaltung/plantList/PlantListModel.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index 913e2a5..22b7de2 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -4,7 +4,6 @@ import ch.zhaw.gartenverwaltung.types.Plant; import java.util.Comparator; import java.util.List; -import java.util.function.Function; import java.util.function.Predicate; public class PlantListModel { @@ -17,12 +16,6 @@ public class PlantListModel { public final Comparator getSortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); public final Comparator sortBySpacing = (Plant o1, Plant o2) -> o1.spacing() - o2.spacing(); - /** - * Functions to get Plant Attribute as String - */ - public final Function filterByName = Plant::name; - public final Function filterById = plant -> Long.toString(plant.id()); - public PlantListModel(List plantList) { setPlantList(plantList); @@ -51,11 +44,10 @@ public class PlantListModel { /** * Method to get filtered Plant List with custom filter - * @param filterString String to search for plants - * @param filterFunction Function Object to get Plant attribute as String which must contain filter String + * @param predicate predicate for filter * @return filtered Plant List */ - public List getFilteredPlantListbyString(String filterString, Function filterFunction) { - return plantList.stream().filter(plant -> filterFunction.apply(plant).contains(filterString)).toList(); + public List getFilteredPlantList(Predicate predicate) { + return plantList.stream().filter(predicate).toList(); } } From a29f8c7db790c3d82312f63b750b03bf7d1d5500 Mon Sep 17 00:00:00 2001 From: David Guler Date: Mon, 24 Oct 2022 13:42:31 +0200 Subject: [PATCH 15/83] Added IdProvider --- .../java/ch/zhaw/gartenverwaltung/io/IdProvider.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/IdProvider.java diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/IdProvider.java b/src/main/java/ch/zhaw/gartenverwaltung/io/IdProvider.java new file mode 100644 index 0000000..5842f0d --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/IdProvider.java @@ -0,0 +1,11 @@ +package ch.zhaw.gartenverwaltung.io; + +public class IdProvider { + private long currentId; + public IdProvider(long initialValue) { + currentId = initialValue; + } + public long incrementAndGet() { + return ++currentId; + } +} From 694da97cd673f339784353672cce9c70cf3f88d0 Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Mon, 24 Oct 2022 13:54:28 +0200 Subject: [PATCH 16/83] #17 implementation JsonTaskDatabase.java, writeTasklistToFile --- .../gartenverwaltung/io/JsonTaskDatabase.java | 69 ++++++++++++------- .../gartenverwaltung/io/TaskDatabase.java | 3 +- .../ch/zhaw/gartenverwaltung/io/taskdb.json | 0 3 files changed, 48 insertions(+), 24 deletions(-) rename src/main/{java => resources}/ch/zhaw/gartenverwaltung/io/taskdb.json (100%) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java index 1889706..5c7211b 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -1,10 +1,10 @@ package ch.zhaw.gartenverwaltung.io; +import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Task; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer; -import javafx.util.converter.LocalDateStringConverter; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import java.io.File; import java.io.IOException; @@ -13,34 +13,26 @@ import java.time.LocalDate; import java.time.MonthDay; import java.time.format.DateTimeFormatter; import java.util.Collections; -import java.util.Date; +import java.util.HashMap; import java.util.List; -import java.util.Optional; +import java.util.Map; public class JsonTaskDatabase implements TaskDatabase{ private final URL dataSource = getClass().getResource("taskdb.json"); - public static void main(String[] args) { - Date date = new Date(); - Date yesterday = new Date(-1); - Task testTask = new Task("water", "apply water", date); - JsonTaskDatabase jsonTaskDatabase = new JsonTaskDatabase(); - try { - jsonTaskDatabase.saveTask(testTask); - System.out.println(jsonTaskDatabase.getTaskList(yesterday,date)); - } catch (Exception e){ - System.out.println("Task saving failed!"); - } - } + private Map taskMap = Collections.emptyMap(); + + + private final static JavaTimeModule timeModule = new JavaTimeModule(); static { - DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd"); - MonthDayDeserializer dateDeserializer = new MonthDayDeserializer(dateFormat); - timeModule.addDeserializer(MonthDay.class, dateDeserializer); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM-dd"); + LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); + timeModule.addDeserializer(LocalDate.class, dateDeserializer); } @Override - public List getTaskList(Date start, Date end) throws IOException{ + public List getTaskList(LocalDate start, LocalDate end) throws IOException{ List taskList = Collections.emptyList(); if (dataSource != null) { @@ -49,7 +41,6 @@ public class JsonTaskDatabase implements TaskDatabase{ taskList = mapper.readerForListOf(Task.class).readValue(dataSource); } - return taskList; } @@ -58,8 +49,6 @@ public class JsonTaskDatabase implements TaskDatabase{ ObjectMapper mapper = new ObjectMapper(); if(dataSource != null) { mapper.writeValue(new File(dataSource.getFile()), task); - } else { - mapper.writeValue(new File("taskdb.json"),task); } } @@ -70,6 +59,7 @@ public class JsonTaskDatabase implements TaskDatabase{ if(dataSource != null) { ObjectMapper mapper = new ObjectMapper(); taskList = mapper.readerForListOf(Task.class).readValue(dataSource); + for (int index = 0; index < taskList.size(); index++){ if(task.equals(taskList.get(index))){ taskList.remove(task); @@ -78,4 +68,37 @@ public class JsonTaskDatabase implements TaskDatabase{ } } } + + private void writeTaskListToFile() throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(timeModule); + + mapper.writeValue(new File(dataSource.getFile()), taskMap); + } + + private void loadTaskListFromFile() throws IOException { + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(timeModule); + + List result; + result = mapper.readerForListOf(Task.class).readValue(dataSource); + + //taskMap = result.stream().collect(HashMap::new, + } + + public static void main(String[] args) { + LocalDate date = LocalDate.now(); + LocalDate yesterday = LocalDate.now(); + yesterday.minusDays(1); + + Task testTask = new Task("water", "apply water", date); + JsonTaskDatabase jsonTaskDatabase = new JsonTaskDatabase(); + try { + jsonTaskDatabase.saveTask(testTask); + System.out.println(jsonTaskDatabase.getTaskList(yesterday,date)); + } catch (Exception e){ + System.out.println("Task saving failed!"); + } + } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java index 4c71397..e9c52a8 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java @@ -3,11 +3,12 @@ package ch.zhaw.gartenverwaltung.io; import ch.zhaw.gartenverwaltung.types.Task; import java.io.IOException; +import java.time.LocalDate; import java.util.Date; import java.util.List; public interface TaskDatabase { - List getTaskList(Date start, Date end) throws IOException; + List getTaskList(LocalDate start, LocalDate end) throws IOException; void saveTask(Task task) throws IOException; void removeTask(Task task) throws IOException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json similarity index 100% rename from src/main/java/ch/zhaw/gartenverwaltung/io/taskdb.json rename to src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json From af0e73c007df88d03aec9b80443f1b6965e91e1e Mon Sep 17 00:00:00 2001 From: Elias Csomor Date: Mon, 24 Oct 2022 14:20:59 +0200 Subject: [PATCH 17/83] Prepare for tests --- .../io/JsonTaskDatabaseTest.java | 37 ++++++++++++++ .../ch/zhaw/gartenverwaltung/io/taskdb.json | 50 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/test/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabaseTest.java create mode 100644 src/test/resources/ch/zhaw/gartenverwaltung/io/taskdb.json diff --git a/src/test/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabaseTest.java b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabaseTest.java new file mode 100644 index 0000000..e75ea40 --- /dev/null +++ b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabaseTest.java @@ -0,0 +1,37 @@ +package ch.zhaw.gartenverwaltung.io; + +import ch.zhaw.gartenverwaltung.types.Task; +import org.junit.jupiter.api.*; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +public class JsonTaskDatabaseTest { + + TaskDatabase testDatabase; + SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy"); + @BeforeEach + void connectToDb() { + // testDatabase = new JsonTaskDatabase(); + } + + + @Test + @DisplayName("Check if results are retrieved completely") + void getTasks(){ + /* + List taskList=null; + try { + taskList = testDatabase.getTaskList(formatter.parse("01.05.2022"), formatter.parse("01.08.2022")); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } + + Assertions.assertTrue(taskList.size()>0); + */ + } +} diff --git a/src/test/resources/ch/zhaw/gartenverwaltung/io/taskdb.json b/src/test/resources/ch/zhaw/gartenverwaltung/io/taskdb.json new file mode 100644 index 0000000..1155b9a --- /dev/null +++ b/src/test/resources/ch/zhaw/gartenverwaltung/io/taskdb.json @@ -0,0 +1,50 @@ +[ + { + "id" : "1", + "name" : "sow plant", + "description": "Plant the seeds/ crops in de bed.", + "startDate" : "01.05.2022", + "endDate" : "01.05.2022", + "interval" : "null" + }, + { + "id" : "2", + "name" : "water plant", + "description": "water the plant, so that the soil is wet around the plant.", + "startDate" : "01.05.2022", + "endDate" : "01.09.2022", + "interval" : "2" + }, + { + "id" : "3", + "name" : "fertilize plant", + "description": "The fertilizer has to be mixed with water. Then fertilize the plant's soil with the mixture", + "startDate" : "01.06.2022", + "endDate" : "01.08.2022", + "interval" : "28" + }, + { + "id" : "4", + "name" : "covering plant", + "description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage", + "startDate" : "15.07.2022", + "endDate" : "15.07.2022", + "interval" : "null" + }, + { + "id" : "5", + "name" : "look after plant", + "description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil", + "startDate" : "01.05.2022", + "endDate" : "01.09.2022", + "interval" : "5" + }, + { + "id" : "6", + "name" : "harvest plant", + "description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ", + "startDate" : "01.09.2022", + "endDate" : "01.09.2022", + "interval" : "null" + } +] \ No newline at end of file From aceb6aa1e6c120f0ca0334857a57b3388a768e02 Mon Sep 17 00:00:00 2001 From: Elias Csomor Date: Mon, 24 Oct 2022 14:54:49 +0200 Subject: [PATCH 18/83] first tests and a typo corrected --- .../ch/zhaw/gartenverwaltung/io/plantdb.json | 2 +- .../io/JsonPlantDatabaseTest.java | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json index f513570..723b450 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -165,7 +165,7 @@ }, { "id": 2, - "name": "summertime onion", + "name": "Summertime Onion", "description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.", "lifecycle": [ { diff --git a/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java new file mode 100644 index 0000000..b554502 --- /dev/null +++ b/src/test/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabaseTest.java @@ -0,0 +1,79 @@ +package ch.zhaw.gartenverwaltung.io; + +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import ch.zhaw.gartenverwaltung.types.Plant; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class JsonPlantDatabaseTest { + PlantDatabase testDatabase; + SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy"); + + @BeforeEach + void connectToDb() { + testDatabase = new JsonPlantDatabase(); + } + + + @Test + @DisplayName("Check if results are retrieved completely") + void getPlantList() { + List testList; + try { + testList = testDatabase.getPlantList(HardinessZone.ZONE_8A); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (HardinessZoneNotSetException e) { + throw new RuntimeException(e); + } + Assertions.assertEquals(3, testList.size()); + + List names = testList.stream().map(Plant::name).collect(Collectors.toList()); + List expected = Arrays.asList("Potato","Early Carrot","Summertime Onion"); + Assertions.assertEquals(expected,names); + } + + @Test + @DisplayName("Check whether single access works.") + void getPlantById() { + Optional testPlant; + try { + testDatabase.getPlantList(HardinessZone.ZONE_8A); + testPlant = testDatabase.getPlantById(1); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (HardinessZoneNotSetException e) { + throw new RuntimeException(e); + } + Assertions.assertTrue(testPlant.isPresent()); + Assertions.assertEquals("Early Carrot", testPlant.get().name()); + } + + @Test + @DisplayName("Check for a nonexisting plant.") + void getPlantByIdMustFail() { + Optional testPlant; + try { + testDatabase.getPlantList(HardinessZone.ZONE_8A); + testPlant = testDatabase.getPlantById(99); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (HardinessZoneNotSetException e) { + throw new RuntimeException(e); + } + Assertions.assertFalse(testPlant.isPresent()); + + + } + +} + From d1c5c584686492457ae6fe116415d6d1e5938176 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Mon, 24 Oct 2022 18:55:26 +0200 Subject: [PATCH 19/83] adapted class PlantListModel to PlantDatabase --- .../io/JsonPlantDatabase.java | 4 +- .../gartenverwaltung/io/PlantDatabase.java | 2 +- .../plantList/PlantListModel.java | 72 ++++++++++++++----- .../ch/zhaw/gartenverwaltung/io/plantdb.json | 2 +- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index 5591acd..aa06e65 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -56,9 +56,9 @@ public class JsonPlantDatabase implements PlantDatabase { * @see PlantDatabase#getPlantById(long) */ @Override - public Optional getPlantById(long id) throws HardinessZoneNotSetException, IOException { + public Optional getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException { if (plantMap.isEmpty()) { - loadPlantList(currentZone); + loadPlantList(zone); } return Optional.ofNullable(plantMap.get(id)); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java index 0b6908d..455791e 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java @@ -30,5 +30,5 @@ public interface PlantDatabase { * @throws IOException If the database cannot be accessed * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ - Optional getPlantById(long id) throws IOException, HardinessZoneNotSetException; + Optional getPlantById(HardinessZone zone, long id) throws IOException, HardinessZoneNotSetException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index 22b7de2..86e21f2 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -1,53 +1,87 @@ package ch.zhaw.gartenverwaltung.plantList; +import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; +import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; +import ch.zhaw.gartenverwaltung.io.PlantDatabase; +import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Plant; +import java.io.IOException; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; public class PlantListModel { - private List plantList; + private final PlantDatabase plantDatabase; + private HardinessZone currentZone; /** * Comparators to create sorted Plant List */ public final Comparator sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name()); public final Comparator getSortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); - public final Comparator sortBySpacing = (Plant o1, Plant o2) -> o1.spacing() - o2.spacing(); - - public PlantListModel(List plantList) { - setPlantList(plantList); + public PlantListModel() { + plantDatabase = new JsonPlantDatabase(); } - public void setPlantList(List plantList) { - this.plantList = plantList; + public void setCurrentZone(HardinessZone currentZone) { + this.currentZone = currentZone; + } + + public HardinessZone getCurrentZone() { + return currentZone; } /** * Method to get actual Plant List in alphabetic Order * @return actual Plant List in alphabetic Order */ - public List getPlantList() { - return getSortedPlantList(sortByName); + public List getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + return getSortedPlantList(zone, sortByName); + } + + + /** + * Method to get the actual Plant list in custom Order + * @param zone selected hardiness zone + * @param comparator comparator to sort the list + * @return sorted list with plants in the given hardiness zone + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + */ + public List getSortedPlantList(HardinessZone zone, Comparator comparator) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + return plantDatabase.getPlantList(zone).stream().sorted(comparator).toList(); } /** - * Method to get actual Plant List sorted in custom order - * @param comparator comparator Object which is used to sort list - * @return actual Plant List in custom order + * Method to get Filtered plant list + * @param predicate predicate to filter the list + * @param zone selected hardiness zone + * @return filterd list with plants in the hardinness zone + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ - public List getSortedPlantList(Comparator comparator) { - return plantList.stream().sorted(comparator).toList(); + public List getFilteredPlantList(HardinessZone zone, Predicate predicate) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + return getPlantList(zone).stream().filter(predicate).toList(); } /** - * Method to get filtered Plant List with custom filter - * @param predicate predicate for filter - * @return filtered Plant List + * Method to get Filtered plant list by id by exact match + * @param zone selected hardiness zone + * @param id id of plant + * @return if id doesn't exist: empty List, else list with 1 plant entry. + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ - public List getFilteredPlantList(Predicate predicate) { - return plantList.stream().filter(predicate).toList(); + public List getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + List plantList = new ArrayList<>(); + plantDatabase.getPlantById(zone, id).ifPresent(plantList::add); + return plantList; } } diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json index f513570..aae89bc 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -251,4 +251,4 @@ } ] } -] \ No newline at end of file +] From e5b5e1b88a35c205da02a4b49b06d1df8172f60f Mon Sep 17 00:00:00 2001 From: schrom01 Date: Mon, 24 Oct 2022 18:55:26 +0200 Subject: [PATCH 20/83] adapted class PlantListModel to PlantDatabase #12 --- .../io/JsonPlantDatabase.java | 4 +- .../gartenverwaltung/io/PlantDatabase.java | 2 +- .../plantList/PlantListModel.java | 72 ++++++++++++++----- .../ch/zhaw/gartenverwaltung/io/plantdb.json | 2 +- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java index 5591acd..aa06e65 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonPlantDatabase.java @@ -56,9 +56,9 @@ public class JsonPlantDatabase implements PlantDatabase { * @see PlantDatabase#getPlantById(long) */ @Override - public Optional getPlantById(long id) throws HardinessZoneNotSetException, IOException { + public Optional getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException { if (plantMap.isEmpty()) { - loadPlantList(currentZone); + loadPlantList(zone); } return Optional.ofNullable(plantMap.get(id)); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java index 0b6908d..455791e 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/PlantDatabase.java @@ -30,5 +30,5 @@ public interface PlantDatabase { * @throws IOException If the database cannot be accessed * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ - Optional getPlantById(long id) throws IOException, HardinessZoneNotSetException; + Optional getPlantById(HardinessZone zone, long id) throws IOException, HardinessZoneNotSetException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index 22b7de2..86e21f2 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -1,53 +1,87 @@ package ch.zhaw.gartenverwaltung.plantList; +import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; +import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; +import ch.zhaw.gartenverwaltung.io.PlantDatabase; +import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Plant; +import java.io.IOException; +import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; public class PlantListModel { - private List plantList; + private final PlantDatabase plantDatabase; + private HardinessZone currentZone; /** * Comparators to create sorted Plant List */ public final Comparator sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name()); public final Comparator getSortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); - public final Comparator sortBySpacing = (Plant o1, Plant o2) -> o1.spacing() - o2.spacing(); - - public PlantListModel(List plantList) { - setPlantList(plantList); + public PlantListModel() { + plantDatabase = new JsonPlantDatabase(); } - public void setPlantList(List plantList) { - this.plantList = plantList; + public void setCurrentZone(HardinessZone currentZone) { + this.currentZone = currentZone; + } + + public HardinessZone getCurrentZone() { + return currentZone; } /** * Method to get actual Plant List in alphabetic Order * @return actual Plant List in alphabetic Order */ - public List getPlantList() { - return getSortedPlantList(sortByName); + public List getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + return getSortedPlantList(zone, sortByName); + } + + + /** + * Method to get the actual Plant list in custom Order + * @param zone selected hardiness zone + * @param comparator comparator to sort the list + * @return sorted list with plants in the given hardiness zone + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified + */ + public List getSortedPlantList(HardinessZone zone, Comparator comparator) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + return plantDatabase.getPlantList(zone).stream().sorted(comparator).toList(); } /** - * Method to get actual Plant List sorted in custom order - * @param comparator comparator Object which is used to sort list - * @return actual Plant List in custom order + * Method to get Filtered plant list + * @param predicate predicate to filter the list + * @param zone selected hardiness zone + * @return filterd list with plants in the hardinness zone + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ - public List getSortedPlantList(Comparator comparator) { - return plantList.stream().sorted(comparator).toList(); + public List getFilteredPlantList(HardinessZone zone, Predicate predicate) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + return getPlantList(zone).stream().filter(predicate).toList(); } /** - * Method to get filtered Plant List with custom filter - * @param predicate predicate for filter - * @return filtered Plant List + * Method to get Filtered plant list by id by exact match + * @param zone selected hardiness zone + * @param id id of plant + * @return if id doesn't exist: empty List, else list with 1 plant entry. + * @throws IOException If the database cannot be accessed + * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified */ - public List getFilteredPlantList(Predicate predicate) { - return plantList.stream().filter(predicate).toList(); + public List getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException { + setCurrentZone(zone); + List plantList = new ArrayList<>(); + plantDatabase.getPlantById(zone, id).ifPresent(plantList::add); + return plantList; } } diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json index f513570..aae89bc 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/plantdb.json @@ -251,4 +251,4 @@ } ] } -] \ No newline at end of file +] From fb237e47c084bf4fa76f2268ba2ff9533f993ca1 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Mon, 24 Oct 2022 19:08:24 +0200 Subject: [PATCH 21/83] added new HardinessZone for testing --- src/main/java/ch/zhaw/gartenverwaltung/types/HardinessZone.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/HardinessZone.java b/src/main/java/ch/zhaw/gartenverwaltung/types/HardinessZone.java index 5d0bc91..da3c3c8 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/HardinessZone.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/HardinessZone.java @@ -5,5 +5,6 @@ package ch.zhaw.gartenverwaltung.types; * (Subject to later expansion) */ public enum HardinessZone { + ZONE_1A, ZONE_8A } From 72b0d029d54c6648fcb53d17e4bccb0c07450f4f Mon Sep 17 00:00:00 2001 From: David Guler Date: Mon, 24 Oct 2022 19:19:45 +0200 Subject: [PATCH 22/83] #25 Initial implementation of the JsonGardenPlan Updated and renamed the Crop type to accommodate storage --- .../zhaw/gartenverwaltung/io/GardenPlan.java | 38 ++++- .../io/InvalidJsonException.java | 9 ++ .../gartenverwaltung/io/JsonGardenPlan.java | 134 ++++++++++++++++++ .../ch/zhaw/gartenverwaltung/types/Crop.java | 35 +++++ .../gartenverwaltung/types/UserPlanting.java | 10 -- 5 files changed, 212 insertions(+), 14 deletions(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/InvalidJsonException.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java delete mode 100644 src/main/java/ch/zhaw/gartenverwaltung/types/UserPlanting.java diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/GardenPlan.java b/src/main/java/ch/zhaw/gartenverwaltung/io/GardenPlan.java index 94e1346..0e04f9a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/GardenPlan.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/GardenPlan.java @@ -1,12 +1,42 @@ package ch.zhaw.gartenverwaltung.io; -import ch.zhaw.gartenverwaltung.types.UserPlanting; +import ch.zhaw.gartenverwaltung.types.Crop; import java.io.IOException; import java.util.List; +import java.util.Optional; public interface GardenPlan { - List getPlantings(); - void savePlanting(UserPlanting planting) throws IOException; - void removePlanting(UserPlanting planting) throws IOException; + /** + * Yields a list of all {@link Crop}s in the database. + * + * @return A list of all {@link Crop}s in the database + * @throws IOException If there is a problem reading from or writing to the database + */ + List getCrops() throws IOException; + + /** + * Attempts to retrieve the {@link Crop} with the specified cropId. + * + * @param id The {@link Crop#cropId} to look for + * @return {@link Optional} of the found {@link Crop}, {@link Optional#empty()} if no entry matched the criteria + * @throws IOException If there is a problem reading from or writing to the database + */ + Optional getCropById(long id) throws IOException; + + /** + * Saves a Crop to the Database. + * + * @param crop The {@link Crop} to be saved + * @throws IOException If there is a problem reading from or writing to the database + */ + void saveCrop(Crop crop) throws IOException; + + /** + * Removes a Crop from the Database. + * + * @param crop The {@link Crop} to be removed + * @throws IOException If there is a problem reading from or writing to the database + */ + void removeCrop(Crop crop) throws IOException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/InvalidJsonException.java b/src/main/java/ch/zhaw/gartenverwaltung/io/InvalidJsonException.java new file mode 100644 index 0000000..9c3b8dc --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/InvalidJsonException.java @@ -0,0 +1,9 @@ +package ch.zhaw.gartenverwaltung.io; + +import java.io.IOException; + +class InvalidJsonException extends IOException { + public InvalidJsonException(String reason) { + super(reason); + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java new file mode 100644 index 0000000..c4e224c --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java @@ -0,0 +1,134 @@ +package ch.zhaw.gartenverwaltung.io; + +import ch.zhaw.gartenverwaltung.types.Crop; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class JsonGardenPlan implements GardenPlan { + private final URL dataSource = getClass().getResource("user-crops.json"); + + private IdProvider idProvider; + private Map cropMap = Collections.emptyMap(); + + /** + * Creating constant objects required to deserialize the {@link LocalDate} classes + */ + private final static JavaTimeModule timeModule = new JavaTimeModule(); + private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!"; + static { + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM-dd"); + LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); + timeModule.addDeserializer(LocalDate.class, dateDeserializer); + } + + /** + * @see GardenPlan#getCrops() + */ + @Override + public List getCrops() throws IOException { + if (idProvider == null) { + loadCropList(); + } + return cropMap.values().stream().toList(); + } + + /** + * @see GardenPlan#getCropById(long) + */ + @Override + public Optional getCropById(long id) throws IOException { + if (idProvider == null) { + loadCropList(); + } + return Optional.ofNullable(cropMap.get(id)); + } + + /** + * @see GardenPlan#saveCrop(Crop) + * + * Saves a crop to the database. + * If no {@link Crop#cropId} is set, one will be generated and + * the Crop is stored as a new entry. + */ + @Override + public void saveCrop(Crop crop) throws IOException { + if (idProvider == null) { + loadCropList(); + } + Long cropId = crop.getCropId().orElse(idProvider.incrementAndGet()); + cropMap.put(cropId, crop.withId(cropId)); + writeCropList(); + } + + /** + * @see GardenPlan#removeCrop(Crop) + */ + @Override + public void removeCrop(Crop crop) throws IOException { + if (idProvider == null) { + loadCropList(); + } + Optional cropId = crop.getCropId(); + if (cropId.isPresent()) { + cropMap.remove(cropId.get()); + writeCropList(); + } + } + + /** + * Loads the database from {@link #dataSource} and updates the cached data. + * + * @throws IOException If the database cannot be accessed or invalid data was read. + */ + private void loadCropList() throws IOException { + if (dataSource != null) { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(timeModule); + + List result; + result = mapper.readerForListOf(Crop.class).readValue(dataSource); + + // Turn list into a HashMap with structure cropId => Crop + cropMap = new HashMap<>(result.size()); + for (Crop crop : result) { + Long id = crop.getCropId() + .orElseThrow(() -> new InvalidJsonException(String.format("Invalid value for \"cropId\" in %s!", crop))); + cropMap.put(id, crop); + } + Long maxId = cropMap.isEmpty() ? 0L : Collections.max(cropMap.keySet()); + idProvider = new IdProvider(maxId); + } + } + + /** + * Writes the values from {@link #cropMap} to the {@link #dataSource} + * + * @throws IOException If the database cannot be accessed + */ + private void writeCropList() throws IOException { + if (dataSource != null) { + try { + new ObjectMapper() + .registerModule(timeModule) + .writeValue(new File(dataSource.toURI()), cropMap.values()); + } catch (URISyntaxException e) { + // TODO: Log + throw new IOException(INVALID_DATASOURCE_MSG, e); + } + } + } + +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java new file mode 100644 index 0000000..a55a010 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java @@ -0,0 +1,35 @@ +package ch.zhaw.gartenverwaltung.types; + +import java.time.LocalDate; +import java.util.Optional; + +public class Crop { + private Long cropId = null; + private final long plantId; + private final LocalDate startDate; + private int area = 1; + + public Crop(long plantId, LocalDate startDate) { + this.plantId = plantId; + this.startDate = startDate; + } + + // Builder-Style setter + public Crop withId(long cropId) { + this.cropId = cropId; + return this; + } + public Crop withArea(int area) { + this.area = area; + return this; + } + + // Getters + public Optional getCropId() { + return Optional.ofNullable(cropId); + } + + public long getPlantId() { return plantId; } + public LocalDate getStartDate() { return startDate; } + public int getArea() { return area; } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/UserPlanting.java b/src/main/java/ch/zhaw/gartenverwaltung/types/UserPlanting.java deleted file mode 100644 index 804d388..0000000 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/UserPlanting.java +++ /dev/null @@ -1,10 +0,0 @@ -package ch.zhaw.gartenverwaltung.types; - -import java.util.Date; - -public record UserPlanting( - long plantId, - Date startDate, - int area -) { -} From d686469336286539de9559983266184254b841f4 Mon Sep 17 00:00:00 2001 From: schrom01 Date: Mon, 24 Oct 2022 21:30:20 +0200 Subject: [PATCH 23/83] Created Tests for PlantListModel #15 --- build.gradle | 1 + .../plantList/PlantListModel.java | 19 ++- .../plantList/PlantListModelTest.java | 155 ++++++++++++++++++ 3 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java diff --git a/build.gradle b/build.gradle index 122bba0..74e4660 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,7 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4' + testImplementation 'org.mockito:mockito-core:4.3.+' } test { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index 86e21f2..bd12a28 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -13,17 +13,30 @@ import java.util.List; import java.util.function.Predicate; public class PlantListModel { - private final PlantDatabase plantDatabase; + private PlantDatabase plantDatabase; private HardinessZone currentZone; /** * Comparators to create sorted Plant List */ - public final Comparator sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name()); - public final Comparator getSortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); + static final Comparator sortByName = (Plant o1, Plant o2) -> o1.name().compareTo(o2.name()); + static final Comparator SortById = (Plant o1, Plant o2) -> Long.compare(o1.id(), o2.id()); + /** + * Constructor to create Database Object. + */ public PlantListModel() { plantDatabase = new JsonPlantDatabase(); + setDefaultZone(); + } + + public PlantListModel(PlantDatabase plantDatabase){ + this.plantDatabase = plantDatabase; + setDefaultZone(); + } + + private void setDefaultZone(){ + currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config } public void setCurrentZone(HardinessZone currentZone) { diff --git a/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java b/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java new file mode 100644 index 0000000..fbf6417 --- /dev/null +++ b/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java @@ -0,0 +1,155 @@ +package ch.zhaw.gartenverwaltung.plantList; + +import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; +import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; +import ch.zhaw.gartenverwaltung.io.PlantDatabase; +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import ch.zhaw.gartenverwaltung.types.Plant; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +class PlantListModelTest { + PlantDatabase plantDatabase; + List examplePlantList; + PlantListModel model; + + @BeforeEach + void setUp() throws HardinessZoneNotSetException, IOException { + createExamplePlantList(); + plantDatabase = mockPlantDatabase(examplePlantList); + model = new PlantListModel(plantDatabase); + } + + @AfterEach + void tearDown() { + } + + void createExamplePlantList(){ + examplePlantList = new ArrayList<>(); + examplePlantList.add(new Plant( + 20, + "summertime onion", + "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.", + "15,30,2", + 0, + "sandy to loamy, loose soil, free of stones", + new ArrayList<>(), + new ArrayList<>()) + ); + examplePlantList.add(new Plant( + 0, + "Potato", + "The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.", + "35", + 6, + "sandy", + new ArrayList<>(), + new ArrayList<>()) + ); + examplePlantList.add(new Plant( + 1, + "Early Carrot", + "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.", + "5,35,2.5", + 0, + "sandy to loamy, loose soil, free of stones", + new ArrayList<>(), + new ArrayList<>()) + ); + } + + PlantDatabase mockPlantDatabase(List plantList) throws HardinessZoneNotSetException, IOException { + PlantDatabase plantDatabase = mock(JsonPlantDatabase.class); + when(plantDatabase.getPlantList(HardinessZone.ZONE_8A)).thenReturn(plantList); + when(plantDatabase.getPlantList(HardinessZone.ZONE_1A)).thenReturn(new ArrayList<>()); + when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 0)).thenReturn(Optional.of(plantList.get(1))); + when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 1)).thenReturn(Optional.of(plantList.get(2))); + when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 20)).thenReturn(Optional.of(plantList.get(0))); + when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 2)).thenReturn(Optional.empty()); + + return plantDatabase; + } + + void checkCurrentZone(HardinessZone expectedZone) { + assertEquals(expectedZone, model.getCurrentZone()); + } + + @Test + void setCurrentZone() { + checkCurrentZone(HardinessZone.ZONE_8A); // TODO change to get default zone from config + model.setCurrentZone(HardinessZone.ZONE_1A); + checkCurrentZone(HardinessZone.ZONE_1A); + model.setCurrentZone(HardinessZone.ZONE_8A); + checkCurrentZone(HardinessZone.ZONE_8A); + } + + @Test + void getPlantList() throws HardinessZoneNotSetException, IOException { + model.setCurrentZone(HardinessZone.ZONE_1A); + List plantListResult = model.getPlantList(HardinessZone.ZONE_8A); + checkCurrentZone(HardinessZone.ZONE_8A); + assertEquals(examplePlantList.size(), plantListResult.size()); + assertEquals(examplePlantList.get(2), plantListResult.get(0)); + assertEquals(examplePlantList.get(1), plantListResult.get(1)); + assertEquals(examplePlantList.get(0), plantListResult.get(2)); + + assertEquals(0, model.getPlantList(HardinessZone.ZONE_1A).size()); + } + + @Test + void getSortedPlantList() throws HardinessZoneNotSetException, IOException { + model.setCurrentZone(HardinessZone.ZONE_1A); + List plantListResult = model.getSortedPlantList(HardinessZone.ZONE_8A, PlantListModel.sortByName); + checkCurrentZone(HardinessZone.ZONE_8A); + assertEquals(examplePlantList.size(), plantListResult.size()); + assertEquals(examplePlantList.get(2), plantListResult.get(0)); + assertEquals(examplePlantList.get(1), plantListResult.get(1)); + assertEquals(examplePlantList.get(0), plantListResult.get(2)); + + plantListResult = model.getSortedPlantList(HardinessZone.ZONE_8A, PlantListModel.SortById); + assertEquals(examplePlantList.size(), plantListResult.size()); + assertEquals(examplePlantList.get(1), plantListResult.get(0)); + assertEquals(examplePlantList.get(2), plantListResult.get(1)); + assertEquals(examplePlantList.get(0), plantListResult.get(2)); + } + + @Test + void getFilteredPlantList() throws HardinessZoneNotSetException, IOException { + model.setCurrentZone(HardinessZone.ZONE_1A); + Predicate predicate = new Predicate() { + @Override + public boolean test(Plant plant) { + return plant.name().toUpperCase(Locale.ROOT).contains("E"); + } + }; + List plantListResult = model.getFilteredPlantList(HardinessZone.ZONE_8A, predicate); + checkCurrentZone(HardinessZone.ZONE_8A); + assertEquals(2, plantListResult.size()); + assertEquals(examplePlantList.get(2), plantListResult.get(0)); + assertEquals(examplePlantList.get(0), plantListResult.get(1)); + } + + @Test + void getFilteredPlantListById() throws HardinessZoneNotSetException, IOException { + model.setCurrentZone(HardinessZone.ZONE_1A); + List plantListResult = model.getFilteredPlantListById(HardinessZone.ZONE_8A, 2L); + assertEquals(0, plantListResult.size()); + plantListResult = model.getFilteredPlantListById(HardinessZone.ZONE_8A, 20L); + assertEquals(1, plantListResult.size()); + assertEquals(examplePlantList.get(0), plantListResult.get(0)); + } +} \ No newline at end of file From 4e15a706fb035f43c41453099b1676ed8cc1e7ce Mon Sep 17 00:00:00 2001 From: schrom01 Date: Mon, 24 Oct 2022 21:34:19 +0200 Subject: [PATCH 24/83] code cleanup --- .../gartenverwaltung/plantList/PlantListModelTest.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java b/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java index fbf6417..0aecc35 100644 --- a/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java +++ b/src/test/java/ch/zhaw/gartenverwaltung/plantList/PlantListModelTest.java @@ -21,7 +21,6 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - class PlantListModelTest { PlantDatabase plantDatabase; List examplePlantList; @@ -130,12 +129,7 @@ class PlantListModelTest { @Test void getFilteredPlantList() throws HardinessZoneNotSetException, IOException { model.setCurrentZone(HardinessZone.ZONE_1A); - Predicate predicate = new Predicate() { - @Override - public boolean test(Plant plant) { - return plant.name().toUpperCase(Locale.ROOT).contains("E"); - } - }; + Predicate predicate = plant -> plant.name().toUpperCase(Locale.ROOT).contains("E"); List plantListResult = model.getFilteredPlantList(HardinessZone.ZONE_8A, predicate); checkCurrentZone(HardinessZone.ZONE_8A); assertEquals(2, plantListResult.size()); @@ -152,4 +146,4 @@ class PlantListModelTest { assertEquals(1, plantListResult.size()); assertEquals(examplePlantList.get(0), plantListResult.get(0)); } -} \ No newline at end of file +} From 23d87f7a85e6b609bc32d0c22e412d9d18c72db3 Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Tue, 25 Oct 2022 10:10:40 +0200 Subject: [PATCH 25/83] #17 taskdb.json changed to the right variable types, ad isIntimePeriode to , JasonTaskDatabase --- .../gartenverwaltung/io/JsonTaskDatabase.java | 69 +++++++++---------- .../ch/zhaw/gartenverwaltung/types/Task.java | 8 +++ .../ch/zhaw/gartenverwaltung/io/taskdb.json | 48 ++++++------- 3 files changed, 64 insertions(+), 61 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java index 5c7211b..caf02d1 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -1,16 +1,17 @@ package ch.zhaw.gartenverwaltung.io; -import ch.zhaw.gartenverwaltung.types.Plant; +import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Task; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer; import java.io.File; import java.io.IOException; import java.net.URL; import java.time.LocalDate; -import java.time.MonthDay; +import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.HashMap; @@ -22,26 +23,20 @@ public class JsonTaskDatabase implements TaskDatabase{ private Map taskMap = Collections.emptyMap(); - - private final static JavaTimeModule timeModule = new JavaTimeModule(); static { - DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM-dd"); - LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); - timeModule.addDeserializer(LocalDate.class, dateDeserializer); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM"); + YearMonthDeserializer dateDeserializer = new YearMonthDeserializer(dateFormat); + timeModule.addDeserializer(YearMonth.class, dateDeserializer); } @Override public List getTaskList(LocalDate start, LocalDate end) throws IOException{ - List taskList = Collections.emptyList(); - if (dataSource != null) { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(timeModule); - - taskList = mapper.readerForListOf(Task.class).readValue(dataSource); + if(taskMap.isEmpty()) { + loadTaskListFromFile(); } - return taskList; + return taskMap.values().stream().filter(task -> task.isInTimePeriode(start, end)).toList(); } @Override @@ -54,18 +49,8 @@ public class JsonTaskDatabase implements TaskDatabase{ @Override public void removeTask(Task task) throws IOException { - List taskList; - - if(dataSource != null) { - ObjectMapper mapper = new ObjectMapper(); - taskList = mapper.readerForListOf(Task.class).readValue(dataSource); - - for (int index = 0; index < taskList.size(); index++){ - if(task.equals(taskList.get(index))){ - taskList.remove(task); - break; - } - } + if(!taskMap.isEmpty() && taskMap.containsKey(task.getId())){ + taskMap.remove(task.getId()); } } @@ -76,29 +61,39 @@ public class JsonTaskDatabase implements TaskDatabase{ mapper.writeValue(new File(dataSource.getFile()), taskMap); } + private void loadTaskListFromFile() throws IOException { + if (dataSource != null) { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(timeModule); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(timeModule); + List result; + result = mapper.readerForListOf(Task.class).readValue(dataSource); - List result; - result = mapper.readerForListOf(Task.class).readValue(dataSource); - - //taskMap = result.stream().collect(HashMap::new, + taskMap = result.stream() + .collect(HashMap::new, + (res, task) -> res.put(task.getId(), task), + (existing, replacement) -> {}); + System.out.println(taskMap); + } } + //Test main method public static void main(String[] args) { LocalDate date = LocalDate.now(); LocalDate yesterday = LocalDate.now(); - yesterday.minusDays(1); - + yesterday = yesterday.minusDays(1); Task testTask = new Task("water", "apply water", date); JsonTaskDatabase jsonTaskDatabase = new JsonTaskDatabase(); + JsonPlantDatabase jsonPlantDatabase = new JsonPlantDatabase(); + try { - jsonTaskDatabase.saveTask(testTask); - System.out.println(jsonTaskDatabase.getTaskList(yesterday,date)); + //test load file + jsonPlantDatabase.getPlantList(HardinessZone.ZONE_8A); + jsonTaskDatabase.loadTaskListFromFile(); + } catch (Exception e){ - System.out.println("Task saving failed!"); + System.out.println("Task load failed!: " + e.getMessage()); } } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java index 90e00f4..64d1331 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java @@ -21,6 +21,7 @@ public class Task { this.description = description; this.startDate = startDate; } + public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval) { this.name = name; this.description = description; @@ -43,6 +44,13 @@ public class Task { return this; } + public boolean isInTimePeriode(LocalDate searchStartDate, LocalDate searchEndDate){ + if(startDate.isAfter(searchStartDate) &&startDate.isBefore(searchEndDate)){ + return true; + } + return false; + } + // Getters public long getId() { return id; } public String getName() { return name; } diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json index 1155b9a..1597ab0 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json @@ -1,50 +1,50 @@ [ { - "id" : "1", + "id" : 1, "name" : "sow plant", "description": "Plant the seeds/ crops in de bed.", - "startDate" : "01.05.2022", - "endDate" : "01.05.2022", - "interval" : "null" + "startDate" : "22-05", + "endDate" : "22-05", + "interval" : 0 }, { - "id" : "2", + "id" : 2, "name" : "water plant", "description": "water the plant, so that the soil is wet around the plant.", - "startDate" : "01.05.2022", - "endDate" : "01.09.2022", - "interval" : "2" + "startDate" : "22-05", + "endDate" : "22-09", + "interval" : 2 }, { - "id" : "3", + "id" : 3, "name" : "fertilize plant", "description": "The fertilizer has to be mixed with water. Then fertilize the plant's soil with the mixture", - "startDate" : "01.06.2022", - "endDate" : "01.08.2022", - "interval" : "28" + "startDate" : "22-06", + "endDate" : "22-08", + "interval" : 28 }, { - "id" : "4", + "id" : 4, "name" : "covering plant", "description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage", - "startDate" : "15.07.2022", - "endDate" : "15.07.2022", - "interval" : "null" + "startDate" : "22-07", + "endDate" : "22-07", + "interval" : 0 }, { - "id" : "5", + "id" : 5, "name" : "look after plant", "description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil", - "startDate" : "01.05.2022", - "endDate" : "01.09.2022", - "interval" : "5" + "startDate" : "22-05", + "endDate" : "22-09", + "interval" : 5 }, { - "id" : "6", + "id" : 6, "name" : "harvest plant", "description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ", - "startDate" : "01.09.2022", - "endDate" : "01.09.2022", - "interval" : "null" + "startDate" : "22-09", + "endDate" : "22-09", + "interval" : 0 } ] \ No newline at end of file From 6c75fcd0ecf509a5b50c99e265c69cb5e8cf4cee Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Tue, 25 Oct 2022 18:11:29 +0200 Subject: [PATCH 26/83] #17 taskdb.json changed to the right variable types, JsonTaskDatabase.java fully implemented and documented --- .../gartenverwaltung/io/JsonTaskDatabase.java | 91 ++++++++++++------- .../gartenverwaltung/io/TaskDatabase.java | 28 ++++++ .../ch/zhaw/gartenverwaltung/types/Task.java | 10 ++ .../ch/zhaw/gartenverwaltung/io/taskdb.json | 28 +++--- 4 files changed, 110 insertions(+), 47 deletions(-) diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java index caf02d1..a460f2f 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -1,67 +1,112 @@ package ch.zhaw.gartenverwaltung.io; -import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Task; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; -import com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer; import java.io.File; import java.io.IOException; import java.net.URL; import java.time.LocalDate; -import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * Implements the {@link TaskDatabase} interface for loading and writing {@link Task} objects + * from and to a JSON file. + * The reads are cached to minimize file-io operations. + */ public class JsonTaskDatabase implements TaskDatabase{ + IdProvider idProvider; private final URL dataSource = getClass().getResource("taskdb.json"); private Map taskMap = Collections.emptyMap(); + /** + * Creating constant objects required to deserialize the {@link LocalDate} classes + */ private final static JavaTimeModule timeModule = new JavaTimeModule(); static { - DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM"); - YearMonthDeserializer dateDeserializer = new YearMonthDeserializer(dateFormat); - timeModule.addDeserializer(YearMonth.class, dateDeserializer); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); + timeModule.addDeserializer(LocalDate.class, dateDeserializer); } + /** + * If no data is currently loaded, data is loaded from {@link #dataSource}. + * In any case, the values of {@link #taskMap} are returned. + * + * @see TaskDatabase#getTaskList(LocalDate, LocalDate) + */ @Override public List getTaskList(LocalDate start, LocalDate end) throws IOException{ - if(taskMap.isEmpty()) { loadTaskListFromFile(); } return taskMap.values().stream().filter(task -> task.isInTimePeriode(start, end)).toList(); } + /** + * If no data is currently loaded, data is loaded from {@link #dataSource}. + * If the {@link Task} has an id than the task is added to the {@link #taskMap} + * otherwise the id is generated with the {@link IdProvider} before adding + * it to the {@link #taskMap}. In any case, the {@link #taskMap} is written + * to the {@link #dataSource}. + * + * @see TaskDatabase#saveTask(Task) + */ @Override public void saveTask(Task task) throws IOException { - ObjectMapper mapper = new ObjectMapper(); - if(dataSource != null) { - mapper.writeValue(new File(dataSource.getFile()), task); + if(taskMap.isEmpty()) { + loadTaskListFromFile(); } + if(task.getId() == 0) { + task.withId(idProvider.incrementAndGet()); + } + writeTaskListToFile(); } + /** + * If no data is currently loaded, data is loaded from {@link #dataSource}. + * If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed + * from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}. + * + * @see TaskDatabase#removeTask(Task) + */ @Override public void removeTask(Task task) throws IOException { - if(!taskMap.isEmpty() && taskMap.containsKey(task.getId())){ + if(taskMap.isEmpty()) { + loadTaskListFromFile(); + } + if(taskMap.containsKey(task.getId())){ taskMap.remove(task.getId()); + writeTaskListToFile(); } } + /** + * Writes cached data to the {@link #dataSource}. + * + * @throws IOException If the database cannot be accessed + */ private void writeTaskListToFile() throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(timeModule); - mapper.writeValue(new File(dataSource.getFile()), taskMap); + if(dataSource != null) { + mapper.writeValue(new File(dataSource.getFile()), taskMap); + } } - + /** + * Loads the database from {@link #dataSource} and updates the cached data. + * + * @throws IOException If the database cannot be accessed + */ private void loadTaskListFromFile() throws IOException { if (dataSource != null) { ObjectMapper mapper = new ObjectMapper(); @@ -74,26 +119,6 @@ public class JsonTaskDatabase implements TaskDatabase{ .collect(HashMap::new, (res, task) -> res.put(task.getId(), task), (existing, replacement) -> {}); - System.out.println(taskMap); - } - } - - //Test main method - public static void main(String[] args) { - LocalDate date = LocalDate.now(); - LocalDate yesterday = LocalDate.now(); - yesterday = yesterday.minusDays(1); - Task testTask = new Task("water", "apply water", date); - JsonTaskDatabase jsonTaskDatabase = new JsonTaskDatabase(); - JsonPlantDatabase jsonPlantDatabase = new JsonPlantDatabase(); - - try { - //test load file - jsonPlantDatabase.getPlantList(HardinessZone.ZONE_8A); - jsonTaskDatabase.loadTaskListFromFile(); - - } catch (Exception e){ - System.out.println("Task load failed!: " + e.getMessage()); } } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java index e9c52a8..19ad778 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/TaskDatabase.java @@ -1,5 +1,7 @@ package ch.zhaw.gartenverwaltung.io; +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Task; import java.io.IOException; @@ -7,8 +9,34 @@ import java.time.LocalDate; import java.util.Date; import java.util.List; +/** + * A database of {@link Task}s. + * The interface specifies the minimal required operations. + */ public interface TaskDatabase { + /** + * Yields a list of all {@link Task}s in the database with the start and end date of a period of time. + * + * @param start The start date of the wanted time period + * @param end The end date of the wanted time period + * @return A list of {@link Task}s planned in the specified time period + * @throws IOException If the database cannot be accessed + */ List getTaskList(LocalDate start, LocalDate end) throws IOException; + + /** + * Saves the {@link Task} in the Cache. + * + * @param task The {@link Task} which is wanted to be saved in the TaskList + * @throws IOException If the database cannot be accessed + */ void saveTask(Task task) throws IOException; + + /** + * Removes the {@link Task}from the Cache. + * + * @param task The {@link Task} which has to be removed from the {@link Task} list. + * @throws IOException If the database cannot be accessed + */ void removeTask(Task task) throws IOException; } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java index 64d1331..3b26745 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Task.java @@ -16,6 +16,16 @@ public class Task { private Integer interval; private LocalDate endDate; + /** + * default constructor + * (used by Json deserializer) + */ + public Task(){ + name= ""; + description= ""; + startDate = LocalDate.now(); + } + public Task(String name, String description, LocalDate startDate) { this.name = name; this.description = description; diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json index 1597ab0..da201c9 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json @@ -2,49 +2,49 @@ { "id" : 1, "name" : "sow plant", - "description": "Plant the seeds/ crops in de bed.", - "startDate" : "22-05", - "endDate" : "22-05", + "description": "Plant the seeds, crops in de bed.", + "startDate" : "2022-05-01", + "endDate" : "2022-05-01", "interval" : 0 }, { "id" : 2, "name" : "water plant", "description": "water the plant, so that the soil is wet around the plant.", - "startDate" : "22-05", - "endDate" : "22-09", + "startDate" : "2022-05-01", + "endDate" : "2022-09-01", "interval" : 2 }, { "id" : 3, "name" : "fertilize plant", - "description": "The fertilizer has to be mixed with water. Then fertilize the plant's soil with the mixture", - "startDate" : "22-06", - "endDate" : "22-08", + "description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture", + "startDate" : "2022-06-01", + "endDate" : "2022-08-01", "interval" : 28 }, { "id" : 4, "name" : "covering plant", "description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage", - "startDate" : "22-07", - "endDate" : "22-07", + "startDate" : "2022-07-01", + "endDate" : "2022-07-01", "interval" : 0 }, { "id" : 5, "name" : "look after plant", "description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil", - "startDate" : "22-05", - "endDate" : "22-09", + "startDate" : "2022-05-01", + "endDate" : "2022-09-01", "interval" : 5 }, { "id" : 6, "name" : "harvest plant", "description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ", - "startDate" : "22-09", - "endDate" : "22-09", + "startDate" : "2022-09-01", + "endDate" : "2022-09-01", "interval" : 0 } ] \ No newline at end of file From 629e64143b2f68609696f94d4331b0416e930b18 Mon Sep 17 00:00:00 2001 From: David Guler Date: Wed, 26 Oct 2022 20:32:01 +0200 Subject: [PATCH 27/83] Fixed serialisation of cropId and startDate --- build.gradle | 1 + .../gartenverwaltung/io/JsonGardenPlan.java | 8 ++++++- .../ch/zhaw/gartenverwaltung/types/Crop.java | 23 ++++++++++++++++--- src/main/java/module-info.java | 2 +- .../zhaw/gartenverwaltung/io/user-crops.json | 20 ++++++++++++++++ 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/io/user-crops.json diff --git a/build.gradle b/build.gradle index 122bba0..6f8ef6d 100644 --- a/build.gradle +++ b/build.gradle @@ -39,6 +39,7 @@ dependencies { testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}") implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4' } test { diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java index c4e224c..5a25a0a 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonGardenPlan.java @@ -2,8 +2,10 @@ package ch.zhaw.gartenverwaltung.io; import ch.zhaw.gartenverwaltung.types.Crop; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import java.io.File; import java.io.IOException; @@ -29,9 +31,12 @@ public class JsonGardenPlan implements GardenPlan { private final static JavaTimeModule timeModule = new JavaTimeModule(); private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!"; static { - DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM-dd"); + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); + LocalDateSerializer dateSerializer = new LocalDateSerializer(dateFormat); + timeModule.addDeserializer(LocalDate.class, dateDeserializer); + timeModule.addSerializer(LocalDate.class, dateSerializer); } /** @@ -123,6 +128,7 @@ public class JsonGardenPlan implements GardenPlan { try { new ObjectMapper() .registerModule(timeModule) + .registerModule(new Jdk8Module()) .writeValue(new File(dataSource.toURI()), cropMap.values()); } catch (URISyntaxException e) { // TODO: Log diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java index a55a010..fbd6286 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Crop.java @@ -7,7 +7,15 @@ public class Crop { private Long cropId = null; private final long plantId; private final LocalDate startDate; - private int area = 1; + private double area = 1; + + /** + * Default Constructor (needed for deserialization) + */ + public Crop() { + plantId = 0; + startDate = null; + } public Crop(long plantId, LocalDate startDate) { this.plantId = plantId; @@ -19,7 +27,7 @@ public class Crop { this.cropId = cropId; return this; } - public Crop withArea(int area) { + public Crop withArea(double area) { this.area = area; return this; } @@ -31,5 +39,14 @@ public class Crop { public long getPlantId() { return plantId; } public LocalDate getStartDate() { return startDate; } - public int getArea() { return area; } + public double getArea() { return area; } + + @Override + public String toString() { + return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]", + cropId, + plantId, + startDate, + area); + } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 8e02fc7..00cf813 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,10 +3,10 @@ module ch.zhaw.gartenverwaltung { requires javafx.fxml; requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.datatype.jsr310; + requires com.fasterxml.jackson.datatype.jdk8; opens ch.zhaw.gartenverwaltung to javafx.fxml; opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; -// opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; exports ch.zhaw.gartenverwaltung; exports ch.zhaw.gartenverwaltung.types; exports ch.zhaw.gartenverwaltung.json; diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/io/user-crops.json b/src/main/resources/ch/zhaw/gartenverwaltung/io/user-crops.json new file mode 100644 index 0000000..ebd1d2d --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/io/user-crops.json @@ -0,0 +1,20 @@ +[ + { + "cropId": 0, + "plantId": 1, + "startDate": "2023-02-25", + "area": 0.5 + }, + { + "cropId": 1, + "plantId": 1, + "startDate": "2023-03-01", + "area": 0.5 + }, + { + "cropId": 2, + "plantId": 0, + "startDate": "2023-03-25", + "area": 1.5 + } +] \ No newline at end of file From 51e8f27a20bdc7ca28b6bd28ff8f67f6ae6ebc48 Mon Sep 17 00:00:00 2001 From: Gian-Andrea Hutter Date: Fri, 28 Oct 2022 12:24:55 +0200 Subject: [PATCH 28/83] #17 implementation objectmapper writeFile --- .../zhaw/gartenverwaltung/gardenplan/Gardenplanmodel.java | 8 ++++++++ .../ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/gardenplan/Gardenplanmodel.java diff --git a/src/main/java/ch/zhaw/gartenverwaltung/gardenplan/Gardenplanmodel.java b/src/main/java/ch/zhaw/gartenverwaltung/gardenplan/Gardenplanmodel.java new file mode 100644 index 0000000..24757bd --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/gardenplan/Gardenplanmodel.java @@ -0,0 +1,8 @@ +package ch.zhaw.gartenverwaltung.gardenplan; + +public class Gardenplanmodel { +// private JasonGardenplan gardenplan; + // liste von crops + //task generieren + //plant holen, task template mit tasktemplateklasse tasks erstellen +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java index a460f2f..e16a74d 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java @@ -2,6 +2,7 @@ package ch.zhaw.gartenverwaltung.io; import ch.zhaw.gartenverwaltung.types.Task; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; @@ -95,7 +96,8 @@ public class JsonTaskDatabase implements TaskDatabase{ */ private void writeTaskListToFile() throws IOException { ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(timeModule); + mapper.registerModule(timeModule) + .registerModule(new Jdk8Module()); if(dataSource != null) { mapper.writeValue(new File(dataSource.getFile()), taskMap); From 7fd18f3830a7da8fc9ce68ea38032fe372ec5231 Mon Sep 17 00:00:00 2001 From: giavaphi Date: Fri, 28 Oct 2022 15:36:33 +0200 Subject: [PATCH 29/83] #16 create MainFXML with skeletal structure --- .../gartenverwaltung/HelloApplication.java | 6 +- .../zhaw/gartenverwaltung/HomeController.java | 5 + .../gartenverwaltung/MainFXMLController.java | 94 +++++++++ .../gartenverwaltung/MyPlantsController.java | 58 ++++++ .../MyScheduleController.java | 124 ++++++++++++ .../gartenverwaltung/PlantsController.java | 186 ++++++++++++++++++ .../plantList/PlantListModel.java | 5 +- .../ch/zhaw/gartenverwaltung/types/Plant.java | 3 +- .../ch/zhaw/gartenverwaltung/Home.fxml | 43 ++++ .../ch/zhaw/gartenverwaltung/MainFXML.fxml | 21 ++ .../ch/zhaw/gartenverwaltung/MyPlants.fxml | 36 ++++ .../ch/zhaw/gartenverwaltung/MySchedule.fxml | 74 +++++++ .../ch/zhaw/gartenverwaltung/Plants.fxml | 125 ++++++++++++ .../ch/zhaw/gartenverwaltung/placeholder.png | Bin 0 -> 4734 bytes 14 files changed, 774 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/HomeController.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java create mode 100644 src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/MyPlants.fxml create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml create mode 100644 src/main/resources/ch/zhaw/gartenverwaltung/placeholder.png diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java index 0d70b44..fd31017 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/HelloApplication.java @@ -10,9 +10,9 @@ import java.io.IOException; public class HelloApplication extends Application { @Override public void start(Stage stage) throws IOException { - FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 320, 240); - stage.setTitle("Hello!"); + FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("MainFXML.fxml")); + Scene scene = new Scene(fxmlLoader.load()); + stage.setTitle("Gartenverwaltung"); stage.setScene(scene); stage.show(); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/HomeController.java b/src/main/java/ch/zhaw/gartenverwaltung/HomeController.java new file mode 100644 index 0000000..7df4661 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/HomeController.java @@ -0,0 +1,5 @@ +package ch.zhaw.gartenverwaltung; + +public class HomeController +{ +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java b/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java new file mode 100644 index 0000000..c3fe04d --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/MainFXMLController.java @@ -0,0 +1,94 @@ +package ch.zhaw.gartenverwaltung; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.layout.AnchorPane; + +import java.io.IOException; +import java.net.URL; +import java.util.Objects; +import java.util.ResourceBundle; + +public class MainFXMLController implements Initializable { + + @FXML + private Button home_button; + + @FXML + private AnchorPane mainPane; + + @FXML + private Button myPlants_button; + + @FXML + private Button mySchedule_button; + + @FXML + private Button plants_button; + + @FXML + void goToHome(ActionEvent event) throws IOException { + loadPane("Home.fxml"); + styleChangeButton(home_button); + } + + @FXML + void goToMyPlants(ActionEvent event) throws IOException { + loadPane("MyPlants.fxml"); + styleChangeButton(myPlants_button); + } + + @FXML + void goToMySchedule(ActionEvent event) throws IOException { + loadPane("MySchedule.fxml"); + styleChangeButton(mySchedule_button); + } + + @FXML + void goToPlants(ActionEvent event) throws IOException { + loadPane("Plants.fxml"); + styleChangeButton(plants_button); + } + + /** + * Updates the mainPane with the selected fxml file. + * set HGrow and VGrow to parent AnchorPane. + * Sends MainController to other Controllers. + * @param fxmlFile string of fxml file + * @throws IOException exception when file does not exist + */ + public void loadPane(String fxmlFile) throws IOException { + //ToDo HGrow and VGrow of new node + Node node; + FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); + node = (Node)loader.load(); + if(fxmlFile.equals("MyPlants.fxml")) { + MyPlantsController myPlantsController = loader.getController(); + myPlantsController.getMainController(this); + } + mainPane.getChildren().setAll(node); + } + + private void styleChangeButton(Button button) { + //ToDo changeStyle of the menu buttons + } + + /** + * loads the default FXML File + * {@inheritDoc} + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + try { + loadPane("Home.fxml"); + styleChangeButton(home_button); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java b/src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java new file mode 100644 index 0000000..e223e8b --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java @@ -0,0 +1,58 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.layout.VBox; + +import java.io.IOException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; +import java.util.ResourceBundle; + +public class MyPlantsController implements Initializable { + MainFXMLController mainController; + + @FXML + private Button addPlant_button; + + @FXML + private VBox myPlants_vbox; + + @FXML + void addPlant(ActionEvent event) throws IOException { + mainController.loadPane("Plants.fxml"); + } + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + //ToDo + List myPlants = getMyPlants(); + createPlantView(myPlants); + } + + private void createPlantView(List myPlants) { + //ToDo + for(Plant plant : myPlants) { + createPlantView(); + } + } + + public void getMainController(MainFXMLController controller) { + mainController = controller; + } + + private List getMyPlants() { + //ToDo method to get myPlantList(scheduled) + //Method to call all Plants saved + List myPlantList = new LinkedList<>(); + return myPlantList; + } + + private void createPlantView() { + //ToDo FXML Panel with Plant data + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java b/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java new file mode 100644 index 0000000..b16445a --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java @@ -0,0 +1,124 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Pane; + +import java.net.URL; +import java.time.LocalDate; +import java.util.LinkedList; +import java.util.List; +import java.util.ResourceBundle; + +public class MyScheduleController implements Initializable { + private Plant selectedPlant = null; + + @FXML + private Label day1_label; + + @FXML + private Pane day1_pane; + + @FXML + private Label day2_label; + + @FXML + private Pane day2_pane; + + @FXML + private Label day3_label; + + @FXML + private Pane day3_pane; + + @FXML + private Label day4_label; + + @FXML + private Pane day4_pane; + + @FXML + private Label day5_label; + + @FXML + private Pane day5_pane; + + @FXML + private Label day6_label; + + @FXML + private Pane day6_pane; + + @FXML + private Label day7_label; + + @FXML + private Pane day7_pane; + + @FXML + private Label information_label; + + @FXML + private ListView scheduledPlants_listview; + + @Override + public void initialize(URL location, ResourceBundle resources) { + List plantList = new LinkedList<>(); + fillListViewMyPlantsInSchedule(plantList); + getSelectedPlantTask(); + setDayLabels(); + information_label.setText(""); + } + + private void getSelectedPlantTask() { + scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Plant oldValue, Plant newValue) { + if(newValue != null) { + selectedPlant = newValue; + //ToDo update day_panel with task for the day + } else { + selectedPlant = null; + //ToDo update day_panel with task for the day (all plants) + } + } + }); + } + + private void setDayLabels() { + LocalDate today = LocalDate.now(); + day1_label.setText(today.getDayOfWeek().toString()); + day2_label.setText(today.plusDays(1).getDayOfWeek().toString()); + day3_label.setText(today.plusDays(2).getDayOfWeek().toString()); + day4_label.setText(today.plusDays(3).getDayOfWeek().toString()); + day5_label.setText(today.plusDays(4).getDayOfWeek().toString()); + day6_label.setText(today.plusDays(5).getDayOfWeek().toString()); + day7_label.setText(today.plusDays(6).getDayOfWeek().toString()); + } + + private void fillListViewMyPlantsInSchedule(List list) { + for (Plant plant : list) { + scheduledPlants_listview.getItems().add(plant); + } + scheduledPlants_listview.setCellFactory(param -> new ListCell() { + @Override + protected void updateItem(Plant plant, boolean empty) { + super.updateItem(plant, empty); + + if (empty || plant == null || plant.name() == null) { + setText(null); + } else { + setText(plant.name()); + } + } + }); + } + + +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java new file mode 100644 index 0000000..a8c8d93 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java @@ -0,0 +1,186 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; +import ch.zhaw.gartenverwaltung.plantList.PlantListModel; +import ch.zhaw.gartenverwaltung.types.HardinessZone; +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.scene.image.ImageView; +import javafx.scene.input.InputMethodEvent; +import javafx.scene.layout.VBox; + +import java.io.IOException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; +import java.util.ResourceBundle; + +public class PlantsController implements Initializable { + private final PlantListModel plantListModel = new PlantListModel(); + private Plant selectedPlant = null; + private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A; + + @FXML + private CheckBox autum_filter; + + @FXML + private VBox climate_zones; + + @FXML + private Label description_plant; + + @FXML + private ImageView img_plant; + + @FXML + private ListView list_plants; + + @FXML + private Button saveToMyPlant_button; + + @FXML + private TextField search_plants; + + @FXML + private CheckBox sommer_filter; + + @FXML + private CheckBox spring_filter; + + @FXML + private CheckBox winter_filter; + + @FXML + void filterAutum(ActionEvent event) { + //ToDo + + } + + @FXML + void filterSommer(ActionEvent event) { + //ToDo + + } + + @FXML + void filterSpring(ActionEvent event) { + //ToDo + + } + + @FXML + void filterWinter(ActionEvent event) { + //ToDo + + } + + @FXML + void saveToMyPlant(ActionEvent event) { + //ToDo model save selectedPlant to mySelectedPlant(IO) + } + + @FXML + void searchForPlant(InputMethodEvent event) { + viewFilteredListBySearch(search_plants.getText()); + } + + /** + * {@inheritDoc} + */ + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + List plantList = new LinkedList<>(); + try { + plantList = plantListModel.getPlantList(DEFAULT_HARDINESS_ZONE); + } catch (HardinessZoneNotSetException | IOException e) { + e.printStackTrace(); + } + fillListViewWithData(plantList); + + description_plant.setText(""); + saveToMyPlant_button.setDisable(true); + + createFilterHardinessZone(); + lookForSelectedListEntry(); + } + + /** + * update the ListView according to the plant list provided + * Entry in ListView is plant name + * @param list plantList which fill the ListView + */ + private void fillListViewWithData(List list) { + clearListView(); + for (Plant plant : list) { + list_plants.getItems().add(plant); + } + list_plants.setCellFactory(param -> new ListCell() { + @Override + protected void updateItem(Plant plant, boolean empty) { + super.updateItem(plant, empty); + + if (empty || plant == null || plant.name() == null) { + setText(null); + } else { + setText(plant.name()); + } + } + }); + } + + private void viewFilteredListByFilters() { + boolean springValue = spring_filter.isSelected(); + boolean sommerValue = sommer_filter.isSelected(); + boolean autumValue = autum_filter.isSelected(); + boolean winterValue = winter_filter.isSelected(); + //ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, )) + //List plantList = new LinkedList<>(); + //fillListViewWithData(plantList); + } + + private void viewFilteredListBySearch(String query) { + //ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, )) + //List plantList = new LinkedList<>(); + //fillListViewWithData(plantList); + } + + private void createFilterHardinessZone() { + //ToDo create radioList of hardinessZone in VBox climate_zones + } + + /** + * observes changes in the selectedProperty of ListView and updates the description label + */ + private void lookForSelectedListEntry() { + list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Plant oldValue, Plant newValue) { + //ToDo + if(newValue != null) { + selectedPlant = newValue; + description_plant.setText(selectedPlant.description()); + saveToMyPlant_button.setDisable(false); + //update img plant + } else { + selectedPlant = null; + description_plant.setText(""); + saveToMyPlant_button.setDisable(true); + //update img when null placeholder PNG + } + } + }); + } + + + /** + * clears the ListView of entries + */ + private void clearListView() { + list_plants.getItems().clear(); + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java index bd12a28..8217962 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/plantList/PlantListModel.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; +import java.util.stream.Collectors; public class PlantListModel { private PlantDatabase plantDatabase; @@ -67,7 +68,7 @@ public class PlantListModel { */ public List getSortedPlantList(HardinessZone zone, Comparator comparator) throws HardinessZoneNotSetException, IOException { setCurrentZone(zone); - return plantDatabase.getPlantList(zone).stream().sorted(comparator).toList(); + return plantDatabase.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList()); } /** @@ -80,7 +81,7 @@ public class PlantListModel { */ public List getFilteredPlantList(HardinessZone zone, Predicate predicate) throws HardinessZoneNotSetException, IOException { setCurrentZone(zone); - return getPlantList(zone).stream().filter(predicate).toList(); + return getPlantList(zone).stream().filter(predicate).collect(Collectors.toList()); } /** diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 13652a6..ee90a6e 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -2,6 +2,7 @@ package ch.zhaw.gartenverwaltung.types; import java.time.LocalDate; import java.util.List; +import java.util.stream.Collectors; import static java.time.temporal.ChronoUnit.DAYS; @@ -22,7 +23,7 @@ public record Plant( public List lifecycleForGroup(int group) { return lifecycle.stream() .filter(growthPhase -> growthPhase.group() != group) - .toList(); + .collect(Collectors.toList()); } public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) { diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml new file mode 100644 index 0000000..65ee774 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/Home.fxml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml new file mode 100644 index 0000000..75ff18b --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/MainFXML.fxml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml new file mode 100644 index 0000000..0a75e12 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/MySchedule.fxml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml new file mode 100644 index 0000000..80abd33 --- /dev/null +++ b/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +