Compare commits

...
Author SHA1 Message Date
giavaphiandGitHub Enterprise 2be9df6094 Merge branch 'dev' into feature_cropsAndTaskGUI_M2 2022-11-11 12:21:05 +01:00
Roman SchenkandGitHub Enterprise 5faf61089a Merge pull request #56 from schrom01/feature_logger_M2
Include Logger
2022-11-11 12:19:04 +01:00
giavaphi 8e23124c6b display List of Crops, get detail of crop and display taskList 2022-11-10 22:50:49 +01:00
Gian-Andrea Hutter 78a27499a8 #27 gardenplanmodelTest bugfixed 2022-11-08 23:11:38 +01:00
giavaphi 0e40bc6304 Merge remote-tracking branch 'origin/feature_cropsAndTaskGUI_M2' into feature_cropsAndTaskGUI_M2
# Conflicts:
#	src/main/java/ch/zhaw/gartenverwaltung/CropDetailController.java
#	src/main/java/ch/zhaw/gartenverwaltung/MyPlantsController.java
#	src/main/java/ch/zhaw/gartenverwaltung/MyScheduleController.java
#	src/main/resources/ch/zhaw/gartenverwaltung/CropDetail.fxml
2022-11-08 21:08:23 +01:00
giavaphi ced2645bd7 connection task list model and garden plan model with controllers 2022-11-08 21:03:01 +01:00
giavaphi 802f238d69 gui details of crop basics 2022-11-08 19:26:08 +01:00
giavaphi 096abfd148 #48 and #47 basics of gui 2022-11-08 19:26:08 +01:00
David Guler 590049b9cf feat: Added logging and refactored code to accommodate 2022-11-08 07:36:31 +01:00
David Guler ad05e9e95a refactor: remove exception-based control-flow 2022-11-08 07:31:04 +01:00
giavaphiandGitHub Enterprise 60c6dcd0d9 Merge pull request #49 from schrom01/feature_gardenplan-model_M2
Feature gardenplan model m2
2022-11-07 11:57:40 +01:00
giavaphiandGitHub Enterprise 52ae2b02bc Merge pull request #55 from schrom01/feature_savePlantToCropGUI_M2
savePlantToCropGUI m2
2022-11-07 11:56:59 +01:00
Roman SchenkandGitHub Enterprise 00db602904 Merge branch 'dev' into feature_gardenplan-model_M2 2022-11-06 18:22:33 +01:00
giavaphiandGitHub Enterprise 5e206ace39 Merge pull request #51 from schrom01/feature_taskList_m2
TaskListModel
2022-11-06 17:50:30 +01:00
giavaphi ea4a1ecc6a gui details of crop basics 2022-11-06 17:46:35 +01:00
schrom01 b7d08944a6 added new Methods to filter List by Crop ID 2022-11-06 17:29:21 +01:00
giavaphi 3077e02b32 #48 and #47 basics of gui 2022-11-05 23:50:45 +01:00
giavaphi 96fdc64105 #36 update size of imageView 2022-11-05 17:55:25 +01:00
giavaphi e92538fbb4 #54 update Seasons enum + update method name + javadoc update 2022-11-05 17:19:41 +01:00
giavaphi 2cf7f55215 #53 fixed bug get sow date from harvest date + javadoc Plant and SelectSowDayController 2022-11-05 16:50:18 +01:00
David Guler f6ec411b7a doc: design class diagram "dcd" 2022-11-05 09:50:35 +01:00
Gian-Andrea Hutter 8408af175d #27 gardenplanmodelTest bugfixed 2022-11-04 15:46:11 +01:00
Gian-Andrea Hutter e91773b360 #27 gardenplanmodelTest bugfixed 2022-11-04 15:35:45 +01:00
Gian-Andrea Hutter 2361afd537 #27 gardenplanmodel bugfixed cropid 2022-11-04 15:32:15 +01:00
giavaphiandGitHub Enterprise a43a23427c Merge pull request #50 from schrom01/feature_json-gardenplan_M2 2022-11-04 12:12:43 +01:00
Gian-Andrea Hutter d45b8e116e Merge remote-tracking branch 'origin/feature_taskList_m2' into feature_gardenplan-model_M2
# Conflicts:
#	src/test/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabaseTest.java
2022-11-04 07:39:57 +01:00
gulerdavandGitHub Enterprise fb0c50a715 Merge branch 'dev' into feature_json-gardenplan_M2 2022-11-03 14:31:23 +01:00
David Guler 83bc011870 #26 added fixed test files, made Crop class testable 2022-11-03 14:28:26 +01:00
David Guler ce93531ab8 Made tests pass for plants not in zone 2022-11-03 14:25:25 +01:00
giavaphi efc95dbcc6 #46 DatePicker for sow and harvest day 2022-11-02 19:33:27 +01:00
Gian-Andrea Hutter 2cb64f3dee Merge branch 'dev' into feature_gardenplan-model_M2 2022-11-02 13:10:06 +01:00
Gian-Andrea Hutter b1e4e51d68 #27 gardenplanmodelTest first version implemented 2022-11-02 13:08:23 +01:00
Gian-Andrea Hutter 9e14920fbb #27 gardenplanmodel fully implemented 2022-11-01 10:33:07 +01:00
Gian-Andrea Hutter 0987b42086 Merge remote-tracking branch 'origin/feature_taskList_m2' into feature_gardenplan-model_M2
# Conflicts:
#	src/main/resources/ch/zhaw/gartenverwaltung/io/taskdb.json
2022-10-31 14:32:33 +01:00
Gian-Andrea Hutter 521f3ae025 Merge remote-tracking branch 'origin/feature_taskList_m2' into feature_gardenplan-model_M2 2022-10-31 14:28:06 +01:00
Gian-Andrea Hutter 52b4b1c01d Merge remote-tracking branch 'origin/feature_taskList_m2' into feature_gardenplan-model_M2 2022-10-31 13:47:38 +01:00
Gian-Andrea Hutter bc3ba43b7e Merge branch 'feature_json-task-db_M2' into feature_gardenplan-model_M2
# Conflicts:
#	src/main/java/ch/zhaw/gartenverwaltung/gardenplan/Gardenplanmodel.java
#	src/main/java/ch/zhaw/gartenverwaltung/io/JsonTaskDatabase.java
2022-10-31 13:23:02 +01:00
Gian-Andrea Hutter 2e12c3f868 Merge remote-tracking branch 'origin/feature_json-task-db_M2' into feature_json-task-db_M2 2022-10-31 13:19:31 +01:00
Gian-Andrea Hutter 29ad2fdae2 #17 Bug Mismatchexception fixed 2022-10-31 13:19:14 +01:00
giavaphiandGitHub Enterprise 7a6a0eb66f Merge pull request #45 from schrom01/feature_controllerPlantList_M2
Plant Controller Filter to hardiness zone and season
2022-10-31 13:07:49 +01:00
gulerdavandGitHub Enterprise 3afeb8a22d Merge branch 'dev' into feature_controllerPlantList_M2 2022-10-31 13:05:49 +01:00
gulerdavandGitHub Enterprise 88f9bf7990 Merge pull request #44 from schrom01/feature_plantlist-gui_M2
Display images in gui and fix image deserialise with spaces
2022-10-31 12:59:06 +01:00
Elias Csomor 5c6528a038 Update JsonTaskDatabaseTest.java 2022-10-31 12:54:52 +01:00
giavaphi 5f35d99839 fixed merge conflict PlantsController 2022-10-31 12:52:27 +01:00
Gian-Andrea Hutter afac3ba855 #17 Serialization implemented 2022-10-31 12:45:24 +01:00
Gian-Andrea Hutter a87f3da9d2 Merge branch 'feature_plantlist-gui_M2' into feature_json-task-db_M2 2022-10-31 12:43:43 +01:00
schrom01 d0cef1fe82 fixed problems with file Path
#43
2022-10-31 12:43:13 +01:00
Gian-Andrea Hutter 6273b0e59a #27 implementation of task template with task generation 2022-10-31 12:24:54 +01:00
Elias Csomor 2510608117 Extended taskDb tests 2022-10-31 11:55:20 +01:00
Elias Csomor 82eab6d5cd continue GardenPlan tests 2022-10-31 11:21:45 +01:00
Elias Csomor 5411ac69ae initial GardenPlan tests 2022-10-31 11:15:27 +01:00
Elias Csomor 146c43d5d9 extended tests for Zones 2022-10-31 10:07:06 +01:00
David Guler 7a060be84a case insensitive serarch, images 2022-10-31 10:06:17 +01:00
Elias Csomor 160880d4f5 removed useless quotes, corrected typo 2022-10-31 09:42:39 +01:00
giavaphi 5b039eb762 #12 small changes and java doc 2022-10-31 09:23:44 +01:00
Gian-Andrea Hutter b79387abc2 Merge branch 'dev' into feature_json-task-db_M2 2022-10-31 08:40:52 +01:00
gulerdavandGitHub Enterprise 6c00b7f182 Merge pull request #41 from schrom01/feature_plantList_M2
fixed Method getFilteredPlantListByString
2022-10-31 08:01:43 +01:00
Gian-Andrea Hutter 63d7eddff4 #27 first implemention of the Gardenplanmodel 2022-10-30 23:34:42 +01:00
giavaphi c83b8695ab #12 PlantsController filter list view 2022-10-30 23:23:19 +01:00
giavaphi f22ef61d3c #42 bug resize window fix 2022-10-30 18:06:18 +01:00
schrom01 98ff259d95 fixed Method getFilteredPlantListByString for case if SearchString is empty. 2022-10-30 10:38:46 +01:00
39 changed files with 2278 additions and 325 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

+144
View File
@@ -0,0 +1,144 @@
<diagram program="umletino" version="15.0.0"><zoom_level>10</zoom_level><element><id>UMLClass</id><coordinates><x>482</x><y>360</y><w>100</w><h>30</h></coordinates><panel_attributes>Main</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1082</x><y>380</y><w>370</w><h>100</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
PlantDatabase
--
+ getPlantList(zone: HardinessZone): List&lt;Plant&gt;
+ getPlantById(zone: HardinessZone id: long): Optional&lt;Plant&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>672</x><y>710</y><w>220</w><h>40</h></coordinates><panel_attributes>TaskListController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1132</x><y>110</y><w>300</w><h>180</h></coordinates><panel_attributes>&lt;&lt;Record&gt;&gt;
Plant
--
+ id: long
+ name: String
+ description: String
+ spacing: int
+ lifecycle: List&lt;GrowthPhase&gt;
--
+ calculateStartDate(harvestDate: Date): Date
+ generateTasks()
</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>942</x><y>710</y><w>210</w><h>120</h></coordinates><panel_attributes>TaskListModel
--
- tasks: ListProperty&lt;Task&gt;
- taskDb: TaskDatabase
--
+ getTask(id: long): Optional&lt;Task&gt;
+ saveTask(task: Task)
+ removeTask(task: Task)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>322</x><y>690</y><w>250</w><h>120</h></coordinates><panel_attributes>GardenPlanModel
--
- tasks: ListProperty&lt;Crop&gt;
- gardenPlan: GardenPlan
--
+ plantAsCrop(planting: UserPlanting)
+ removePlanting(planting: UserPlanting)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>72</x><y>680</y><w>180</w><h>80</h></coordinates><panel_attributes>GardenPlanController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1362</x><y>830</y><w>490</w><h>240</h></coordinates><panel_attributes>Task
--
+ id: long
+ name: String {readOnly}
+ description: String {readOnly}
+ startDate: Date {readOnly}
+ isOptional: boolean {readOnly}
- interval: int
- endDate: Date
--
+ Task(name: String, description: String, startDate: String, isReadOnly: boolean): Task
+ withInterval(interval: int): Task
+ withEndDate(endDate: Date): Task
+ withId(id: long): Task
--
+ getInterval(): Optional&lt;int&gt;
+ getEndDate(): Optional&lt;Date&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>902</x><y>930</y><w>280</w><h>140</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
TaskDatabase
--
+ getTaskList(start: Date, end: Date): List&lt;Task&gt;
+ saveTask(Task task) throws ??Exception
+ removeTask(Task task) throws ??Exception</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>442</x><y>440</y><w>170</w><h>80</h></coordinates><panel_attributes>MainWindowController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>962</x><y>1310</y><w>210</w><h>70</h></coordinates><panel_attributes>NotificationService
--
- taskDb: TaskDatabase
--
+ tick()</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1412</x><y>1310</y><w>240</w><h>90</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
WeatherProvider
--
+ getWeatherForecast: WeatherForecast</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1392</x><y>1150</y><w>400</w><h>100</h></coordinates><panel_attributes>WeatherService
--
- weatherPovider: WeatherProvider
- taskDb: TaskDatabase
--
+ WeatherService(provider: WeatherProvider, taskDb: TaskDatabase)
- updateTasks()</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1502</x><y>110</y><w>260</w><h>150</h></coordinates><panel_attributes>&lt;&lt;Record&gt;&gt;
GrowthPhase
--
+ startDate: MonthDay
+ endDate: MonthDay
+ type: GrowthPhaseType
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1422</x><y>180</y><w>100</w><h>40</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;&gt;
m1=*
m2=1</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>Text</id><coordinates><x>152</x><y>130</y><w>190</w><h>70</h></coordinates><panel_attributes>Note:
--
{final, readOnly} omitted on public data fields in &lt;&lt;Record&gt;&gt; Entities for clarity
style=wordwrap</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1172</x><y>970</y><w>210</w><h>50</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns &gt;</panel_attributes><additional_attributes>190;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>1032</x><y>820</y><w>140</w><h>130</h></coordinates><panel_attributes>lt=&lt;-
m1=1 external database
m2=1 internal list
accesses</panel_attributes><additional_attributes>10;110;10;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>322</x><y>1120</y><w>240</w><h>150</h></coordinates><panel_attributes>Crop
--
- cropId: Long
+ plantId: long {final, readOnly}
+ startDate: LocalDate {final, readOnly}
+ area: Double
--
+ withId(long): Crop
+ withArea(double): Crop
--
+ getCropId(): Optional&lt;Long&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1262</x><y>280</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns</panel_attributes><additional_attributes>10;10;10;100</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1632</x><y>290</y><w>130</w><h>100</h></coordinates><panel_attributes>&lt;&lt;Enumeration&gt;&gt;
GrowthPhaseType
--
SOW
PLANT
HARVEST</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1582</x><y>250</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;
m1=1
m2=*</panel_attributes><additional_attributes>50;90;10;90;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>882</x><y>720</y><w>80</w><h>30</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;</panel_attributes><additional_attributes>60;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>432</x><y>1020</y><w>110</w><h>120</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns / saves</panel_attributes><additional_attributes>10;100;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>242</x><y>710</y><w>100</w><h>30</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>302</x><y>890</y><w>280</w><h>140</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
GardenPlan
--
+ getPlantings(): List&lt;UserPlanting&gt;
+ addPlanting(plantId: long, startDate)
+ savePlanting(planting: UserPlanting)
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>422</x><y>800</y><w>80</w><h>110</h></coordinates><panel_attributes>lt=&lt;.
m1=1
m2=1
accesses</panel_attributes><additional_attributes>10;90;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1522</x><y>1240</y><w>30</w><h>90</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;70;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1062</x><y>1060</y><w>80</w><h>270</h></coordinates><panel_attributes>lt=&lt;-
m1=1
m2=1
accesses</panel_attributes><additional_attributes>10;10;10;250</additional_attributes></element><element><id>Relation</id><coordinates><x>1142</x><y>1060</y><w>270</w><h>170</h></coordinates><panel_attributes>lt=&lt;-
m1=1
m2=1
adds Tasks</panel_attributes><additional_attributes>10;10;10;140;250;140</additional_attributes></element><element><id>UMLClass</id><coordinates><x>822</x><y>1140</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonTaskDatabase</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>912</x><y>1060</y><w>30</w><h>100</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>10;10;10;80</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1562</x><y>430</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonPlantDatabase</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1442</x><y>440</y><w>140</w><h>30</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>10;10;120;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>32</x><y>930</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonGardenPlan</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>222</x><y>940</y><w>100</w><h>30</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>582</x><y>510</y><w>220</w><h>220</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>200;200;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>172</x><y>510</y><w>340</w><h>190</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;170;320;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>582</x><y>250</y><w>160</w><h>60</h></coordinates><panel_attributes>PlantListController</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>522</x><y>380</y><w>30</w><h>80</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;60;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>602</x><y>300</y><w>80</w><h>210</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>60;10;10;190</additional_attributes></element><element><id>UMLClass</id><coordinates><x>732</x><y>390</y><w>160</w><h>60</h></coordinates><panel_attributes>PlantListModel</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>732</x><y>280</y><w>90</w><h>130</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>70;110;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>882</x><y>400</y><w>220</w><h>70</h></coordinates><panel_attributes>lt=&lt;-
m1=1\nexternal\ndatabase
m2=1\ninternal\nlist
accesses &gt;</panel_attributes><additional_attributes>200;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>582</x><y>0</y><w>190</w><h>50</h></coordinates><panel_attributes>lt=-
m1=0..n
m2=0..1
teaches to &gt;</panel_attributes><additional_attributes>10;20;170;20</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1412</x><y>530</y><w>420</w><h>200</h></coordinates><panel_attributes>TaskTemplate
--
+ name: String {readOnly}
+ description: String {readOnly}
+ relativeStartDate: int {readOnly}
- interval: Integer
- relativeEndDate: Integer
- isOptional: boolean
--
+ Task(name: String, description: String, startDate: String): TaskTemplate
+ setRelativeEndDate(relativeEndDate: int)
+ setInterval(interval: Integer)
+ generateTask(realStartDate: LocalDate): Task
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1752</x><y>180</y><w>130</w><h>490</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;&gt;
m1=*
m2=1</panel_attributes><additional_attributes>80;460;110;460;110;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1602</x><y>720</y><w>90</w><h>130</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
generates</panel_attributes><additional_attributes>10;110;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>562</x><y>770</y><w>400</w><h>40</h></coordinates><panel_attributes>lt=&lt;-
delegates generating Tasks</panel_attributes><additional_attributes>380;20;10;20</additional_attributes></element></diagram>
@@ -0,0 +1,132 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Pest;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.List;
public class CropDetailController {
private Crop crop = null;
private final PlantListModel plantListModel = new PlantListModel();
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
@FXML
private ImageView imageView;
@FXML
private Button area_button;
@FXML
private Label area_label;
@FXML
private Label cropName_label;
@FXML
private Label description_label;
@FXML
private VBox growthPhases_vbox;
@FXML
private Label location_label;
@FXML
private Label light_label;
@FXML
private Button location_button;
@FXML
private VBox pests_vbox;
@FXML
private Label soil_label;
@FXML
private Label spacing_label;
public CropDetailController() throws IOException {
}
@FXML
void editTaskList(ActionEvent event) {
}
@FXML
void goBack(ActionEvent event) {
Stage stage = (Stage) imageView.getScene().getWindow();
stage.close();
}
@FXML
void setArea(ActionEvent event) {
}
@FXML
void setLocation(ActionEvent event) {
}
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException {
this.crop = crop;
Plant plant = plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0);
cropName_label.setText(plant.name());
description_label.setText(plant.description());
light_label.setText(String.valueOf(plant.light()));
soil_label.setText(plant.soil());
spacing_label.setText(plant.spacing());
if (plant.image() != null) {
imageView.setImage(plant.image());
}
area_label.setText("");
location_label.setText("");
createTaskLists(crop);
createPestList(plant);
}
private void createTaskLists(Crop crop) throws IOException {
List<Task> taskList = taskListModel.getTaskListForCrop(crop.getCropId().get());
for (Task task : taskList) {
Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label);
}
}
private void createPestList(Plant plant) {
List<Pest> pests = plant.pests();
for (Pest pest : pests) {
Label label = new Label(pest.name() + ":");
label.setStyle("-fx-font-weight: bold");
HBox hBox = new HBox();
hBox.fillHeightProperty();
Label label1 = new Label(pest.description());
label1.setAlignment(Pos.TOP_LEFT);
label1.setWrapText(true);
label1.setMaxWidth(600);
label1.setMaxHeight(100);
Button button = new Button("Get Counter Measures");
hBox.getChildren().addAll(label1, button);
pests_vbox.getChildren().addAll(label, hBox);
}
}
}
@@ -4,16 +4,24 @@ 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.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MainFXMLController implements Initializable {
/**
* Caching the panes
*/
private final Map<String, AnchorPane> panes = new HashMap<>();
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
@FXML
private Button home_button;
@@ -62,15 +70,22 @@ public class MainFXMLController implements Initializable {
* @throws IOException exception when file does not exist
*/
public void loadPane(String fxmlFile) throws IOException {
//ToDo HGrow and VGrow of new node
Node node;
AnchorPane anchorPane = panes.get(fxmlFile);
if (anchorPane == null) {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
node = (Node)loader.load();
anchorPane = loader.load();
panes.put(fxmlFile, anchorPane);
if(fxmlFile.equals("MyPlants.fxml")) {
MyPlantsController myPlantsController = loader.getController();
myPlantsController.getMainController(this);
}
mainPane.getChildren().setAll(node);
}
mainPane.getChildren().setAll(anchorPane);
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
}
private void styleChangeButton(Button button) {
@@ -87,7 +102,7 @@ public class MainFXMLController implements Initializable {
loadPane("Home.fxml");
styleChangeButton(home_button);
} catch (IOException e) {
e.printStackTrace();
LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);
}
}
}
@@ -1,27 +1,45 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.*;
public class MyPlantsController implements Initializable {
MainFXMLController mainController;
@FXML
private Button addPlant_button;
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
private final PlantListModel plantListModel = new PlantListModel();
@FXML
private VBox myPlants_vbox;
public MyPlantsController() throws IOException {
}
@FXML
void addPlant(ActionEvent event) throws IOException {
mainController.loadPane("Plants.fxml");
@@ -29,15 +47,29 @@ public class MyPlantsController implements Initializable {
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//ToDo
List<Plant> myPlants = getMyPlants();
createPlantView(myPlants);
//ToDo update, when new crops are added
try {
loadCropList();
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
private void createPlantView(List<Plant> myPlants) {
//ToDo
for(Plant plant : myPlants) {
createPlantView();
private void loadCropList() throws HardinessZoneNotSetException, IOException {
List<Crop> cropList = new LinkedList<>();
try {
cropList = getCropList();
} catch (IOException e) {
e.printStackTrace();
}
createPlantView(cropList);
}
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
myPlants_vbox.getChildren().clear();
for(Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
@@ -45,14 +77,84 @@ public class MyPlantsController implements Initializable {
mainController = controller;
}
private List<Plant> getMyPlants() {
//ToDo method to get myPlantList(scheduled)
//Method to call all Plants saved
List<Plant> myPlantList = new LinkedList<>();
return myPlantList;
private List<Crop> getCropList() throws IOException {
List<Crop> cropList;
cropList = gardenplanmodel.getCrops();
return cropList;
}
private void createPlantView() {
//ToDo FXML Panel with Plant data
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0);
HBox hBox = new HBox(10);
ImageView imageView = new ImageView();
imageView.setPreserveRatio(false);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
imageView.maxHeight(100);
if (plant.image() != null) {
imageView.setImage(plant.image());
}
hBox.setMinHeight(100);
Label label = new Label(plant.name());
label.setMaxWidth(2000);
HBox.setHgrow(label, Priority.ALWAYS);
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(imageView, label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Parent root;
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("CropDetail.fxml")));
try {
root = fxmlLoader.load();
CropDetailController controller = fxmlLoader.getController();
controller.setPlantFromCrop(crop);
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
}
};
return event;
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
}
};
return event;
}
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Delete Crop");
alert.setHeaderText("Are you sure want to delete this Crop?");
alert.setContentText("placeholder");
Optional<ButtonType> option = alert.showAndWait();
if (option.get() == ButtonType.OK) {
gardenplanmodel.removeCrop(crop);
loadCropList();
}
}
}
@@ -1,15 +1,25 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
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 javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDate;
import java.util.LinkedList;
@@ -17,7 +27,12 @@ import java.util.List;
import java.util.ResourceBundle;
public class MyScheduleController implements Initializable {
private Plant selectedPlant = null;
private Crop selectedCrop = null;
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
private final PlantListModel plantListModel = new PlantListModel();
private final ListProperty<Crop> cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML
private Label day1_label;
@@ -65,27 +80,41 @@ public class MyScheduleController implements Initializable {
private Label information_label;
@FXML
private ListView<Plant> scheduledPlants_listview;
private ListView<Crop> scheduledPlants_listview;
public MyScheduleController() throws IOException {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
List<Plant> plantList = new LinkedList<>();
fillListViewMyPlantsInSchedule(plantList);
getSelectedPlantTask();
List<Crop> cropList;
try {
cropList = gardenplanmodel.getCrops();
cropListProperty.addAll(cropList);
} catch (IOException e) {
e.printStackTrace();
}
setCellFactoryListView();
scheduledPlants_listview.itemsProperty().bind(cropListProperty);
lookForSelectedListEntries();
setDayLabels();
information_label.setText("");
try {
loadTaskList();
} catch (IOException e) {
e.printStackTrace();
}
}
private void getSelectedPlantTask() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() {
private void lookForSelectedListEntries() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Crop>() {
@Override
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) {
if(newValue != null) {
selectedPlant = newValue;
//ToDo update day<x>_panel with task for the day
} else {
selectedPlant = null;
//ToDo update day<x>_panel with task for the day (all plants)
public void changed(ObservableValue<? extends Crop> observable, Crop oldValue, Crop newValue) {
selectedCrop = newValue;
try {
loadTaskList();
} catch (IOException e) {
e.printStackTrace();
}
}
});
@@ -102,23 +131,52 @@ public class MyScheduleController implements Initializable {
day7_label.setText(today.plusDays(6).getDayOfWeek().toString());
}
private void fillListViewMyPlantsInSchedule(List<Plant> list) {
for (Plant plant : list) {
scheduledPlants_listview.getItems().add(plant);
}
scheduledPlants_listview.setCellFactory(param -> new ListCell<Plant>() {
private void setCellFactoryListView() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<Crop>() {
@Override
protected void updateItem(Plant plant, boolean empty) {
super.updateItem(plant, empty);
protected void updateItem(Crop crop, boolean empty) {
super.updateItem(crop, empty);
if (empty || plant == null || plant.name() == null) {
if (empty || crop == null) {
setText(null);
} else {
setText(plant.name());
try {
setText(plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0).name());
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
}
});
}
private void loadTaskList() throws IOException {
List<List<Task>> taskLists = new LinkedList<>();
if (selectedCrop != null) {
taskLists = taskListModel.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
} else {
taskLists = taskListModel.getTasksUpcomingWeek();
}
if (!taskLists.isEmpty()) {
viewTaskListOfDay(day1_pane, taskLists.get(0));
viewTaskListOfDay(day2_pane, taskLists.get(1));
viewTaskListOfDay(day3_pane, taskLists.get(2));
viewTaskListOfDay(day4_pane, taskLists.get(3));
viewTaskListOfDay(day5_pane, taskLists.get(4));
viewTaskListOfDay(day6_pane, taskLists.get(5));
viewTaskListOfDay(day7_pane, taskLists.get(6));
}
}
private void viewTaskListOfDay(Pane pane, List<Task> tasks) {
//ToDo update pane with task list
VBox vBox = new VBox();
for (Task task : tasks) {
Label label = new Label(task.getDescription());
vBox.getChildren().add(label);
}
pane.getChildren().add(vBox);
}
}
@@ -4,29 +4,43 @@ 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 ch.zhaw.gartenverwaltung.types.Seasons;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PlantsController implements Initializable {
private static final Logger LOG = Logger.getLogger(PlantsController.class.getName());
private final PlantListModel plantListModel = new PlantListModel();
private Plant selectedPlant = null;
private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;
// TODO: move to model
private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML
private CheckBox autum_filter;
private VBox seasons;
@FXML
private VBox climate_zones;
@@ -41,85 +55,62 @@ public class PlantsController implements Initializable {
private ListView<Plant> list_plants;
@FXML
private Button saveToMyPlant_button;
private Button selectSowDay_button;
@FXML
private TextField search_plants;
/**
* open new window to select sow or harvest day to save the crop
* @param event event
*/
@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());
void selectSowDate(ActionEvent event) throws IOException {
Parent root;
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("SelectSowDay.fxml")));
root = fxmlLoader.load();
SelectSowDayController controller = fxmlLoader.getController();
controller.getSelectedPlant(selectedPlant);
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(false);
stage.showAndWait();
}
/**
* fill list view with current hardiness zone
* set default values
* create filter of season and hardiness zone
* create event listener for selected list entry and search by query
* {@inheritDoc}
*/
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
List<Plant> plantList = new LinkedList<>();
try {
plantList = plantListModel.getPlantList(DEFAULT_HARDINESS_ZONE);
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
fillListViewWithData(plantList);
setListCellFactory();
fillPlantListWithHardinessZone();
list_plants.itemsProperty().bind(plantListProperty);
description_plant.setText("");
saveToMyPlant_button.setDisable(true);
selectSowDay_button.setDisable(true);
createFilterSeasons();
createFilterHardinessZone();
lookForSelectedListEntry();
try {
viewFilteredListBySearch();
} catch (HardinessZoneNotSetException e) {
LOG.log(Level.WARNING, "Hardiness Zone not set!");
} catch (IOException e) {
LOG.log(Level.WARNING, "Could not retrieve data!", e);
}
}
/**
* update the ListView according to the plant list provided
* Entry in ListView is plant name
* @param list plantList which fill the ListView
* set text of list view to plant name
*/
private void fillListViewWithData(List<Plant> list) {
clearListView();
for (Plant plant : list) {
list_plants.getItems().add(plant);
}
list_plants.setCellFactory(param -> new ListCell<Plant>() {
private void setListCellFactory() {
list_plants.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(Plant plant, boolean empty) {
super.updateItem(plant, empty);
@@ -133,45 +124,140 @@ public class PlantsController implements Initializable {
});
}
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, <predicate>))
//List<Plant> plantList = new LinkedList<>();
//fillListViewWithData(plantList);
}
private void viewFilteredListBySearch(String query) {
//ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, <predicate>))
//List<Plant> plantList = new LinkedList<>();
//fillListViewWithData(plantList);
}
private void createFilterHardinessZone() {
//ToDo create radioList of hardinessZone in VBox climate_zones
/**
* get plant list according to param season and hardiness zone
* fill list view with plant list
* @param season enum of seasons
* @throws HardinessZoneNotSetException throws exception
* @throws IOException throws exception
*/
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
clearListView();
plantListProperty.addAll(plantListModel.getFilteredPlantListBySaisonWithoutGrowthPhase(plantListModel.getCurrentZone(), season.getStartDate(), season.getEndDate()));
}
/**
* observes changes in the selectedProperty of ListView and updates the description label
* get plant list filtered by search plant entry and hardiness zone
* fill list view with plant list
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
* @throws IOException throws exception
*/
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.isEmpty()) {
fillPlantListWithHardinessZone();
} else {
try {
List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
clearListView();
plantListProperty.addAll(filteredPlants);
} catch (HardinessZoneNotSetException e) {
LOG.log(Level.WARNING, "Hardiness Zone not set!");
} catch (IOException e) {
LOG.log(Level.WARNING, "Could not retrieve data!", e);
}
}
});
}
/**
* get plant list of current hardiness zone
* fill list view with plant list
*/
private void fillPlantListWithHardinessZone() {
try {
clearListView();
plantListProperty.addAll(plantListModel.getPlantList(plantListModel.getCurrentZone()));
} catch (HardinessZoneNotSetException e) {
LOG.log(Level.WARNING, "Hardiness Zone not set!");
} catch (IOException e) {
LOG.log(Level.WARNING, "Could not retrieve data!", e);
}
}
/**
* creates radio buttons for the hardiness zones defined in enum HardinessZone
* defines default value as selected
* when selected filter viewList according to hardiness zone
*/
private void createFilterHardinessZone() {
ToggleGroup hardinessGroup = new ToggleGroup();
for (HardinessZone zone : HardinessZone.values()) {
RadioButton radioButton = new RadioButton(zone.name());
radioButton.setToggleGroup(hardinessGroup);
radioButton.setPadding(new Insets(0,0,10,0));
if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
radioButton.setSelected(true);
}
radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
plantListModel.setCurrentZone(zone);
fillPlantListWithHardinessZone();
});
climate_zones.getChildren().add(radioButton);
}
}
/**
* creates radio buttons for the seasons defined in enum Seasons
* defines default value as selected
* when selected filter viewList according to seasons
*/
private void createFilterSeasons() {
ToggleGroup seasonGroup = new ToggleGroup();
for (Seasons season : Seasons.values()) {
RadioButton radioButton = new RadioButton(season.getName());
radioButton.setToggleGroup(seasonGroup);
radioButton.setPadding(new Insets(0,0,10,0));
if (season.equals(Seasons.AllSEASONS)) {
radioButton.setSelected(true);
}
radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (season.equals(Seasons.AllSEASONS)) {
fillPlantListWithHardinessZone();
} else {
try {
viewFilteredListBySeason(season);
} catch (HardinessZoneNotSetException e) {
LOG.log(Level.WARNING, "Hardiness Zone not set!");
} catch (IOException e) {
LOG.log(Level.WARNING, "Could not retrieve data!", e);
}
}
});
seasons.getChildren().add(radioButton);
}
}
/**
* observes changes in the selectedProperty of ListView and updates:
* the description label
* image of the plant
*/
private void lookForSelectedListEntry() {
list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() {
@Override
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) {
//ToDo
selectedPlant = null;
description_plant.setText("");
selectSowDay_button.setDisable(true);
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
img_plant.setImage(img);
list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null) {
selectedPlant = newValue;
description_plant.setText(selectedPlant.description());
saveToMyPlant_button.setDisable(false);
//update img plant
selectSowDay_button.setDisable(false);
Image img1;
if(selectedPlant.image() != null) {
img1 = selectedPlant.image();
} else {
img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
}
img_plant.setImage(img1);
} else {
selectedPlant = null;
description_plant.setText("");
saveToMyPlant_button.setDisable(true);
//update img when null placeholder PNG
}
selectSowDay_button.setDisable(true);
Image img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
img_plant.setImage(img1);
}
});
}
@@ -181,6 +267,6 @@ public class PlantsController implements Initializable {
* clears the ListView of entries
*/
private void clearListView() {
list_plants.getItems().clear();
plantListProperty.clear();
}
}
@@ -0,0 +1,174 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
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.stage.Stage;
import javafx.util.Callback;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDate;
import java.util.List;
import java.util.ResourceBundle;
public class SelectSowDayController implements Initializable {
private Plant selectedPlant = null;
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
@FXML
private DatePicker datepicker;
@FXML
private Label popup_label;
@FXML
private Button save_button;
@FXML
private RadioButton sow_radio;
public SelectSowDayController() throws IOException {}
/**
* close the date selector window
* @param event event
*/
@FXML
void cancel(ActionEvent event) {
closeWindow();
}
/**
* get sow date from datePicker or calculate sow date from harvest date
* save selected plant and sow date
* @param event event
*/
@FXML
void save(ActionEvent event) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
LocalDate sowDate;
if (sow_radio.isSelected()) {
sowDate = datepicker.getValue();
} else {
//ToDo method to get current lifecycle group in plant
sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0);
}
gardenplanmodel.plantAsCrop(selectedPlant, sowDate);
closeWindow();
}
/**
* save the plant which will be planted and update label
* @param plant Plant
*/
public void getSelectedPlant(Plant plant) {
selectedPlant = plant;
popup_label.setText("Select Harvest/Sow Date for" + selectedPlant.name());
}
/**
* add listener and set default values
* {@inheritDoc}
* @param location location
* @param resources resources
*/
@Override
public void initialize(URL location, ResourceBundle resources) {
clearDatePickerEntries();
Callback<DatePicker, DateCell> dayCellFactory= getDayCellFactory();
datepicker.setDayCellFactory(dayCellFactory);
datepicker.getEditor().setEditable(false);
enableDisableSaveButton();
}
/**
* clear date picker editor when radio button is changed
*/
private void clearDatePickerEntries() {
sow_radio.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean isNowSelected) {
datepicker.getEditor().clear();
}
});
}
/**
* date picker disable/enable dates according to selected plant: sow or harvest day
* @return cellFactory of datePicker
*/
private Callback<DatePicker, DateCell> getDayCellFactory() {
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
@Override
public DateCell call(final DatePicker datePicker) {
return new DateCell() {
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
List<LocalDate> dates;
LocalDate today = LocalDate.now();
if (sow_radio.isSelected()) {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
} else {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST);
}
for (LocalDate date : dates) {
if (item.getMonth() == date.getMonth()
&& item.getDayOfMonth() == date.getDayOfMonth()
&& item.compareTo(today) > 0) {
setDisable(false);
setStyle("-fx-background-color: #32CD32;");
}
}
if ((!sow_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
}
}
};
}
};
return dayCellFactory;
}
/**
* close date picker window
*/
private void closeWindow() {
Stage stage = (Stage) save_button.getScene().getWindow();
stage.close();
}
/**
* disable save button, when there is no date selected in date picker
*/
private void enableDisableSaveButton() {
save_button.setDisable(true);
datepicker.getEditor().textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue == null || newValue.equals("")) {
save_button.setDisable(true);
} else {
save_button.setDisable(false);
}
}
});
}
}
@@ -1,8 +1,92 @@
package ch.zhaw.gartenverwaltung.gardenplan;
import ch.zhaw.gartenverwaltung.io.GardenPlan;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonGardenPlan;
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
/**
* The Gardenplan model manages the crops in the gardenplan.
*/
public class Gardenplanmodel {
// private JasonGardenplan gardenplan;
// liste von crops
//task generieren
//plant holen, task template mit tasktemplateklasse tasks erstellen
private GardenPlan gardenPlan;
private List<Crop> cropList;
private TaskListModel taskListModel;
private Object IllegalArgumentException;
/**
* Constructor of Gardenplan model
*
* @param taskListModel holds a reference to the task list object.
*/
public Gardenplanmodel(TaskListModel taskListModel) throws IOException {
this.taskListModel = taskListModel;
gardenPlan = new JsonGardenPlan();
cropList = new ArrayList<>();
cropList = gardenPlan.getCrops();
}
/**
* Creates a Crop with a {@link Plant} and the planting date of the plant. Then let the Tasklistmodel create the
* gardening {@link Task} for the crop. Store the crop in the gardenplan file and the cache.
*
* @param plant The plant which is wnated to be planted
* @param plantingDate The date, when the plant is planted
* @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If the hardinesszone could not be added
* @throws PlantNotFoundException If the plant is not found in the database.
*/
public void plantAsCrop(Plant plant, LocalDate plantingDate) throws IOException, HardinessZoneNotSetException, PlantNotFoundException {
Crop crop = new Crop(plant.id(), plantingDate);
//TODO Add Area to Plant
//crop.withArea(0);
gardenPlan.saveCrop(crop);
taskListModel.planTasksForCrop(crop);
cropList = gardenPlan.getCrops();
}
/**
* Removes a {@link Crop} from the file and the cache
*
* @param crop The plant which is wnated to be planted
* @throws IOException If the database cannot be accessed
*/
public void removeCrop(Crop crop) throws IOException {
gardenPlan.removeCrop(crop);
taskListModel.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new));
cropList = gardenPlan.getCrops();
}
/**
* Returns a list of {@link Crop}s which are currently in the gardenplan.
*
* @throws IOException If the database cannot be accessed
*/
public List<Crop> getCrops() throws IOException {
if(!cropList.isEmpty()){
cropList = gardenPlan.getCrops();
}
return cropList;
}
/**
* Returns an Optional of {@link Crop} if the crop is the gardenplan
*
* @param cropId The date, when the plant is planted
* @throws IOException If the database cannot be accessed
*/
public Optional<Crop> getCrop(Long cropId) throws IOException {
return gardenPlan.getCropById(cropId);
}
}
@@ -18,11 +18,11 @@ public interface GardenPlan {
/**
* Attempts to retrieve the {@link Crop} with the specified cropId.
*
* @param id The {@link Crop#cropId} to look for
* @param cropId The {@link Crop} 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<Crop> getCropById(long id) throws IOException;
Optional<Crop> getCropById(long cropId) throws IOException;
/**
* Saves a Crop to the Database.
@@ -20,7 +20,7 @@ import java.util.Map;
import java.util.Optional;
public class JsonGardenPlan implements GardenPlan {
private final URL dataSource = getClass().getResource("user-crops.json");
private final URL dataSource;
private IdProvider idProvider;
private Map<Long, Crop> cropMap = Collections.emptyMap();
@@ -40,7 +40,21 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#getCrops()
* Default constructor
*/
public JsonGardenPlan() {
this.dataSource = getClass().getResource("user-crops.json");
}
/**
* Constructor to use a specified {@link URL} as a {@link #dataSource}
* @param dataSource A {@link URL} to the file to be used as a data source
*/
public JsonGardenPlan(URL dataSource) {
this.dataSource = dataSource;
}
/**
* {@inheritDoc}
*/
@Override
public List<Crop> getCrops() throws IOException {
@@ -51,7 +65,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#getCropById(long)
* {@inheritDoc}
*/
@Override
public Optional<Crop> getCropById(long id) throws IOException {
@@ -62,7 +76,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#saveCrop(Crop)
* {@inheritDoc}
*
* Saves a crop to the database.
* If no {@link Crop#cropId} is set, one will be generated and
@@ -79,7 +93,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#removeCrop(Crop)
* {@inheritDoc}
*/
@Override
public void removeCrop(Crop crop) throws IOException {
@@ -131,7 +145,6 @@ public class JsonGardenPlan implements GardenPlan {
.registerModule(new Jdk8Module())
.writeValue(new File(dataSource.toURI()), cropMap.values());
} catch (URISyntaxException e) {
// TODO: Log
throw new IOException(INVALID_DATASOURCE_MSG, e);
}
}
@@ -89,12 +89,13 @@ public class JsonPlantDatabase implements PlantDatabase {
List<Plant> result;
result = mapper.readerForListOf(Plant.class).readValue(dataSource);
for (Plant plant : result) {
plant.inZone(currentZone);
}
// Turn list into a HashMap with structure id => Plant
plantMap = result.stream()
// Remove plants not in the current zone
.filter(plant -> {
plant.inZone(currentZone);
return !plant.lifecycle().isEmpty();
})
// Create Hashmap from results
.collect(HashMap::new,
(res, plant) -> res.put(plant.id(), plant),
(existing, replacement) -> {});
@@ -6,9 +6,11 @@ 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;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@@ -25,6 +27,7 @@ import java.util.Map;
public class JsonTaskDatabase implements TaskDatabase{
IdProvider idProvider;
private final URL dataSource = getClass().getResource("taskdb.json");
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
private Map<Long, Task> taskMap = Collections.emptyMap();
@@ -35,7 +38,10 @@ public class JsonTaskDatabase implements TaskDatabase{
static {
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);
}
/**
@@ -121,12 +127,16 @@ public class JsonTaskDatabase implements TaskDatabase{
* @throws IOException If the database cannot be accessed
*/
private void writeTaskListToFile() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule)
.registerModule(new Jdk8Module());
if(dataSource != null) {
mapper.writeValue(new File(dataSource.getFile()), taskMap);
try {
new ObjectMapper()
.registerModule(timeModule)
.registerModule(new Jdk8Module())
.writeValue(new File(dataSource.toURI()), taskMap.values());
} catch (URISyntaxException e) {
throw new IOException(INVALID_DATASOURCE_MSG, e);
}
}
}
@@ -138,7 +148,8 @@ public class JsonTaskDatabase implements TaskDatabase{
private void loadTaskListFromFile() throws IOException {
if (dataSource != null) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule);
mapper.registerModule(timeModule)
.registerModule(new Jdk8Module());
List<Task> result;
result = mapper.readerForListOf(Task.class).readValue(dataSource);
@@ -148,5 +159,8 @@ public class JsonTaskDatabase implements TaskDatabase{
(res, task) -> res.put(task.getId(), task),
(existing, replacement) -> {});
}
Long maxId = taskMap.isEmpty() ? 0L : Collections.max(taskMap.keySet());
idProvider = new IdProvider(maxId);
}
}
@@ -16,13 +16,12 @@ public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType
@Override
public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException {
GrowthPhaseType result = null;
GrowthPhaseType result;
String token = parser.getText();
try {
result = GrowthPhaseType.valueOf(token.toUpperCase());
} catch (IllegalArgumentException e) {
// TODO: Log
System.err.printf("Bad growth phase type \"%s\"\n", token);
throw new IOException(String.format("Bad growth phase type \"%s\"\n", token));
}
return result;
}
@@ -17,13 +17,12 @@ public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
@Override
public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException {
HardinessZone result = null;
HardinessZone result;
String token = parser.getText();
try {
result = HardinessZone.valueOf(token.toUpperCase());
} catch (IllegalArgumentException e) {
// TODO: Log
System.err.printf("Unknown Hardiness Zone \"%s\"\n", token);
throw new IOException(String.format("Unknown Hardiness Zone \"%s\"\n", token), e);
}
return result;
}
@@ -6,9 +6,11 @@ import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import javafx.scene.image.Image;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
public class PlantImageDeserializer extends JsonDeserializer<Image> {
@@ -18,12 +20,10 @@ public class PlantImageDeserializer extends JsonDeserializer<Image> {
Image result = null;
URL imageUrl = PlantDatabase.class.getResource(String.format("images/%s", parser.getText()));
if (imageUrl != null) {
try (InputStream is = new FileInputStream(imageUrl.getFile())) {
try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) {
result = new Image(is);
} catch (IllegalArgumentException e) {
// TODO: Log
e.printStackTrace();
System.err.printf("Cannot find Image \"%s\"\n", imageUrl.getFile());
} catch (IllegalArgumentException | URISyntaxException e) {
throw new IOException(String.format("Cannot find Image \"%s\"\n", imageUrl.getFile()));
}
}
return result;
@@ -52,6 +52,7 @@ public class PlantListModel {
/**
* Method to get actual Plant List in alphabetic Order
*
* @return actual Plant List in alphabetic Order
*/
public List<Plant> getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException {
@@ -62,6 +63,7 @@ public class PlantListModel {
/**
* 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
@@ -75,6 +77,7 @@ public class PlantListModel {
/**
* 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
@@ -88,6 +91,7 @@ public class PlantListModel {
/**
* 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.
@@ -102,7 +106,6 @@ public class PlantListModel {
}
/**
*
* @param zone selected hardiness zone
* @param searchString the string to search plant List, set '#' as first char the search by id.
* @return List of plants found in Plant List which contain the search String in the name or description
@@ -110,19 +113,26 @@ public class PlantListModel {
* @throws IOException If the database cannot be accessed
*/
public List<Plant> getFilteredPlantListByString(HardinessZone zone, String searchString) throws HardinessZoneNotSetException, IOException {
if(searchString.charAt(0) == '#') {
try {
return getFilteredPlantListById(zone, Long.parseLong(searchString.substring(1)));
} catch (NumberFormatException e) {
if (searchString.length() == 0) {
return getPlantList(zone);
} else if (searchString.charAt(0) == '#') {
if (isPositiveIntegral(searchString.substring(1))) {
Long searchId = Long.parseLong(searchString.substring(1));
return getFilteredPlantListById(zone, searchId);
} else {
return new ArrayList<>();
}
} else {
return getFilteredPlantList(zone, plant -> plant.name().contains(searchString) || plant.description().contains(searchString));
String caseInsensitiveSearchString = searchString.toLowerCase();
return getFilteredPlantList(zone, plant ->
plant.name().toLowerCase().contains(caseInsensitiveSearchString) ||
plant.description().toLowerCase().contains(caseInsensitiveSearchString)
);
}
}
/**
*
* @param type GrowPhaseType to filter
* @param zone selected hardiness zone
* @param from the earliest date to for the filter
@@ -136,7 +146,6 @@ public class PlantListModel {
}
/**
*
* @param zone selected hardiness zone
* @param from the earliest date to for the filter
* @param to the lastest date for the filter
@@ -149,7 +158,6 @@ public class PlantListModel {
}
/**
*
* @param zone selected hardiness zone
* @param from the earliest date to for the filter
* @param to the lastest date for the filter
@@ -160,4 +168,26 @@ public class PlantListModel {
public List<Plant> getFilteredPlantListByHarvestSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
return getFilteredPlantListBySaison(GrowthPhaseType.HARVEST, zone, from, to);
}
/**
* @param zone selected hardiness zone
* @param from the earliest date to for the filter
* @param to the lastest date for the filter
* @return List of Plants with selected saison
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
* @throws IOException If the database cannot be accessed
*/
public List<Plant> getFilteredPlantListBySaisonWithoutGrowthPhase(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
return getFilteredPlantList(zone, plant -> plant.lifecycle().stream().anyMatch(growthPhase -> growthPhase.startDate().compareTo(from) >= 0 && (growthPhase.startDate().compareTo(to) <= 0)));
}
/**
* Check if a string can safely be parsed as a positive Integral value (short/int/long)
*
* @param subject The string to be tested
* @return Whether the string contains only digits
*/
private boolean isPositiveIntegral(String subject) {
return subject != null && subject.matches("[0-9]+");
}
}
@@ -1,10 +1,7 @@
package ch.zhaw.gartenverwaltung.taskList;
import ch.zhaw.gartenverwaltung.Config;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonTaskDatabase;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.io.TaskDatabase;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.*;
import java.io.IOException;
@@ -12,6 +9,7 @@ import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class TaskListModel {
@@ -25,6 +23,7 @@ public class TaskListModel {
public TaskListModel(){
taskDatabase = new JsonTaskDatabase();
plantDatabase = new JsonPlantDatabase();
}
/**
@@ -78,6 +77,10 @@ public class TaskListModel {
taskDatabase.removeTask(task);
}
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
return taskList.stream().filter(task -> task.getCropId() == cropId).collect(Collectors.toList());
}
/**
* Method to get all Tasks
* @return List of all Tasks
@@ -87,6 +90,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.MIN, LocalDate.MAX);
}
/**
* Method to get all Tasks for specific Crop
* @return List of all Tasks for with given CropID
* @throws IOException If the database cannot be accessed
*/
public List<Task> getTaskListForCrop(Long cropId) throws IOException {
return filterListByCrop(getTaskList(), cropId);
}
/**
* Method to get all Tasks which are today or in future
* @return List of Tasks
@@ -96,6 +108,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.now(), LocalDate.MAX);
}
/**
* Method to get all Tasks which are today or in future for specific Crop
* @return List of Tasks with given crop ID
* @throws IOException If the database cannot be accessed
*/
public List<Task> getFutureTasksForCrop(Long cropId) throws IOException {
return filterListByCrop(getFutureTasks(), cropId);
}
/**
* Method to get all Tasks which are today or in past
* @return List of Tasks
@@ -105,6 +126,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.MIN, LocalDate.now());
}
/**
* Method to get all Tasks which are today or in past for specifc crop
* @return List of Tasks with given grop id
* @throws IOException If the database cannot be accessed
*/
public List<Task> getPastTasksForCrop(Long cropId) throws IOException {
return filterListByCrop(getPastTasks(), cropId);
}
/**
* Method to get an List of 7 Tasklists for the next 7 days. Index 0 is Tasklist for Today.
* @return List with length 7 (List<List<Task>>)
@@ -119,6 +149,20 @@ public class TaskListModel {
return dayTaskList;
}
/**
* Method to get an List of 7 Tasklists for the next 7 days. (Filtered Index 0 is Tasklist for Today.
* @return List with length 7 (List<List<Task>>)
* @throws IOException If the database cannot be accessed
*/
public List<List<Task>> getTasksUpcomingWeekForCrop(Long cropId) throws IOException {
List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i);
dayTaskList.add(filterListByCrop(taskDatabase.getTaskList(date, date), cropId));
}
return dayTaskList;
}
/**
* Method to get Tasklist filtered by date.
* @param start the start date for the filter
@@ -1,6 +1,7 @@
package ch.zhaw.gartenverwaltung.types;
import java.time.LocalDate;
import java.util.Objects;
import java.util.Optional;
public class Crop {
@@ -41,6 +42,24 @@ public class Crop {
public LocalDate getStartDate() { return startDate; }
public double getArea() { return area; }
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other instanceof Crop otherCrop) {
return Objects.equals(this.cropId, otherCrop.cropId) &&
plantId == otherCrop.plantId &&
startDate != null && startDate.equals(otherCrop.startDate) &&
area == otherCrop.area;
}
return false;
}
@Override
public int hashCode() {
int startCode = startDate != null ? startDate.hashCode() : 0;
return (int) plantId ^ (startCode << 16);
}
@Override
public String toString() {
return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]",
@@ -3,6 +3,7 @@ package ch.zhaw.gartenverwaltung.types;
import javafx.scene.image.Image;
import java.time.LocalDate;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@@ -19,20 +20,40 @@ public record Plant(
List<Pest> pests,
List<GrowthPhase> lifecycle) {
/**
* remove all growthPhase which do not belong to the hardiness zone
* @param zone hardiness zone
*/
public void inZone(HardinessZone zone) {
lifecycle.removeIf(growthPhase -> !growthPhase.zone().equals(zone));
}
/**
* get all growthPhases of lifecycle group
* @param group lifecycle group
* @return list of growthPhases
*/
public List<GrowthPhase> lifecycleForGroup(int group) {
return lifecycle.stream()
.filter(growthPhase -> growthPhase.group() != group)
.filter(growthPhase -> growthPhase.group() == group)
.collect(Collectors.toList());
}
/**
* get sow date from given harvest day from lifecycle group
* @param harvestDate date of the harvest
* @param group lifecycle group
* @return LocaleDate of sow date
*/
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) {
return harvestDate.minusDays(timeToHarvest(group));
}
/**
* calculate the days between sow and harvest day for lifecycle group
* @param group the lifecycle group
* @return Integer number of dates between sow and harvest day
*/
public int timeToHarvest(int group) {
List<GrowthPhase> activeLifecycle = lifecycleForGroup(group);
GrowthPhase sow = activeLifecycle.stream()
@@ -47,4 +68,38 @@ public record Plant(
int currentYear = LocalDate.now().getYear();
return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear));
}
/**
* filter out the given growthPhase out of the lifecycle
* create list of dates for this growthPhase and return it
* @param growthPhase the wanted growthPhase
* @return a list of dates of the current year
*/
public List<LocalDate> getDateListOfGrowthPhase(GrowthPhaseType growthPhase) {
List<LocalDate> dates = new LinkedList<>();
for (GrowthPhase growth : lifecycle) {
if (growth.type().equals(growthPhase)) {
dates = addDatesFromMonthDay(growth);
}
}
return dates;
}
/**
* transform monthDay value of the given growthPhase to localDate
* return a list of dates from start to end of growth phase
* @param growthPhase the current growthPhase
* @return a list of dates of the current year
*/
private List<LocalDate> addDatesFromMonthDay(GrowthPhase growthPhase) {
List<LocalDate> dates = new LinkedList<>();
LocalDate today = LocalDate.now();
LocalDate start = growthPhase.startDate().atYear(today.getYear());
LocalDate end = growthPhase.endDate().atYear(today.getYear());
while (!start.isAfter(end)) {
dates.add(start);
start = start.plusDays(1);
}
return dates;
}
}
@@ -0,0 +1,31 @@
package ch.zhaw.gartenverwaltung.types;
import java.time.MonthDay;
public enum Seasons {
AllSEASONS("--01-01", "--12-31", "All Seasons"),
SPRING("--03-01", "--05-30", "Spring"),
SUMMER("--06-01", "--08-30", "Summer"),
AUTUMN("--09-01", "--11-30", "Autumn"),
WINTER("--12-01", "--02-28", "Winter");
public final String startDate;
public final String endDate;
public final String name;
Seasons(String startDate, String endDate, String name) {
this.startDate = startDate;
this.endDate = endDate;
this.name = name;
}
public MonthDay getStartDate() {
return MonthDay.parse(this.startDate);
}
public MonthDay getEndDate() {
return MonthDay.parse(this.endDate);
}
public String getName() {
return this.name;
}
}
@@ -21,11 +21,10 @@ public class Task {
* default constructor
* (used by Json deserializer)
*/
public Task(long cropId){
public Task(){
name= "";
description= "";
startDate = LocalDate.now();
this.cropId = cropId;
}
public Task(String name, String description, LocalDate startDate, long cropId) {
+1
View File
@@ -4,6 +4,7 @@ module ch.zhaw.gartenverwaltung {
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310;
requires com.fasterxml.jackson.datatype.jdk8;
requires java.logging;
opens ch.zhaw.gartenverwaltung to javafx.fxml;
opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind;
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="785.0" prefWidth="899.0"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.CropDetailController">
<children>
<ScrollPane fitToWidth="true" prefHeight="759.0" prefWidth="664.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<content>
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="503.0" prefWidth="897.0">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
<children>
<Button mnemonicParsing="false" onAction="#goBack" prefHeight="25.0" prefWidth="91.0" text="Go Back">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Button>
<Label fx:id="cropName_label" text="Label">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Label>
<HBox prefHeight="265.0" prefWidth="879.0">
<children>
<GridPane maxWidth="1.7976931348623157E308" prefHeight="296.0" prefWidth="577.0" HBox.hgrow="ALWAYS">
<columnConstraints>
<ColumnConstraints halignment="LEFT" hgrow="SOMETIMES" maxWidth="284.0" minWidth="10.0" prefWidth="97.33334350585938" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="488.99999237060547" minWidth="10.0" prefWidth="481.9999898274739" />
</columnConstraints>
<rowConstraints>
<RowConstraints maxHeight="149.66665903727215" minHeight="10.0" prefHeight="149.66665903727215" valignment="TOP" vgrow="SOMETIMES" />
<RowConstraints maxHeight="187.9999647140503" minHeight="10.0" prefHeight="51.00000762939453" vgrow="SOMETIMES" />
<RowConstraints maxHeight="105.66662597656247" minHeight="10.0" prefHeight="54.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="105.66662597656247" minHeight="10.0" prefHeight="46.66666666666666" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label prefHeight="17.0" prefWidth="65.0" text="Description:">
<GridPane.margin>
<Insets top="10.0" />
</GridPane.margin>
</Label>
<Label text="Light-Level:" GridPane.rowIndex="1" />
<Label text="Spacing:" GridPane.rowIndex="2" />
<Label text="Soil-Type:" GridPane.rowIndex="3" />
<Label fx:id="description_label" text="Label" wrapText="true" GridPane.columnIndex="1">
<GridPane.margin>
<Insets left="10.0" top="10.0" />
</GridPane.margin>
</Label>
<Label fx:id="light_label" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="1">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Label>
<Label fx:id="spacing_label" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="2">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Label>
<Label fx:id="soil_label" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="3">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Label>
</children>
</GridPane>
<ImageView fx:id="imageView" fitHeight="300.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
</children>
</HBox>
<Label text="Growth Phases:">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Label>
<VBox fx:id="growthPhases_vbox" prefHeight="135.0" prefWidth="879.0">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</VBox>
<Button mnemonicParsing="false" onAction="#editTaskList" prefHeight="25.0" prefWidth="92.0" text="Edit Tasklist">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</Button>
<Label text="Pests:" />
<VBox fx:id="pests_vbox" prefHeight="200.0" prefWidth="100.0">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</VBox>
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
<children>
<Label text="Area:">
<HBox.margin>
<Insets right="60.0" />
</HBox.margin>
</Label>
<Label fx:id="area_label" text="Label">
<HBox.margin>
<Insets right="10.0" />
</HBox.margin>
</Label>
<Button fx:id="area_button" mnemonicParsing="false" onAction="#setArea" prefHeight="25.0" prefWidth="116.0" text="Add Area" />
</children>
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
</HBox>
<HBox alignment="CENTER_LEFT" layoutX="20.0" layoutY="719.0" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
<children>
<Label text="Location:">
<HBox.margin>
<Insets right="40.0" />
</HBox.margin>
</Label>
<Label fx:id="location_label" text="Label">
<HBox.margin>
<Insets right="10.0" />
</HBox.margin>
</Label>
<Button fx:id="location_button" mnemonicParsing="false" onAction="#setLocation" prefHeight="25.0" prefWidth="115.0" text="Add Location" />
</children>
</HBox>
</children>
</VBox>
</content>
</ScrollPane>
</children>
</AnchorPane>
@@ -7,9 +7,7 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyPlantsController">
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyPlantsController">
<children>
<VBox layoutY="49.0" prefHeight="655.0" prefWidth="1175.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
@@ -21,7 +19,7 @@
<Insets bottom="10.0" />
</VBox.margin>
</Label>
<VBox fx:id="myPlants_vbox" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" />
<VBox fx:id="myPlants_vbox" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" />
<Button fx:id="addPlant_button" mnemonicParsing="false" onAction="#addPlant" prefHeight="45.0" prefWidth="155.0" text="Add new Plant">
<VBox.margin>
<Insets top="10.0" />
@@ -1,23 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.geometry.Rectangle2D?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0" prefWidth="1219.0"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
<children>
<SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
@@ -30,18 +28,11 @@
<Font name="System Bold" size="30.0" />
</font>
</Label>
<TextField fx:id="search_plants" onInputMethodTextChanged="#searchForPlant" promptText="Search for Plant Name" />
<TextField fx:id="search_plants" promptText="Search for Plant Name" />
<HBox alignment="CENTER_LEFT" prefHeight="480.0" prefWidth="881.0" VBox.vgrow="ALWAYS">
<children>
<ListView fx:id="list_plants" maxWidth="1.7976931348623157E308" prefHeight="497.0" prefWidth="580.0" HBox.hgrow="ALWAYS" />
<ImageView fx:id="img_plant" fitHeight="322.0" fitWidth="861.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
<viewport>
<Rectangle2D height="300.0" width="300.0" />
</viewport>
<image>
<Image url="@placeholder.png" />
</image>
</ImageView>
<ListView fx:id="list_plants" maxWidth="1.7976931348623157E308" prefHeight="497.0" prefWidth="400.0" HBox.hgrow="ALWAYS" />
<ImageView fx:id="img_plant" fitWidth="300" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
</children>
</HBox>
<Label prefHeight="33.0" prefWidth="919.0" text="Plant Information:">
@@ -57,7 +48,7 @@
<Insets bottom="10.0" top="10.0" />
</padding>
</Label>
<Button fx:id="saveToMyPlant_button" alignment="CENTER" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#saveToMyPlant" prefHeight="38.0" prefWidth="917.0" text="Save to MyPlants" />
<Button fx:id="selectSowDay_button" alignment="CENTER" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#selectSowDate" prefHeight="38.0" prefWidth="917.0" text="Select Harvest/Sow Day" />
</children>
</VBox>
</children>
@@ -75,30 +66,7 @@
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<VBox layoutX="37.0" layoutY="-19.0" prefHeight="180.66666666666666" prefWidth="310.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<CheckBox fx:id="spring_filter" mnemonicParsing="false" onAction="#filterSpring" text="Spring">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
<CheckBox fx:id="sommer_filter" mnemonicParsing="false" onAction="#filterSommer" text="Sommer">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
<CheckBox fx:id="autum_filter" mnemonicParsing="false" onAction="#filterAutum" text="Autum">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
<CheckBox fx:id="winter_filter" mnemonicParsing="false" onAction="#filterWinter" text="Winter">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
</children>
</VBox>
<VBox fx:id="seasons" layoutX="37.0" layoutY="-19.0" prefHeight="180.66666666666666" prefWidth="310.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</content>
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0"
prefWidth="308.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.SelectSowDayController">
<children>
<VBox maxWidth="1.7976931348623157E308" prefHeight="408.0" prefWidth="640.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="popup_label" text="Label" />
<HBox alignment="CENTER" prefHeight="293.0" prefWidth="631.0">
<children>
<VBox alignment="CENTER_LEFT" prefHeight="88.0" prefWidth="155.0">
<children>
<RadioButton fx:id="sow_radio" mnemonicParsing="false" selected="true" text="Sow">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
<toggleGroup>
<ToggleGroup fx:id="group" />
</toggleGroup>
</RadioButton>
<RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$group" />
</children>
<HBox.margin>
<Insets top="10.0" />
</HBox.margin>
</VBox>
<DatePicker fx:id="datepicker" />
</children>
</HBox>
<HBox fillHeight="false" prefHeight="54.0" prefWidth="631.0" VBox.vgrow="NEVER">
<children>
<Button fx:id="save_button" mnemonicParsing="false" onAction="#save" prefHeight="25.0" prefWidth="53.0" text="Save">
<HBox.margin>
<Insets right="10.0" />
</HBox.margin>
</Button>
<Button fx:id="cancel_button" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
</children>
</HBox>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
</children>
</AnchorPane>
@@ -31,7 +31,7 @@
"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.\"",
"description": "Take an egg carton and fill it with soil. Put the seedling deep enough so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.",
"interval": null,
"isOptional": false
}
@@ -53,7 +53,7 @@
"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.\"",
"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
}
@@ -94,6 +94,7 @@
"endDate": "03-10",
"zone": "ZONE_8A",
"type": "SOW",
"group": 0,
"wateringCycle": {
"litersPerSqM": 15,
"interval": 3,
@@ -115,6 +116,7 @@
"endDate": "05-10",
"zone": "ZONE_8A",
"type": "PLANT",
"group": 0,
"wateringCycle": {
"litersPerSqM": 25,
"interval": 3,
@@ -138,6 +140,7 @@
"endDate": "05-20",
"zone": "ZONE_8A",
"type": "HARVEST",
"group": 0,
"wateringCycle": {
"litersPerSqM": 0,
"interval": null,
@@ -6,7 +6,7 @@
"startDate" : "2022-05-01",
"endDate" : "2022-05-01",
"interval" : 0,
"cropID" : 0
"cropId" : 0
},
{
"id" : 2,
@@ -15,7 +15,7 @@
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 2,
"cropID" : 0
"cropId" : 0
},
{
"id" : 3,
@@ -24,7 +24,7 @@
"startDate" : "2022-06-01",
"endDate" : "2022-08-01",
"interval" : 28,
"cropID" : 0
"cropId" : 0
},
{
"id" : 4,
@@ -33,7 +33,7 @@
"startDate" : "2022-07-01",
"endDate" : "2022-07-01",
"interval" : 0,
"cropID" : 0
"cropId" : 0
},
{
"id" : 5,
@@ -42,7 +42,7 @@
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 5,
"cropID" : 0
"cropId" : 0
},
{
"id" : 6,
@@ -51,6 +51,6 @@
"startDate" : "2022-09-01",
"endDate" : "2022-09-01",
"interval" : 0,
"cropID" : 0
"cropId" : 0
}
]
@@ -0,0 +1,113 @@
package ch.zhaw.gartenverwaltung.gardenplan;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
public class GardenPlanModelTest {
GardenPlan gardenPlan;
List<Crop> cropList;
Crop exampleCropOnion;
Crop exampleCropCarrot;
Crop exampleCrop1;
Crop exampleCrop2;
Crop exampleCrop3;
Plant examplePlantOnion;
Plant examplePlantCarrot;
Gardenplanmodel model;
@BeforeEach
void setUp() throws IOException {
examplePlantOnion = new Plant(
0,
"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.",
null,
"15,30,2",
0,
"sandy to loamy, loose soil, free of stones",
new ArrayList<>(),
List.of(new GrowthPhase(MonthDay.of(6, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.HARVEST, HardinessZone.ZONE_8A, new ArrayList<>()),
new GrowthPhase(MonthDay.of(4, 3), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
new GrowthPhase(MonthDay.of(8, 5), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
new GrowthPhase(MonthDay.of(2, 8), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
new GrowthPhase(MonthDay.of(10, 2), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023,3,1));
exampleCropOnion.withId(3);
examplePlantCarrot = 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.",
null,
"5,35,2.5",
0,
"sandy to loamy, loose soil, free of stones",
new ArrayList<>(),
List.of(new GrowthPhase(MonthDay.of(4, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
exampleCropCarrot = new Crop(examplePlantCarrot.id(), LocalDate.now());
exampleCropCarrot.withId(5);
exampleCrop1 = new Crop(1, LocalDate.of(2023,2,25));
exampleCrop1.withId(0);
exampleCrop1.withArea(0.5);
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
exampleCrop2.withId(1);
exampleCrop2.withArea(0.5);
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,01));
exampleCrop3.withId(2);
exampleCrop3.withArea(1.0);
cropList = new ArrayList<>();
cropList.add(exampleCrop1);
cropList.add(exampleCrop2);
cropList.add(exampleCrop3);
gardenPlan = mockGardenPlan(cropList);
TaskListModel taskListModel = new TaskListModel(new JsonTaskDatabase(), new JsonPlantDatabase());
model = new Gardenplanmodel(taskListModel);
}
GardenPlan mockGardenPlan(List<Crop> cropList) throws IOException {
GardenPlan gardenPlan = mock(GardenPlan.class);
when(gardenPlan.getCrops()).thenReturn(cropList);
when(gardenPlan.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
when(gardenPlan.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
return gardenPlan;
}
@Test
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
exampleCropOnion = model.getCrop(2L).get();
assertEquals(model.getCrops().get(2),exampleCropOnion);
}
@Test
void removeCrop() throws IOException {
exampleCrop1.withId(2);
exampleCrop1.withArea(1.500000);
model.removeCrop(exampleCrop1);
assertEquals(2,model.getCrops().size());
}
}
@@ -0,0 +1,105 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Crop;
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.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JsonGardenPlanTest {
private GardenPlan testDatabase;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* Files to isolate the test-units
*/
private final URL dbDataSource = this.getClass().getResource("user-crops.json");
private final URL testFile = this.getClass().getResource("test-user-crops.json");
@BeforeEach
void connectToDb() throws URISyntaxException, IOException {
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonGardenPlan(dbDataSource);
}
@Test
@DisplayName("Check if results are retrieved completely.")
void getCropsNotEmpty() {
List<Crop> testList;
try {
testList = testDatabase.getCrops();
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(3, testList.size());
List<Long> plantIds = testList.stream().map(Crop::getPlantId).collect(Collectors.toList());
List<Long> expected = Arrays.asList(1L, 1L, 0L);
Assertions.assertEquals(expected, plantIds);
}
@Test
@DisplayName("Check whether single access works.")
void getCropById() throws IOException {
Optional<Crop> testCrop = testDatabase.getCropById(1);
assertTrue(testCrop.isPresent());
Assertions.assertEquals(1, testCrop.get().getPlantId());
}
@Test
@DisplayName("Check for a nonexisting crop.")
void getCropByIdMustFail() throws IOException {
Optional<Crop> testCrop = testDatabase.getCropById(99);
Assertions.assertFalse(testCrop.isPresent());
}
@Test
@DisplayName("Add new Crop.")
void addNewCrop() {
Crop crop = new Crop(3L, LocalDate.parse("2023-02-22", formatter));
try {
testDatabase.saveCrop(crop);
assertTrue(crop.getCropId().isPresent());
Optional<Crop> testCrop = testDatabase.getCropById(crop.getCropId().get());
assertTrue(testCrop.isPresent());
Assertions.assertEquals(crop, testCrop.get());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
@DisplayName("Remove crop")
void removeCrop(){
try {
Optional<Crop> crop = testDatabase.getCropById(2L);
Assertions.assertTrue(crop.isPresent());
testDatabase.removeCrop(crop.get());
crop = testDatabase.getCropById(2L);
Assertions.assertFalse(crop.isPresent());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -8,7 +8,6 @@ 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;
@@ -16,7 +15,6 @@ import java.util.stream.Collectors;
public class JsonPlantDatabaseTest {
PlantDatabase testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach
void connectToDb() {
@@ -26,7 +24,7 @@ public class JsonPlantDatabaseTest {
@Test
@DisplayName("Check if results are retrieved completely")
void getPlantList() {
void getPlantListNotEmpty() {
List<Plant> testList;
try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_8A);
@@ -40,9 +38,23 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals(expected,names);
}
@Test
@DisplayName("Check if results are retrieved correctly when empty")
void getPlantListEmpty() {
List<Plant> testList;
try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_1A);
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(0, testList.size());
}
@Test
@DisplayName("Check whether single access works.")
void getPlantById() {
void getPlantByIdAndZone() {
Optional<Plant> testPlant;
try {
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
@@ -53,6 +65,17 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals("Early Carrot", testPlant.get().name());
}
@Test
@DisplayName("Check whether single access respects zone correctly.")
void getPlantByIdAndWrongZone() throws HardinessZoneNotSetException, IOException {
Optional<Plant> testPlant = testDatabase.getPlantById(HardinessZone.ZONE_1A, 1);
Assertions.assertFalse(testPlant.isPresent());
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
Assertions.assertTrue(testPlant.isPresent());
Assertions.assertEquals("Early Carrot", testPlant.get().name());
}
@Test
@DisplayName("Check for a nonexisting plant.")
void getPlantByIdMustFail() {
@@ -173,6 +173,8 @@ class PlantListModelTest {
assertEquals(examplePlantList.get(1), plantListResult.get(0));
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "apple");
assertEquals(0, plantListResult.size());
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "");
assertEquals(3, plantListResult.size());
}
@Test
@@ -0,0 +1,257 @@
[
{
"id": 0,
"name": "Potato",
"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,
"spacing": "35",
"soil": "sandy",
"image": "potato.jpg",
"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.",
"measures": "Less water."
}
],
"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
}
]
}
]
},
{
"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.",
"image": "carrot.jpg",
"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.",
"measures": "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.",
"image": "onion.jpg",
"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.",
"measures": "less water"
}
]
}
]
@@ -0,0 +1,56 @@
[
{
"id" : 1,
"name" : "sow plant",
"description": "Plant the seeds, crops in de bed.",
"startDate" : "2022-05-01",
"endDate" : "2022-05-01",
"interval" : 0,
"cropId" : 0
},
{
"id" : 2,
"name" : "water plant",
"description": "water the plant, so that the soil is wet around the plant.",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 2,
"cropId" : 0
},
{
"id" : 3,
"name" : "fertilize plant",
"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,
"cropId" : 0
},
{
"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" : "2022-07-01",
"endDate" : "2022-07-01",
"interval" : 0,
"cropId" : 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" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 5,
"cropId" : 0
},
{
"id" : 6,
"name" : "harvest plant",
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
"startDate" : "2022-09-01",
"endDate" : "2022-09-01",
"interval" : 0,
"cropId" : 0
}
]
@@ -0,0 +1,257 @@
[
{
"id": 0,
"name": "Potato",
"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,
"spacing": "35",
"soil": "sandy",
"image": "potato.jpg",
"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.",
"measures": "Less water."
}
],
"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
}
]
}
]
},
{
"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.",
"image": "carrot.jpg",
"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.",
"measures": "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.",
"image": "onion.jpg",
"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.",
"measures": "less water"
}
]
}
]
@@ -1,50 +0,0 @@
[
{
"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"
}
]
@@ -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
}
]
@@ -0,0 +1,2 @@
[
]