Compare commits

..

87 Commits

Author SHA1 Message Date
gulerdav 8e3af2ba32 Merge branch 'dev' into feature_taskList_m2 2022-11-18 12:27:33 +01:00
gulerdav b70a758099 Merge pull request #61 from schrom01/refactor_gui_M3
Refactor gui m3
2022-11-18 12:22:13 +01:00
David Guler 3a69119eb7 refactor: bind croplist in schedule 2022-11-18 12:21:50 +01:00
schrom01 165dc6d901 implemented Method getGrowphaseGroupForDate 2022-11-18 12:21:21 +01:00
schrom01 384dc2d853 completed Tests for GardenScheduleTest 2022-11-18 11:34:53 +01:00
David Guler 2f69c48800 refactor: dedicated loadPaneToDialog function replacing the previous workaround 2022-11-16 20:37:07 +01:00
gulerdav 86a9eeaf2e Merge pull request #60 from schrom01/refactor_dependency-injection_M3
Refactor: dependency injection M3
2022-11-16 15:30:43 +01:00
David Guler 9ba252b828 refactor: fixed and simplified dayCellFactory even more
Added method to check if a date is within a GrowthPhaseType to plant, thus removing the need for the ugly getMinDate methods and moving knowledge of the phase-internals to the Plant class.

Also removed the need to specify the lifecycle-group to the sowDateFromHarvest method
2022-11-15 22:45:01 +01:00
David Guler 90d2de65de refactor: simplified dayCellFactory for date selector
Instead of generating a list of dates for every single visible date and checking if it is contained in that list, we now use a (admittedly scary-looking) predicate to compare the date to the start and enddates
2022-11-15 15:25:51 +01:00
David Guler 05e7bcc2e8 fix: Made GardenPlanModelTest independent from json content.
isolated GardenPlanModelTests by pre-copying files. Tests pass now.
2022-11-15 11:28:48 +01:00
David Guler 09e582b8a2 refactor: converted SelectSowDay popup to proper JFX Dialog 2022-11-15 11:03:36 +01:00
David Guler 2b7cec7e6a refactor: removed MainFXMLController from dependencies
Replaced MainFXMLController-based scene-changing with event-based scene-changing to remove cyclic dependency
2022-11-15 08:40:42 +01:00
David Guler 5ef3f6c587 refactor: annotation-based dependency-injection 2022-11-14 21:15:27 +01:00
David Guler 15279838b7 refactor: first attempt at dependency injection
also some more renaming and improving date-picker dialog
2022-11-14 20:00:01 +01:00
David Guler 4f80a0a3e0 refactor: renamed everything 2022-11-14 13:47:22 +01:00
Roman Schenk a5e1acc7c3 Merge pull request #59 from schrom01/test_complete_M2
Task Test Copied over from dev
2022-11-14 12:13:15 +01:00
Roman Schenk a8efa8fc2b Merge branch 'dev' into test_complete_M2 2022-11-14 12:09:37 +01:00
Elias Csomor 6737e67cda Task Test Copied over from dev 2022-11-13 16:30:53 +01:00
Elias Csomor a7fa58344c Continued testing for TaskDatabase
Moved from branch and extended
2022-11-12 16:45:18 +01:00
Elias Csomor 6523e1d791 added jacoco 2022-11-12 16:13:02 +01:00
gulerdav c653005652 Merge pull request #58 from schrom01/dev
Merge for Hand-in M2
2022-11-11 12:40:03 +01:00
Roman Schenk bee517317a Merge pull request #57 from schrom01/feature_cropsAndTaskGUI_M2
Feature crops and task gui m2
2022-11-11 12:28:17 +01:00
giavaphi 2be9df6094 Merge branch 'dev' into feature_cropsAndTaskGUI_M2 2022-11-11 12:21:05 +01:00
Roman Schenk 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
giavaphi 60c6dcd0d9 Merge pull request #49 from schrom01/feature_gardenplan-model_M2
Feature gardenplan model m2
2022-11-07 11:57:40 +01:00
giavaphi 52ae2b02bc Merge pull request #55 from schrom01/feature_savePlantToCropGUI_M2
savePlantToCropGUI m2
2022-11-07 11:56:59 +01:00
Roman Schenk 00db602904 Merge branch 'dev' into feature_gardenplan-model_M2 2022-11-06 18:22:33 +01:00
giavaphi 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
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
giavaphi 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
gulerdav 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
giavaphi 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
gulerdav 3afeb8a22d Merge branch 'dev' into feature_controllerPlantList_M2 2022-10-31 13:05:49 +01:00
gulerdav 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
gulerdav 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
Roman Schenk ceb448c1ff Update issue templates 2022-10-20 20:05:31 +02:00
Roman Schenk 8c651aec2b Update issue templates 2022-10-20 20:02:18 +02:00
gulerdav 56fac84e12 Merge pull request #29 from dev
Documents from teams and project setup
2022-10-18 15:24:11 +02:00
Roman Schenk 8b3a0c1570 Update issue templates 2022-10-18 11:19:24 +02:00
Roman Schenk 516a185546 Update issue templates 2022-10-18 11:18:36 +02:00
61 changed files with 3010 additions and 705 deletions

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: Bug report
about: Create a report to document bugs.
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Class / Methods**
List of classes or methods which contains the bug. (If known)
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Features to implement
title: "[FEATURE]"
labels: feature
assignees: ''
---
**Description of Usecase**
A clear and concise description of what the feature allows to do.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**List of Classes and Methods to implement**
List all Classes and Methods which have to be implemented.
**Additional context**
Add any other context or screenshots about the feature request here.

20
.github/ISSUE_TEMPLATE/tests.md vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: Tests
about: Tests to implement
title: "[TEST]"
labels: Tests
assignees: ''
---
**Feature description**
A clear and concise description of the functionality which have to be tested.
**Test cases**
A list of cases which have to be tested.
**List of affected Classes and Methods**
A clear and concise list of the classes and methods which have to be tested.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -3,6 +3,7 @@ plugins {
id 'application' id 'application'
id 'org.openjfx.javafxplugin' version '0.0.13' id 'org.openjfx.javafxplugin' version '0.0.13'
id 'org.beryx.jlink' version '2.25.0' id 'org.beryx.jlink' version '2.25.0'
id 'jacoco'
} }
group 'ch.zhaw.gartenverwaltung' group 'ch.zhaw.gartenverwaltung'
@ -45,6 +46,15 @@ dependencies {
test { test {
useJUnitPlatform() useJUnitPlatform()
finalizedBy jacocoTestReport
}
jacocoTestReport {
dependsOn test
}
jacoco {
toolVersion = "0.8.8"
} }
jlink { jlink {

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

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>

View File

@ -1,19 +0,0 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
public class Config {
private static HardinessZone currentHardinessZone;
static {
currentHardinessZone = HardinessZone.ZONE_8A;
}
public static HardinessZone getCurrentHardinessZone() {
return currentHardinessZone;
}
public static void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
Config.currentHardinessZone = currentHardinessZone;
}
}

View File

@ -0,0 +1,152 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
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.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;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CropDetailController {
private Crop crop;
@Inject
private PlantList plantList;
@Inject
private GardenSchedule gardenSchedule;
@Inject
private Garden garden;
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
@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;
@FXML
void editTaskList() {
}
@FXML
void goBack() {
Stage stage = (Stage) imageView.getScene().getWindow();
stage.close();
}
@FXML
void setArea() {
}
@FXML
void setLocation() {
}
public void setPlantFromCrop(Crop crop) throws PlantNotFoundException {
this.crop = crop;
try {
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
.orElseThrow(PlantNotFoundException::new);
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);
} catch (HardinessZoneNotSetException | IOException e) {
throw new PlantNotFoundException();
}
}
private void createTaskLists(Crop crop) {
crop.getCropId().ifPresent(id -> {
List<Task> taskList;
try {
taskList = gardenSchedule.getTaskListForCrop(id);
for (Task task : taskList) {
Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label);
}
} catch (IOException e) {
// TODO: Alert
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
}
});
}
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);
}
}
}

View File

@ -1,8 +1,7 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import javafx.application.Application; import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
@ -10,10 +9,11 @@ import java.io.IOException;
public class HelloApplication extends Application { public class HelloApplication extends Application {
@Override @Override
public void start(Stage stage) throws IOException { public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("MainFXML.fxml")); AppLoader appLoader = new AppLoader();
Scene scene = new Scene(fxmlLoader.load());
appLoader.loadSceneToStage("MainFXML.fxml", stage);
stage.setTitle("Gartenverwaltung"); stage.setTitle("Gartenverwaltung");
stage.setScene(scene);
stage.show(); stage.show();
} }

View File

@ -1,19 +1,24 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import javafx.event.ActionEvent; import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.util.logging.Level;
import java.util.Objects; import java.util.logging.Logger;
import java.util.ResourceBundle;
public class MainFXMLController implements Initializable { public class MainFXMLController {
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
@Inject
AppLoader appLoader;
@FXML @FXML
private Button home_button; private Button home_button;
@ -22,7 +27,7 @@ public class MainFXMLController implements Initializable {
private AnchorPane mainPane; private AnchorPane mainPane;
@FXML @FXML
private Button myPlants_button; private Button myGarden_button;
@FXML @FXML
private Button mySchedule_button; private Button mySchedule_button;
@ -31,26 +36,26 @@ public class MainFXMLController implements Initializable {
private Button plants_button; private Button plants_button;
@FXML @FXML
void goToHome(ActionEvent event) throws IOException { void goToHome() {
loadPane("Home.fxml"); showPaneAsMainView("Home.fxml");
styleChangeButton(home_button); styleChangeButton(home_button);
} }
@FXML @FXML
void goToMyPlants(ActionEvent event) throws IOException { void goToMyPlants() {
loadPane("MyPlants.fxml"); showPaneAsMainView("MyGarden.fxml");
styleChangeButton(myPlants_button); styleChangeButton(myGarden_button);
} }
@FXML @FXML
void goToMySchedule(ActionEvent event) throws IOException { void goToMySchedule() {
loadPane("MySchedule.fxml"); showPaneAsMainView("MySchedule.fxml");
styleChangeButton(mySchedule_button); styleChangeButton(mySchedule_button);
} }
@FXML @FXML
void goToPlants(ActionEvent event) throws IOException { void goToPlants() {
loadPane("Plants.fxml"); showPaneAsMainView("Plants.fxml");
styleChangeButton(plants_button); styleChangeButton(plants_button);
} }
@ -59,18 +64,27 @@ public class MainFXMLController implements Initializable {
* set HGrow and VGrow to parent AnchorPane. * set HGrow and VGrow to parent AnchorPane.
* Sends MainController to other Controllers. * Sends MainController to other Controllers.
* @param fxmlFile string of fxml file * @param fxmlFile string of fxml file
* @throws IOException exception when file does not exist
*/ */
public void loadPane(String fxmlFile) throws IOException { public void showPaneAsMainView(String fxmlFile) {
//ToDo HGrow and VGrow of new node try {
Node node; Pane anchorPane = appLoader.loadPane(fxmlFile);
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile))); mainPane.getChildren().setAll(anchorPane);
node = (Node)loader.load(); anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
if(fxmlFile.equals("MyPlants.fxml")) { anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
MyPlantsController myPlantsController = loader.getController();
myPlantsController.getMainController(this); anchorPane.removeEventHandler(ChangeViewEvent.CHANGE_MAIN_VIEW, changeMainViewHandler);
anchorPane.addEventHandler(ChangeViewEvent.CHANGE_MAIN_VIEW, changeMainViewHandler);
} catch (IOException e) {
LOG.log(Level.SEVERE, "Could not load pane.", e);
} }
mainPane.getChildren().setAll(node); }
private final EventHandler<ChangeViewEvent> changeMainViewHandler = (ChangeViewEvent event) -> showPaneAsMainView(event.view());
private void preloadPanes() throws IOException {
appLoader.loadAndCacheFxml("MyGarden.fxml");
appLoader.loadAndCacheFxml("MySchedule.fxml");
appLoader.loadAndCacheFxml("Plants.fxml");
} }
private void styleChangeButton(Button button) { private void styleChangeButton(Button button) {
@ -81,13 +95,15 @@ public class MainFXMLController implements Initializable {
* loads the default FXML File * loads the default FXML File
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @AfterInject
public void initialize(URL url, ResourceBundle resourceBundle) { @SuppressWarnings("unused")
public void init() {
try { try {
loadPane("Home.fxml"); preloadPanes();
showPaneAsMainView("Home.fxml");
styleChangeButton(home_button); styleChangeButton(home_button);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);
} }
} }
} }

View File

@ -0,0 +1,148 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
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.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.AnchorPane;
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.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyGardenController {
private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName());
@Inject
AppLoader appLoader;
@Inject
private Garden garden;
@Inject
private PlantList plantList;
@FXML
public AnchorPane myGardenRoot;
@FXML
private VBox myPlants_vbox;
@AfterInject
@SuppressWarnings("unused")
public void init() {
garden.getPlantedCrops().addListener((observable, oldValue, newValue) -> {
try {
createPlantView(newValue);
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
});
try {
createPlantView(garden.getPlantedCrops());
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
}
@FXML
void addPlant() {
myGardenRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "Plants.fxml"));
}
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);
}
}
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
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) {
return (event) -> {
try {
Stage stage = new Stage();
if (appLoader.loadSceneToStage("CropDetail.fxml", stage) instanceof CropDetailController controller) {
controller.setPlantFromCrop(crop);
}
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | PlantNotFoundException e) {
// TODO: show error alert
LOG.log(Level.SEVERE, "Could not load plant details.", e);
}
};
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
return (event) -> {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
};
}
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("Deleting this crop will remove all associated tasks from your schedule.");
alert.showAndWait()
.ifPresent(buttonType -> {
if (buttonType == ButtonType.OK) {
try {
garden.removeCrop(crop);
} catch (IOException e) {
// TODO: Show error alert
LOG.log(Level.SEVERE, "Could not remove crop.", e);
}
}
});
}
}

View File

@ -1,58 +0,0 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
public class MyPlantsController implements Initializable {
MainFXMLController mainController;
@FXML
private Button addPlant_button;
@FXML
private VBox myPlants_vbox;
@FXML
void addPlant(ActionEvent event) throws IOException {
mainController.loadPane("Plants.fxml");
}
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//ToDo
List<Plant> myPlants = getMyPlants();
createPlantView(myPlants);
}
private void createPlantView(List<Plant> myPlants) {
//ToDo
for(Plant plant : myPlants) {
createPlantView();
}
}
public void getMainController(MainFXMLController controller) {
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 void createPlantView() {
//ToDo FXML Panel with Plant data
}
}

View File

@ -1,23 +1,38 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.beans.value.ChangeListener; import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.net.URL; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.logging.Level;
import java.util.logging.Logger;
public class MyScheduleController implements Initializable { public class MyScheduleController {
private Plant selectedPlant = null; private static final Logger LOG = Logger.getLogger(MyScheduleController.class.getName());
private Crop selectedCrop = null;
@Inject
private GardenSchedule gardenSchedule;
@Inject
private Garden garden;
@Inject
private PlantList plantList;
@FXML @FXML
private Label day1_label; private Label day1_label;
@ -65,28 +80,30 @@ public class MyScheduleController implements Initializable {
private Label information_label; private Label information_label;
@FXML @FXML
private ListView<Plant> scheduledPlants_listview; private ListView<Crop> scheduledPlants_listview;
@Override @AfterInject
public void initialize(URL location, ResourceBundle resources) { @SuppressWarnings("unused")
List<Plant> plantList = new LinkedList<>(); public void init() {
fillListViewMyPlantsInSchedule(plantList); setCellFactoryListView();
getSelectedPlantTask(); scheduledPlants_listview.itemsProperty().bind(garden.getPlantedCrops());
lookForSelectedListEntries();
setDayLabels(); setDayLabels();
information_label.setText(""); information_label.setText("");
try {
loadTaskList();
} catch (IOException e) {
e.printStackTrace();
}
} }
private void getSelectedPlantTask() { private void lookForSelectedListEntries() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() { scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
@Override selectedCrop = newValue;
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) { try {
if(newValue != null) { loadTaskList();
selectedPlant = newValue; } catch (IOException e) {
//ToDo update day<x>_panel with task for the day e.printStackTrace();
} else {
selectedPlant = null;
//ToDo update day<x>_panel with task for the day (all plants)
}
} }
}); });
} }
@ -102,23 +119,55 @@ public class MyScheduleController implements Initializable {
day7_label.setText(today.plusDays(6).getDayOfWeek().toString()); day7_label.setText(today.plusDays(6).getDayOfWeek().toString());
} }
private void fillListViewMyPlantsInSchedule(List<Plant> list) { private void setCellFactoryListView() {
for (Plant plant : list) { scheduledPlants_listview.setCellFactory(param -> new ListCell<>() {
scheduledPlants_listview.getItems().add(plant);
}
scheduledPlants_listview.setCellFactory(param -> new ListCell<Plant>() {
@Override @Override
protected void updateItem(Plant plant, boolean empty) { protected void updateItem(Crop crop, boolean empty) {
super.updateItem(plant, empty); super.updateItem(crop, empty);
if (empty || plant == null || plant.name() == null) { if (empty || crop == null) {
setText(null); setText(null);
} else { } else {
setText(plant.name()); try {
String text = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
.map(Plant::name)
.orElse("");
setText(text);
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.WARNING, "Could not get plant for Cell", e);
}
} }
} }
}); });
} }
private void loadTaskList() throws IOException {
List<List<Task>> taskLists;
if (selectedCrop != null) {
taskLists = gardenSchedule.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
} else {
taskLists = gardenSchedule.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);
}
} }

View File

@ -1,32 +1,54 @@
package ch.zhaw.gartenverwaltung; package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel; import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.beans.value.ChangeListener; import ch.zhaw.gartenverwaltung.types.Seasons;
import javafx.beans.value.ObservableValue; import javafx.beans.property.ListProperty;
import javafx.event.ActionEvent; import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.geometry.Insets;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.InputMethodEvent; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.time.LocalDate;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.logging.Level;
import java.util.logging.Logger;
public class PlantsController {
private static final Logger LOG = Logger.getLogger(PlantsController.class.getName());
@Inject
private PlantListModel plantListModel;
@Inject
private AppLoader appLoader;
@Inject
private Garden garden;
public class PlantsController implements Initializable {
private final PlantListModel plantListModel = new PlantListModel();
private Plant selectedPlant = null; private Plant selectedPlant = null;
private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A; private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;
// TODO: move to model
private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML @FXML
private CheckBox autum_filter; public AnchorPane plantsRoot;
@FXML
private VBox seasons;
@FXML @FXML
private VBox climate_zones; private VBox climate_zones;
@ -41,85 +63,78 @@ public class PlantsController implements Initializable {
private ListView<Plant> list_plants; private ListView<Plant> list_plants;
@FXML @FXML
private Button saveToMyPlant_button; private Button selectSowDay_button;
@FXML @FXML
private TextField search_plants; private TextField search_plants;
/**
* open new window to select sow or harvest day to save the crop
*/
@FXML @FXML
private CheckBox sommer_filter; void selectSowDate() throws IOException {
Dialog<LocalDate> dateSelection = new Dialog<>();
dateSelection.setTitle("Select Date");
dateSelection.setHeaderText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name()));
dateSelection.setResizable(false);
@FXML DialogPane dialogPane = dateSelection.getDialogPane();
private CheckBox spring_filter;
@FXML ButtonType sowButton = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
private CheckBox winter_filter; dialogPane.getButtonTypes().addAll(sowButton, ButtonType.CANCEL);
@FXML if (appLoader.loadPaneToDialog("SelectSowDay.fxml", dialogPane) instanceof SelectSowDayController controller) {
void filterAutum(ActionEvent event) { controller.initSaveButton((Button) dialogPane.lookupButton(sowButton));
//ToDo controller.setSelectedPlant(selectedPlant);
dateSelection.setResultConverter(button -> button.equals(sowButton) ? controller.retrieveResult() : null);
} dateSelection.showAndWait()
.ifPresent(date -> {
@FXML try {
void filterSommer(ActionEvent event) { garden.plantAsCrop(selectedPlant, date);
//ToDo } catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
LOG.log(Level.SEVERE, "Couldn't save Crop", e);
} }
plantsRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "MyGarden.fxml"));
@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());
} }
/** /**
* 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} * {@inheritDoc}
*/ */
@Override @AfterInject
public void initialize(URL url, ResourceBundle resourceBundle) { @SuppressWarnings("unused")
List<Plant> plantList = new LinkedList<>(); public void init() {
try { setListCellFactory();
plantList = plantListModel.getPlantList(DEFAULT_HARDINESS_ZONE); fillPlantListWithHardinessZone();
} catch (HardinessZoneNotSetException | IOException e) { list_plants.itemsProperty().bind(plantListProperty);
e.printStackTrace();
}
fillListViewWithData(plantList);
description_plant.setText(""); description_plant.setText("");
saveToMyPlant_button.setDisable(true); selectSowDay_button.setDisable(true);
createFilterSeasons();
createFilterHardinessZone(); createFilterHardinessZone();
lookForSelectedListEntry(); 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 * set text of list view to plant name
* Entry in ListView is plant name
* @param list plantList which fill the ListView
*/ */
private void fillListViewWithData(List<Plant> list) { private void setListCellFactory() {
clearListView(); list_plants.setCellFactory(param -> new ListCell<>() {
for (Plant plant : list) {
list_plants.getItems().add(plant);
}
list_plants.setCellFactory(param -> new ListCell<Plant>() {
@Override @Override
protected void updateItem(Plant plant, boolean empty) { protected void updateItem(Plant plant, boolean empty) {
super.updateItem(plant, empty); super.updateItem(plant, empty);
@ -133,45 +148,142 @@ public class PlantsController implements Initializable {
}); });
} }
private void viewFilteredListByFilters() { /**
boolean springValue = spring_filter.isSelected(); * get plant list according to param season and hardiness zone
boolean sommerValue = sommer_filter.isSelected(); * fill list view with plant list
boolean autumValue = autum_filter.isSelected(); *
boolean winterValue = winter_filter.isSelected(); * @param season enum of seasons
//ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, <predicate>)) * @throws HardinessZoneNotSetException throws exception
//List<Plant> plantList = new LinkedList<>(); * @throws IOException throws exception
//fillListViewWithData(plantList); */
} private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
clearListView();
private void viewFilteredListBySearch(String query) { plantListProperty.addAll(plantListModel.getFilteredPlantListBySaisonWithoutGrowthPhase(plantListModel.getCurrentZone(), season.getStartDate(), season.getEndDate()));
//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
} }
/** /**
* 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() { private void lookForSelectedListEntry() {
list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() { selectedPlant = null;
@Override description_plant.setText("");
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) { selectSowDay_button.setDisable(true);
//ToDo Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
if(newValue != null) { img_plant.setImage(img);
selectedPlant = newValue; list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
description_plant.setText(selectedPlant.description()); if (newValue != null) {
saveToMyPlant_button.setDisable(false); selectedPlant = newValue;
//update img plant description_plant.setText(selectedPlant.description());
selectSowDay_button.setDisable(false);
Image img1;
if (selectedPlant.image() != null) {
img1 = selectedPlant.image();
} else { } else {
selectedPlant = null; img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
description_plant.setText("");
saveToMyPlant_button.setDisable(true);
//update img when null placeholder PNG
} }
img_plant.setImage(img1);
} else {
selectedPlant = null;
description_plant.setText("");
selectSowDay_button.setDisable(true);
Image img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
img_plant.setImage(img1);
} }
}); });
} }
@ -181,6 +293,6 @@ public class PlantsController implements Initializable {
* clears the ListView of entries * clears the ListView of entries
*/ */
private void clearListView() { private void clearListView() {
list_plants.getItems().clear(); plantListProperty.clear();
} }
} }

View File

@ -0,0 +1,94 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.util.Callback;
import java.time.LocalDate;
public class SelectSowDayController {
private Plant selectedPlant;
@FXML
private DatePicker datepicker;
@FXML
private RadioButton harvest_radio;
@FXML
private RadioButton sow_radio;
@FXML
public ToggleGroup phase_group;
public LocalDate retrieveResult() {
LocalDate sowDate = datepicker.getValue();
if (harvest_radio.isSelected()) {
sowDate = selectedPlant.sowDateFromHarvestDate(sowDate);
}
return sowDate;
}
/**
* Set the {@link Plant} for which a date should be selected.
*
* @param plant Plant
*/
public void setSelectedPlant(Plant plant) {
selectedPlant = plant;
}
/**
* add listener and set default values
*/
@FXML
public void initialize() {
clearDatePickerEntries();
Callback<DatePicker, DateCell> dayCellFactory = getDayCellFactory();
datepicker.setDayCellFactory(dayCellFactory);
datepicker.setEditable(false);
sow_radio.setUserData(GrowthPhaseType.SOW);
harvest_radio.setUserData(GrowthPhaseType.HARVEST);
}
public void initSaveButton(Button saveButton) {
saveButton.disableProperty().bind(datepicker.valueProperty().isNull());
}
/**
* clear date picker editor when radio button is changed
*/
private void clearDatePickerEntries() {
harvest_radio.selectedProperty().addListener((observable, oldValue, isNowSelected) -> datepicker.setValue(null));
}
/**
* date picker disable/enable dates according to selected plant: sow or harvest day
*
* @return cellFactory of datePicker
*/
private Callback<DatePicker, DateCell> getDayCellFactory() {
return (datePicker) -> new DateCell() {
private final LocalDate today = LocalDate.now();
@Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
if (item.compareTo(today) > 0 && (!harvest_radio.isSelected() || selectedPlant.sowDateFromHarvestDate(item).compareTo(today) >= 0)) {
GrowthPhaseType selectedPhase = (GrowthPhaseType) phase_group.getSelectedToggle().getUserData();
if (selectedPlant.isDateInPhase(item, selectedPhase)) {
setDisable(false);
setStyle("-fx-background-color: #32CD32;");
}
}
}
};
}
}

View File

@ -0,0 +1,26 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
public class Settings {
private HardinessZone currentHardinessZone = HardinessZone.ZONE_8A;
private static Settings instance;
static {
instance = new Settings();
}
public static Settings getInstance() {
return Settings.instance;
}
private Settings() {}
public HardinessZone getCurrentHardinessZone() {
return currentHardinessZone;
}
public void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
this.currentHardinessZone = currentHardinessZone;
}
}

View File

@ -0,0 +1,14 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a method to be executed after all dependencies annotates with {@link Inject}
* have been injected.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterInject { }

View File

@ -0,0 +1,157 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import ch.zhaw.gartenverwaltung.HelloApplication;
import ch.zhaw.gartenverwaltung.io.CropList;
import ch.zhaw.gartenverwaltung.io.JsonCropList;
import ch.zhaw.gartenverwaltung.io.JsonPlantList;
import ch.zhaw.gartenverwaltung.io.JsonTaskList;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.io.TaskList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.DialogPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class AppLoader {
/**
* Caching the panes
*/
private final Map<String, Pane> panes = new HashMap<>();
/**
* Application-wide dependencies
*/
private final PlantList plantList = new JsonPlantList();
private final CropList cropList = new JsonCropList();
private final TaskList taskList = new JsonTaskList();
private final GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList);
private final Garden garden = new Garden(gardenSchedule, cropList);
public AppLoader() throws IOException {
}
/**
* Loads and returns a {@link Pane} (cached).
*
* @param fxmlFile The file name to be loaded
* @return The loaded Pane
* @throws IOException if the file could not be loaded
*/
public Pane loadPane(String fxmlFile) throws IOException {
Pane pane = panes.get(fxmlFile);
if (pane == null) {
loadAndCacheFxml(fxmlFile);
pane = panes.get(fxmlFile);
}
return pane;
}
/**
* Loads the given fxml-file from resources (no caching) and creates a new {@link Scene},
* which is then appended to the given {@link Stage}.
* Performs dependency-injection.
*
* @param fxmlFile The file name to be loaded
* @param appendee The {@link Stage} to which the new {@link Scene} is appended.
* @return The controller of the loaded scene.
* @throws IOException if the file could not be loaded
*/
public Object loadSceneToStage(String fxmlFile, Stage appendee) throws IOException {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
Pane root = loader.load();
appendee.setScene(new Scene(root));
Object controller = loader.getController();
annotationInject(controller);
return controller;
}
/**
* Loads the given fxml-file from resources (no caching) and appendeds it's
* contents to the given {@link DialogPane}.
* Performs dependency-injection.
*
* @param fxmlFile The file name to be loaded
* @param appendee The {@link DialogPane} to which the FXML contents are to be appended.
* @return The controller of the loaded scene.
* @throws IOException if the file could not be loaded
*/
public Object loadPaneToDialog(String fxmlFile, DialogPane appendee) throws IOException {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
appendee.setContent(loader.load());
Object controller = loader.getController();
annotationInject(controller);
return controller;
}
/**
* Loads the given fxml-file from resources and caches the pane.
* Performs dependency-injection.
*
* @param fxmlFile The file name to be loaded
* @throws IOException if the file could not be loaded
*/
public void loadAndCacheFxml(String fxmlFile) throws IOException {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
Pane pane = loader.load();
panes.put(fxmlFile, pane);
annotationInject(loader.getController());
}
/**
* Injects the applications dependencies into the given object's fields annotated with {@link Inject}.
* Afterwards, all methods on the objects annotated with {@link AfterInject} are executed.
* (Success of the injections is not guaranteed!)
*
* @param controller The class containing the injectable fields
*/
public void annotationInject(Object controller) {
Arrays.stream(controller.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Inject.class))
.forEach(field -> {
field.setAccessible(true);
try {
field.set(controller, getAppDependency(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
field.setAccessible(false);
});
Arrays.stream(controller.getClass().getMethods())
.filter(method -> method.isAnnotationPresent(AfterInject.class))
.forEach(afterInjectMethod -> {
if (afterInjectMethod.getParameterCount() == 0) {
try {
afterInjectMethod.invoke(controller);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
private Object getAppDependency(Class<?> type) {
return switch (type.getSimpleName()) {
case "Garden" -> garden;
case "PlantList" -> plantList;
case "PlantListModel" -> new PlantListModel(plantList);
case "GardenSchedule" -> gardenSchedule;
case "AppLoader" -> this;
default -> null;
};
}
}

View File

@ -0,0 +1,19 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import javafx.event.Event;
import javafx.event.EventType;
public class ChangeViewEvent extends Event {
private final String view;
public static final EventType<ChangeViewEvent> CHANGE_MAIN_VIEW = new EventType<>("CHANGE_MAIN_VIEW");
public ChangeViewEvent(EventType<? extends Event> eventType, String view) {
super(eventType);
this.view = view;
}
public String view() {
return view;
}
}

View File

@ -0,0 +1,13 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a Field to be injected from the application-dependencies
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject { }

View File

@ -1,8 +0,0 @@
package ch.zhaw.gartenverwaltung.gardenplan;
public class Gardenplanmodel {
// private JasonGardenplan gardenplan;
// liste von crops
//task generieren
//plant holen, task template mit tasktemplateklasse tasks erstellen
}

View File

@ -6,7 +6,7 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
public interface GardenPlan { public interface CropList {
/** /**
* Yields a list of all {@link Crop}s in the database. * Yields a list of all {@link Crop}s in the database.
* *
@ -18,11 +18,11 @@ public interface GardenPlan {
/** /**
* Attempts to retrieve the {@link Crop} with the specified cropId. * 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 * @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 * @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. * Saves a Crop to the Database.

View File

@ -19,8 +19,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class JsonGardenPlan implements GardenPlan { public class JsonCropList implements CropList {
private final URL dataSource = getClass().getResource("user-crops.json"); private final URL dataSource;
private IdProvider idProvider; private IdProvider idProvider;
private Map<Long, Crop> cropMap = Collections.emptyMap(); private Map<Long, Crop> cropMap = Collections.emptyMap();
@ -40,7 +40,21 @@ public class JsonGardenPlan implements GardenPlan {
} }
/** /**
* @see GardenPlan#getCrops() * Default constructor
*/
public JsonCropList() {
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 JsonCropList(URL dataSource) {
this.dataSource = dataSource;
}
/**
* {@inheritDoc}
*/ */
@Override @Override
public List<Crop> getCrops() throws IOException { public List<Crop> getCrops() throws IOException {
@ -51,7 +65,7 @@ public class JsonGardenPlan implements GardenPlan {
} }
/** /**
* @see GardenPlan#getCropById(long) * {@inheritDoc}
*/ */
@Override @Override
public Optional<Crop> getCropById(long id) throws IOException { 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. * Saves a crop to the database.
* If no {@link Crop#cropId} is set, one will be generated and * 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 @Override
public void removeCrop(Crop crop) throws IOException { public void removeCrop(Crop crop) throws IOException {
@ -131,7 +145,6 @@ public class JsonGardenPlan implements GardenPlan {
.registerModule(new Jdk8Module()) .registerModule(new Jdk8Module())
.writeValue(new File(dataSource.toURI()), cropMap.values()); .writeValue(new File(dataSource.toURI()), cropMap.values());
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
// TODO: Log
throw new IOException(INVALID_DATASOURCE_MSG, e); throw new IOException(INVALID_DATASOURCE_MSG, e);
} }
} }

View File

@ -20,11 +20,11 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
/** /**
* Implements the {@link PlantDatabase} interface for loading {@link Plant} objects * Implements the {@link PlantList} interface for loading {@link Plant} objects
* from a JSON file. * from a JSON file.
* The reads are cached to minimize file-io operations. * The reads are cached to minimize file-io operations.
*/ */
public class JsonPlantDatabase implements PlantDatabase { public class JsonPlantList implements PlantList {
private final URL dataSource = getClass().getResource("plantdb.json"); private final URL dataSource = getClass().getResource("plantdb.json");
private HardinessZone currentZone; private HardinessZone currentZone;
@ -49,7 +49,7 @@ public class JsonPlantDatabase implements PlantDatabase {
* from the {@link #currentZone}, data is loaded from {@link #dataSource}. * from the {@link #currentZone}, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #plantMap} are returned. * In any case, the values of {@link #plantMap} are returned.
* *
* @see PlantDatabase#getPlantList(HardinessZone) * @see PlantList#getPlantList(HardinessZone)
*/ */
@Override @Override
public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException { public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException {
@ -60,7 +60,7 @@ public class JsonPlantDatabase implements PlantDatabase {
} }
/** /**
* @see PlantDatabase#getPlantById(long) * @see PlantList#getPlantById(long)
*/ */
@Override @Override
public Optional<Plant> getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException { public Optional<Plant> getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException {
@ -89,15 +89,16 @@ public class JsonPlantDatabase implements PlantDatabase {
List<Plant> result; List<Plant> result;
result = mapper.readerForListOf(Plant.class).readValue(dataSource); 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() 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, .collect(HashMap::new,
(res, plant) -> res.put(plant.id(), plant), (res, plant) -> res.put(plant.id(), plant),
(existing, replacement) -> { }); (existing, replacement) -> {});
} }
} }
} }

View File

@ -1,14 +1,15 @@
package ch.zhaw.gartenverwaltung.io; package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Task; import ch.zhaw.gartenverwaltung.types.Task;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
@ -18,13 +19,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Implements the {@link TaskDatabase} interface for loading and writing {@link Task} objects * Implements the {@link TaskList} interface for loading and writing {@link Task} objects
* from and to a JSON file. * from and to a JSON file.
* The reads are cached to minimize file-io operations. * The reads are cached to minimize file-io operations.
*/ */
public class JsonTaskDatabase implements TaskDatabase{ public class JsonTaskList implements TaskList {
IdProvider idProvider; IdProvider idProvider;
private final URL dataSource = getClass().getResource("taskdb.json"); 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(); private Map<Long, Task> taskMap = Collections.emptyMap();
@ -35,14 +37,17 @@ public class JsonTaskDatabase implements TaskDatabase{
static { static {
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd"); DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat); LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat);
LocalDateSerializer dateSerializer = new LocalDateSerializer(dateFormat);
timeModule.addDeserializer(LocalDate.class, dateDeserializer); timeModule.addDeserializer(LocalDate.class, dateDeserializer);
timeModule.addSerializer(LocalDate.class, dateSerializer);
} }
/** /**
* If no data is currently loaded, data is loaded from {@link #dataSource}. * If no data is currently loaded, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #taskMap} are returned. * In any case, the values of {@link #taskMap} are returned.
* *
* @see TaskDatabase#getTaskList(LocalDate, LocalDate) * @see TaskList#getTaskList(LocalDate, LocalDate)
*/ */
@Override @Override
public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{ public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{
@ -84,7 +89,7 @@ public class JsonTaskDatabase implements TaskDatabase{
* it to the {@link #taskMap}. In any case, the {@link #taskMap} is written * it to the {@link #taskMap}. In any case, the {@link #taskMap} is written
* to the {@link #dataSource}. * to the {@link #dataSource}.
* *
* @see TaskDatabase#saveTask(Task) * @see TaskList#saveTask(Task)
*/ */
@Override @Override
public void saveTask(Task task) throws IOException { public void saveTask(Task task) throws IOException {
@ -102,7 +107,7 @@ public class JsonTaskDatabase implements TaskDatabase{
* If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed * If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed
* from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}. * from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}.
* *
* @see TaskDatabase#removeTask(Task) * @see TaskList#removeTask(Task)
*/ */
@Override @Override
public void removeTask(Task task) throws IOException { public void removeTask(Task task) throws IOException {
@ -121,12 +126,16 @@ public class JsonTaskDatabase implements TaskDatabase{
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
private void writeTaskListToFile() throws IOException { private void writeTaskListToFile() throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule)
.registerModule(new Jdk8Module());
if(dataSource != null) { 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 +147,8 @@ public class JsonTaskDatabase implements TaskDatabase{
private void loadTaskListFromFile() throws IOException { private void loadTaskListFromFile() throws IOException {
if (dataSource != null) { if (dataSource != null) {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule); mapper.registerModule(timeModule)
.registerModule(new Jdk8Module());
List<Task> result; List<Task> result;
result = mapper.readerForListOf(Task.class).readValue(dataSource); result = mapper.readerForListOf(Task.class).readValue(dataSource);
@ -148,5 +158,8 @@ public class JsonTaskDatabase implements TaskDatabase{
(res, task) -> res.put(task.getId(), task), (res, task) -> res.put(task.getId(), task),
(existing, replacement) -> {}); (existing, replacement) -> {});
} }
Long maxId = taskMap.isEmpty() ? 0L : Collections.max(taskMap.keySet());
idProvider = new IdProvider(maxId);
} }
} }

View File

@ -11,7 +11,7 @@ import java.util.Optional;
* A database of {@link Plant}s. * A database of {@link Plant}s.
* The interface specifies the minimal required operations. * The interface specifies the minimal required operations.
*/ */
public interface PlantDatabase { public interface PlantList {
/** /**
* Yields a list of all {@link Plant}s in the database with only data relevant to the specfied {@link HardinessZone} * Yields a list of all {@link Plant}s in the database with only data relevant to the specfied {@link HardinessZone}
* *

View File

@ -1,20 +1,16 @@
package ch.zhaw.gartenverwaltung.io; package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task; import ch.zhaw.gartenverwaltung.types.Task;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date;
import java.util.List; import java.util.List;
/** /**
* A database of {@link Task}s. * A database of {@link Task}s.
* The interface specifies the minimal required operations. * The interface specifies the minimal required operations.
*/ */
public interface TaskDatabase { public interface TaskList {
/** /**
* Yields a list of all {@link Task}s in the database with the start and end date of a period of time. * Yields a list of all {@link Task}s in the database with the start and end date of a period of time.
* *

View File

@ -16,13 +16,12 @@ public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType
@Override @Override
public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException { public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException {
GrowthPhaseType result = null; GrowthPhaseType result;
String token = parser.getText(); String token = parser.getText();
try { try {
result = GrowthPhaseType.valueOf(token.toUpperCase()); result = GrowthPhaseType.valueOf(token.toUpperCase());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// TODO: Log throw new IOException(String.format("Bad growth phase type \"%s\"\n", token));
System.err.printf("Bad growth phase type \"%s\"\n", token);
} }
return result; return result;
} }

View File

@ -17,13 +17,12 @@ public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
@Override @Override
public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException { public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException {
HardinessZone result = null; HardinessZone result;
String token = parser.getText(); String token = parser.getText();
try { try {
result = HardinessZone.valueOf(token.toUpperCase()); result = HardinessZone.valueOf(token.toUpperCase());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// TODO: Log throw new IOException(String.format("Unknown Hardiness Zone \"%s\"\n", token), e);
System.err.printf("Unknown Hardiness Zone \"%s\"\n", token);
} }
return result; return result;
} }

View File

@ -1,14 +1,16 @@
package ch.zhaw.gartenverwaltung.json; package ch.zhaw.gartenverwaltung.json;
import ch.zhaw.gartenverwaltung.io.PlantDatabase; import ch.zhaw.gartenverwaltung.io.PlantList;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonDeserializer;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
public class PlantImageDeserializer extends JsonDeserializer<Image> { public class PlantImageDeserializer extends JsonDeserializer<Image> {
@ -16,14 +18,12 @@ public class PlantImageDeserializer extends JsonDeserializer<Image> {
@Override @Override
public Image deserialize(JsonParser parser, DeserializationContext context) throws IOException { public Image deserialize(JsonParser parser, DeserializationContext context) throws IOException {
Image result = null; Image result = null;
URL imageUrl = PlantDatabase.class.getResource(String.format("images/%s", parser.getText())); URL imageUrl = PlantList.class.getResource(String.format("images/%s", parser.getText()));
if (imageUrl != null) { if (imageUrl != null) {
try (InputStream is = new FileInputStream(imageUrl.getFile())) { try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) {
result = new Image(is); result = new Image(is);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException | URISyntaxException e) {
// TODO: Log throw new IOException(String.format("Cannot find Image \"%s\"\n", imageUrl.getFile()));
e.printStackTrace();
System.err.printf("Cannot find Image \"%s\"\n", imageUrl.getFile());
} }
} }
return result; return result;

View File

@ -0,0 +1,91 @@
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.CropList;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
/**
* The Gardenplan model manages the crops in the gardenplan.
*/
public class Garden {
private final CropList cropList;
private final ListProperty<Crop> plantedCrops = new SimpleListProperty<>(FXCollections.observableArrayList());
private final GardenSchedule gardenSchedule;
/**
* Constructor of Gardenplan model
*
* @param gardenSchedule holds a reference to the task list object.
*/
public Garden(GardenSchedule gardenSchedule, CropList cropList) throws IOException {
this.gardenSchedule = gardenSchedule;
this.cropList = cropList;
plantedCrops.addAll(cropList.getCrops());
}
public ListProperty<Crop> getPlantedCrops() {
return plantedCrops;
}
/**
* 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);
cropList.saveCrop(crop);
gardenSchedule.planTasksForCrop(crop);
plantedCrops.clear();
plantedCrops.addAll(cropList.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 {
cropList.removeCrop(crop);
gardenSchedule.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new));
plantedCrops.clear();
plantedCrops.addAll(cropList.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 {
return cropList.getCrops();
}
/**
* 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 cropList.getCropById(cropId);
}
}

View File

@ -1,10 +1,7 @@
package ch.zhaw.gartenverwaltung.taskList; package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.Config; import ch.zhaw.gartenverwaltung.Settings;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.io.JsonTaskDatabase;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.io.TaskDatabase;
import ch.zhaw.gartenverwaltung.types.*; import ch.zhaw.gartenverwaltung.types.*;
import java.io.IOException; import java.io.IOException;
@ -12,28 +9,23 @@ import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class TaskListModel { public class GardenSchedule {
private TaskDatabase taskDatabase; private final TaskList taskList;
private PlantDatabase plantDatabase; private final PlantList plantList;
/** /**
* Comparators to create sorted Task List * Comparators to create sorted Task List
*/ */
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate); static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
public TaskListModel(){
taskDatabase = new JsonTaskDatabase();
}
/** /**
* Constructor to create Database Objects. * Constructor to create Database Objects.
*/ */
public TaskListModel(TaskDatabase taskDatabase, PlantDatabase plantDatabase) { public GardenSchedule(TaskList taskList, PlantList plantList) {
this.taskDatabase = taskDatabase; this.taskList = taskList;
this.plantDatabase = plantDatabase; this.plantList = plantList;
} }
/** /**
@ -42,7 +34,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void addTask(Task task) throws IOException { public void addTask(Task task) throws IOException {
taskDatabase.saveTask(task); taskList.saveTask(task);
} }
/** /**
@ -53,8 +45,9 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void planTasksForCrop(Crop crop) throws PlantNotFoundException, HardinessZoneNotSetException, IOException { public void planTasksForCrop(Crop crop) throws PlantNotFoundException, HardinessZoneNotSetException, IOException {
Plant plant = plantDatabase.getPlantById(Config.getCurrentHardinessZone(), crop.getPlantId()).orElseThrow(PlantNotFoundException::new); Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).orElseThrow(PlantNotFoundException::new);
for (GrowthPhase growthPhase : plant.lifecycle()) { int growPhaseGroup = plant.getGrowphaseGroupForDate(crop.getStartDate());
for (GrowthPhase growthPhase : plant.lifecycleForGroup(growPhaseGroup)) {
for (TaskTemplate taskTemplate : growthPhase.taskTemplates()) { for (TaskTemplate taskTemplate : growthPhase.taskTemplates()) {
addTask(taskTemplate.generateTask(crop.getStartDate(), crop.getCropId().orElse(0L))); addTask(taskTemplate.generateTask(crop.getStartDate(), crop.getCropId().orElse(0L)));
} }
@ -67,7 +60,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void removeTasksForCrop(long cropId) throws IOException { public void removeTasksForCrop(long cropId) throws IOException {
taskDatabase.removeTasksForCrop(cropId); taskList.removeTasksForCrop(cropId);
} }
/** /**
@ -76,7 +69,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public void removeTask(Task task) throws IOException { public void removeTask(Task task) throws IOException {
taskDatabase.removeTask(task); taskList.removeTask(task);
} }
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) { private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
@ -146,7 +139,7 @@ public class TaskListModel {
List<List<Task>> dayTaskList = new ArrayList<>(); List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) { for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i); LocalDate date = LocalDate.now().plusDays(i);
dayTaskList.add(taskDatabase.getTaskList(date, date)); dayTaskList.add(taskList.getTaskList(date, date));
} }
return dayTaskList; return dayTaskList;
} }
@ -160,7 +153,7 @@ public class TaskListModel {
List<List<Task>> dayTaskList = new ArrayList<>(); List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) { for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i); LocalDate date = LocalDate.now().plusDays(i);
dayTaskList.add(filterListByCrop(taskDatabase.getTaskList(date, date), cropId)); dayTaskList.add(filterListByCrop(taskList.getTaskList(date, date), cropId));
} }
return dayTaskList; return dayTaskList;
} }
@ -173,7 +166,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Task> getFilteredTaskList(LocalDate start, LocalDate end) throws IOException { public List<Task> getFilteredTaskList(LocalDate start, LocalDate end) throws IOException {
return getSortedTaskList(taskDatabase.getTaskList(start, end), sortByStartDate); return getSortedTaskList(taskList.getTaskList(start, end), sortByStartDate);
} }
/** /**

View File

@ -1,8 +1,7 @@
package ch.zhaw.gartenverwaltung.plantList; package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType; import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Plant;
@ -16,7 +15,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class PlantListModel { public class PlantListModel {
private PlantDatabase plantDatabase; private final PlantList plantList;
private HardinessZone currentZone; private HardinessZone currentZone;
/** /**
@ -28,18 +27,13 @@ public class PlantListModel {
/** /**
* Constructor to create Database Object. * Constructor to create Database Object.
*/ */
public PlantListModel() { public PlantListModel(PlantList plantList) {
plantDatabase = new JsonPlantDatabase(); this.plantList = plantList;
setDefaultZone(); setDefaultZone();
} }
public PlantListModel(PlantDatabase plantDatabase){ private void setDefaultZone() {
this.plantDatabase = plantDatabase; currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Settings
setDefaultZone();
}
private void setDefaultZone(){
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config
} }
public void setCurrentZone(HardinessZone currentZone) { public void setCurrentZone(HardinessZone currentZone) {
@ -52,6 +46,7 @@ public class PlantListModel {
/** /**
* Method to get actual Plant List in alphabetic Order * Method to get actual Plant List in alphabetic Order
*
* @return actual Plant List in alphabetic Order * @return actual Plant List in alphabetic Order
*/ */
public List<Plant> getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException { public List<Plant> getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException {
@ -62,23 +57,25 @@ public class PlantListModel {
/** /**
* Method to get the actual Plant list in custom Order * Method to get the actual Plant list in custom Order
* @param zone selected hardiness zone *
* @param zone selected hardiness zone
* @param comparator comparator to sort the list * @param comparator comparator to sort the list
* @return sorted list with plants in the given hardiness zone * @return sorted list with plants in the given hardiness zone
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
*/ */
public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException { public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException {
setCurrentZone(zone); setCurrentZone(zone);
return plantDatabase.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList()); return plantList.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList());
} }
/** /**
* Method to get Filtered plant list * Method to get Filtered plant list
*
* @param predicate predicate to filter the list * @param predicate predicate to filter the list
* @param zone selected hardiness zone * @param zone selected hardiness zone
* @return filterd list with plants in the hardinness zone * @return filterd list with plants in the hardinness zone
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
*/ */
public List<Plant> getFilteredPlantList(HardinessZone zone, Predicate<Plant> predicate) throws HardinessZoneNotSetException, IOException { public List<Plant> getFilteredPlantList(HardinessZone zone, Predicate<Plant> predicate) throws HardinessZoneNotSetException, IOException {
@ -88,76 +85,103 @@ public class PlantListModel {
/** /**
* Method to get Filtered plant list by id by exact match * Method to get Filtered plant list by id by exact match
*
* @param zone selected hardiness zone * @param zone selected hardiness zone
* @param id id of plant * @param id id of plant
* @return if id doesn't exist: empty List, else list with 1 plant entry. * @return if id doesn't exist: empty List, else list with 1 plant entry.
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
*/ */
public List<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException { public List<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException {
setCurrentZone(zone); setCurrentZone(zone);
List<Plant> plantList = new ArrayList<>(); List<Plant> plantList = new ArrayList<>();
plantDatabase.getPlantById(zone, id).ifPresent(plantList::add); this.plantList.getPlantById(zone, id).ifPresent(plantList::add);
return plantList; return plantList;
} }
/** /**
* * @param zone selected hardiness zone
* @param zone selected hardiness zone
* @param searchString the string to search plant List, set '#' as first char the search by id. * @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 * @return List of plants found in Plant List which contain the search String in the name or description
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Plant> getFilteredPlantListByString(HardinessZone zone, String searchString) throws HardinessZoneNotSetException, IOException { public List<Plant> getFilteredPlantListByString(HardinessZone zone, String searchString) throws HardinessZoneNotSetException, IOException {
if(searchString.charAt(0) == '#') { if (searchString.length() == 0) {
try { return getPlantList(zone);
return getFilteredPlantListById(zone, Long.parseLong(searchString.substring(1))); } else if (searchString.charAt(0) == '#') {
} catch (NumberFormatException e) { if (isPositiveIntegral(searchString.substring(1))) {
Long searchId = Long.parseLong(searchString.substring(1));
return getFilteredPlantListById(zone, searchId);
} else {
return new ArrayList<>(); return new ArrayList<>();
} }
} else { } 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 type GrowPhaseType to filter
* @param zone selected hardiness zone * @param zone selected hardiness zone
* @param from the earliest date to for the filter * @param from the earliest date to for the filter
* @param to the lastest date for the filter * @param to the lastest date for the filter
* @return List of Plants with selected saison * @return List of Plants with selected saison
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
private List<Plant> getFilteredPlantListBySaison(GrowthPhaseType type, HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException { private List<Plant> getFilteredPlantListBySaison(GrowthPhaseType type, 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) && growthPhase.type() == type)); return getFilteredPlantList(zone, plant -> plant.lifecycle().stream().anyMatch(growthPhase -> growthPhase.startDate().compareTo(from) >= 0 && (growthPhase.startDate().compareTo(to) <= 0) && growthPhase.type() == type));
} }
/** /**
*
* @param zone selected hardiness zone * @param zone selected hardiness zone
* @param from the earliest date to for the filter * @param from the earliest date to for the filter
* @param to the lastest date for the filter * @param to the lastest date for the filter
* @return List of Plants with selected saison * @return List of Plants with selected saison
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Plant> getFilteredPlantListByPlantingSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException { public List<Plant> getFilteredPlantListByPlantingSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
return getFilteredPlantListBySaison(GrowthPhaseType.PLANT, zone, from, to); return getFilteredPlantListBySaison(GrowthPhaseType.PLANT, zone, from, to);
} }
/** /**
*
* @param zone selected hardiness zone * @param zone selected hardiness zone
* @param from the earliest date to for the filter * @param from the earliest date to for the filter
* @param to the lastest date for the filter * @param to the lastest date for the filter
* @return List of Plants with selected saison * @return List of Plants with selected saison
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified * @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Plant> getFilteredPlantListByHarvestSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException { public List<Plant> getFilteredPlantListByHarvestSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
return getFilteredPlantListBySaison(GrowthPhaseType.HARVEST, zone, from, to); 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]+");
}
} }

View File

@ -1,4 +1,4 @@
package ch.zhaw.gartenverwaltung.taskList; package ch.zhaw.gartenverwaltung.models;
public class PlantNotFoundException extends Exception { public class PlantNotFoundException extends Exception {
public PlantNotFoundException() { public PlantNotFoundException() {

View File

@ -1,6 +1,7 @@
package ch.zhaw.gartenverwaltung.types; package ch.zhaw.gartenverwaltung.types;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
public class Crop { public class Crop {
@ -41,6 +42,24 @@ public class Crop {
public LocalDate getStartDate() { return startDate; } public LocalDate getStartDate() { return startDate; }
public double getArea() { return area; } 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 @Override
public String toString() { public String toString() {
return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]", return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]",

View File

@ -3,6 +3,7 @@ package ch.zhaw.gartenverwaltung.types;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.MonthDay;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -19,20 +20,49 @@ public record Plant(
List<Pest> pests, List<Pest> pests,
List<GrowthPhase> lifecycle) { List<GrowthPhase> lifecycle) {
/**
* remove all growthPhase which do not belong to the hardiness zone
* @param zone hardiness zone
*/
public void inZone(HardinessZone zone) { public void inZone(HardinessZone zone) {
lifecycle.removeIf(growthPhase -> !growthPhase.zone().equals(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) { public List<GrowthPhase> lifecycleForGroup(int group) {
return lifecycle.stream() return lifecycle.stream()
.filter(growthPhase -> growthPhase.group() != group) .filter(growthPhase -> growthPhase.group() == group)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) { public int getGrowphaseGroupForDate(LocalDate date) {
return harvestDate.minusDays(timeToHarvest(group)); for(GrowthPhase growthPhase : lifecycle){
MonthDay plantingDate = MonthDay.of(date.getMonth().getValue(), date.getDayOfMonth());
if(plantingDate.isAfter(growthPhase.startDate()) && plantingDate.isBefore(growthPhase.endDate())){
return growthPhase.group();
}
}
return 0; // TODO implement
} }
/**
* get sow date from given harvest day from lifecycle group
* @param harvestDate date of the harvest
* @return LocaleDate of sow date
*/
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate) {
return harvestDate.minusDays(timeToHarvest(lifecycleGroupFromHarvestDate(harvestDate)));
}
/**
* 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) { public int timeToHarvest(int group) {
List<GrowthPhase> activeLifecycle = lifecycleForGroup(group); List<GrowthPhase> activeLifecycle = lifecycleForGroup(group);
GrowthPhase sow = activeLifecycle.stream() GrowthPhase sow = activeLifecycle.stream()
@ -47,4 +77,44 @@ public record Plant(
int currentYear = LocalDate.now().getYear(); int currentYear = LocalDate.now().getYear();
return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear)); return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear));
} }
public int lifecycleGroupFromHarvestDate(LocalDate harvestDate) {
return lifecycle.stream()
.filter(growthPhase -> growthPhase.type().equals(GrowthPhaseType.HARVEST) &&
dateInRange(harvestDate, growthPhase.startDate(), growthPhase.endDate()))
.map(GrowthPhase::group)
.findFirst()
.orElse(0);
}
/**
* Checks if the given {@link LocalDate} is within a {@link GrowthPhase} of the given {@link GrowthPhaseType}
*
* @param date The date to check.
* @param phase The {@link GrowthPhaseType} to match against
* @return Whether the date is within the given {@link GrowthPhaseType}
*/
public boolean isDateInPhase(LocalDate date, GrowthPhaseType phase) {
return lifecycle.stream()
.filter(growthPhase -> growthPhase.type().equals(phase))
.anyMatch(growthPhase -> dateInRange(date, growthPhase.startDate(), growthPhase.endDate()));
}
/**
* Checks if the given {@link LocalDate} is within the given {@link MonthDay} range.
* (regardless of year)
*
* @param subject The date to check
* @param min The start of the date-range
* @param max The end of the date-range
* @return Whether the subject is within the range.
*/
private boolean dateInRange(LocalDate subject, MonthDay min, MonthDay max) {
return subject.getMonth().compareTo(min.getMonth()) >= 0 &&
subject.getMonth().compareTo(max.getMonth()) <= 0 &&
// if the day is less than the minimum day, the minimum month must not be equal
(subject.getDayOfMonth() >= min.getDayOfMonth() || !subject.getMonth().equals(min.getMonth())) &&
// if the day is greater than the maximum day, the maximum month must not be equal
(subject.getDayOfMonth() <= max.getDayOfMonth() || !subject.getMonth().equals(max.getMonth()));
}
} }

View File

@ -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;
}
}

View File

@ -21,11 +21,10 @@ public class Task {
* default constructor * default constructor
* (used by Json deserializer) * (used by Json deserializer)
*/ */
public Task(long cropId){ public Task(){
name= ""; name= "";
description= ""; description= "";
startDate = LocalDate.now(); startDate = LocalDate.now();
this.cropId = cropId;
} }
public Task(String name, String description, LocalDate startDate, long cropId) { public Task(String name, String description, LocalDate startDate, long cropId) {

View File

@ -4,10 +4,13 @@ module ch.zhaw.gartenverwaltung {
requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310; requires com.fasterxml.jackson.datatype.jsr310;
requires com.fasterxml.jackson.datatype.jdk8; requires com.fasterxml.jackson.datatype.jdk8;
requires java.logging;
opens ch.zhaw.gartenverwaltung to javafx.fxml; opens ch.zhaw.gartenverwaltung to javafx.fxml;
opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind; opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind;
exports ch.zhaw.gartenverwaltung; exports ch.zhaw.gartenverwaltung;
exports ch.zhaw.gartenverwaltung.io;
exports ch.zhaw.gartenverwaltung.types; exports ch.zhaw.gartenverwaltung.types;
exports ch.zhaw.gartenverwaltung.models;
exports ch.zhaw.gartenverwaltung.json; exports ch.zhaw.gartenverwaltung.json;
} }

View File

@ -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>

View File

@ -11,8 +11,8 @@
<children> <children>
<Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="121.0" text="Home" HBox.hgrow="NEVER" /> <Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="121.0" text="Home" HBox.hgrow="NEVER" />
<Button fx:id="plants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToPlants" prefHeight="38.0" prefWidth="121.0" text="Plants" /> <Button fx:id="plants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToPlants" prefHeight="38.0" prefWidth="121.0" text="Plants" />
<Button fx:id="myPlants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="MyPlants" /> <Button fx:id="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
<Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="MySchedule" /> <Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="My Schedule" />
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" /> <Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
</children> </children>
</HBox> </HBox>

View File

@ -7,13 +7,11 @@
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?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.MyGardenController" fx:id="myGardenRoot">
<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> <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"> <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> <children>
<Label text="MyPlants"> <Label text="My Garden">
<font> <font>
<Font name="System Bold" size="28.0" /> <Font name="System Bold" size="28.0" />
</font> </font>
@ -21,7 +19,7 @@
<Insets bottom="10.0" /> <Insets bottom="10.0" />
</VBox.margin> </VBox.margin>
</Label> </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"> <Button fx:id="addPlant_button" mnemonicParsing="false" onAction="#addPlant" prefHeight="45.0" prefWidth="155.0" text="Add new Plant">
<VBox.margin> <VBox.margin>
<Insets top="10.0" /> <Insets top="10.0" />

View File

@ -1,23 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.geometry.Rectangle2D?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?> <?import javafx.scene.control.ListView?>
<?import javafx.scene.control.SplitPane?> <?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextField?> <?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?> <?import javafx.scene.control.TitledPane?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?> <?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?> <?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0" prefWidth="1219.0" <AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.PlantsController"> prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.zhaw.gartenverwaltung.PlantsController"
fx:id="plantsRoot">
<children> <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"> <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> <items>
@ -30,18 +29,11 @@
<Font name="System Bold" size="30.0" /> <Font name="System Bold" size="30.0" />
</font> </font>
</Label> </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"> <HBox alignment="CENTER_LEFT" prefHeight="480.0" prefWidth="881.0" VBox.vgrow="ALWAYS">
<children> <children>
<ListView fx:id="list_plants" maxWidth="1.7976931348623157E308" prefHeight="497.0" prefWidth="580.0" HBox.hgrow="ALWAYS" /> <ListView fx:id="list_plants" maxWidth="1.7976931348623157E308" prefHeight="497.0" prefWidth="400.0" HBox.hgrow="ALWAYS" />
<ImageView fx:id="img_plant" fitHeight="322.0" fitWidth="861.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER"> <ImageView fx:id="img_plant" fitWidth="300" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
<viewport>
<Rectangle2D height="300.0" width="300.0" />
</viewport>
<image>
<Image url="@placeholder.png" />
</image>
</ImageView>
</children> </children>
</HBox> </HBox>
<Label prefHeight="33.0" prefWidth="919.0" text="Plant Information:"> <Label prefHeight="33.0" prefWidth="919.0" text="Plant Information:">
@ -57,7 +49,7 @@
<Insets bottom="10.0" top="10.0" /> <Insets bottom="10.0" top="10.0" />
</padding> </padding>
</Label> </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> </children>
</VBox> </VBox>
</children> </children>
@ -75,30 +67,7 @@
<content> <content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children> <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"> <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>
<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>
</children> </children>
</AnchorPane> </AnchorPane>
</content> </content>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.DatePicker?>
<?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>
<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" toggleGroup="$phase_group">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
<toggleGroup>
<ToggleGroup fx:id="phase_group" />
</toggleGroup>
</RadioButton>
<RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$phase_group" />
</children>
<HBox.margin>
<Insets top="10.0" />
</HBox.margin>
</VBox>
<DatePicker fx:id="datepicker" />
</children>
</HBox>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
</children>
</AnchorPane>

View File

@ -31,7 +31,7 @@
"name": "Germinate", "name": "Germinate",
"relativeStartDate": -14, "relativeStartDate": -14,
"relativeEndDate": null, "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, "interval": null,
"isOptional": false "isOptional": false
} }
@ -53,7 +53,7 @@
"name": "hilling", "name": "hilling",
"relativeStartDate": 0, "relativeStartDate": 0,
"relativeEndDate": null, "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, "interval": 21,
"isOptional": false "isOptional": false
} }
@ -94,6 +94,7 @@
"endDate": "03-10", "endDate": "03-10",
"zone": "ZONE_8A", "zone": "ZONE_8A",
"type": "SOW", "type": "SOW",
"group": 0,
"wateringCycle": { "wateringCycle": {
"litersPerSqM": 15, "litersPerSqM": 15,
"interval": 3, "interval": 3,
@ -115,6 +116,7 @@
"endDate": "05-10", "endDate": "05-10",
"zone": "ZONE_8A", "zone": "ZONE_8A",
"type": "PLANT", "type": "PLANT",
"group": 0,
"wateringCycle": { "wateringCycle": {
"litersPerSqM": 25, "litersPerSqM": 25,
"interval": 3, "interval": 3,
@ -138,6 +140,7 @@
"endDate": "05-20", "endDate": "05-20",
"zone": "ZONE_8A", "zone": "ZONE_8A",
"type": "HARVEST", "type": "HARVEST",
"group": 0,
"wateringCycle": { "wateringCycle": {
"litersPerSqM": 0, "litersPerSqM": 0,
"interval": null, "interval": null,

View File

@ -6,7 +6,7 @@
"startDate" : "2022-05-01", "startDate" : "2022-05-01",
"endDate" : "2022-05-01", "endDate" : "2022-05-01",
"interval" : 0, "interval" : 0,
"cropID" : 0 "cropId" : 0
}, },
{ {
"id" : 2, "id" : 2,
@ -15,7 +15,7 @@
"startDate" : "2022-05-01", "startDate" : "2022-05-01",
"endDate" : "2022-09-01", "endDate" : "2022-09-01",
"interval" : 2, "interval" : 2,
"cropID" : 0 "cropId" : 0
}, },
{ {
"id" : 3, "id" : 3,
@ -24,7 +24,7 @@
"startDate" : "2022-06-01", "startDate" : "2022-06-01",
"endDate" : "2022-08-01", "endDate" : "2022-08-01",
"interval" : 28, "interval" : 28,
"cropID" : 0 "cropId" : 0
}, },
{ {
"id" : 4, "id" : 4,
@ -33,7 +33,7 @@
"startDate" : "2022-07-01", "startDate" : "2022-07-01",
"endDate" : "2022-07-01", "endDate" : "2022-07-01",
"interval" : 0, "interval" : 0,
"cropID" : 0 "cropId" : 0
}, },
{ {
"id" : 5, "id" : 5,
@ -42,7 +42,7 @@
"startDate" : "2022-05-01", "startDate" : "2022-05-01",
"endDate" : "2022-09-01", "endDate" : "2022-09-01",
"interval" : 5, "interval" : 5,
"cropID" : 0 "cropId" : 0
}, },
{ {
"id" : 6, "id" : 6,
@ -51,6 +51,6 @@
"startDate" : "2022-09-01", "startDate" : "2022-09-01",
"endDate" : "2022-09-01", "endDate" : "2022-09-01",
"interval" : 0, "interval" : 0,
"cropID" : 0 "cropId" : 0
} }
] ]

View File

@ -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 JsonCropListTest {
private CropList 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 JsonCropList(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);
}
}
}

View File

@ -8,25 +8,23 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class JsonPlantDatabaseTest { public class JsonPlantListTest {
PlantDatabase testDatabase; PlantList testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach @BeforeEach
void connectToDb() { void connectToDb() {
testDatabase = new JsonPlantDatabase(); testDatabase = new JsonPlantList();
} }
@Test @Test
@DisplayName("Check if results are retrieved completely") @DisplayName("Check if results are retrieved completely")
void getPlantList() { void getPlantListNotEmpty() {
List<Plant> testList; List<Plant> testList;
try { try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_8A); testList = testDatabase.getPlantList(HardinessZone.ZONE_8A);
@ -40,12 +38,26 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals(expected,names); 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 @Test
@DisplayName("Check whether single access works.") @DisplayName("Check whether single access works.")
void getPlantById() { void getPlantByIdAndZone() {
Optional<Plant> testPlant; Optional<Plant> testPlant;
try { try {
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A,1); testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
} catch (IOException | HardinessZoneNotSetException e) { } catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -53,12 +65,23 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals("Early Carrot", testPlant.get().name()); 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 @Test
@DisplayName("Check for a nonexisting plant.") @DisplayName("Check for a nonexisting plant.")
void getPlantByIdMustFail() { void getPlantByIdMustFail() {
Optional<Plant> testPlant; Optional<Plant> testPlant;
try { try {
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A,99); testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 99);
} catch (IOException | HardinessZoneNotSetException e) { } catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -1,47 +0,0 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.*;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class JsonTaskDatabaseTest {
TaskDatabase testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach
void connectToDb() {
// testDatabase = new JsonTaskDatabase();
}
@Test
@DisplayName("Check if results are retrieved completely")
void getTasks(){
/*
List<Task> taskList=null;
try {
taskList = testDatabase.getTaskList(formatter.parse("01.05.2022"), formatter.parse("01.08.2022"));
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
Assertions.assertTrue(taskList.size()>0);
*/
}
@Test
void getTaskForCrop() {
// TODO implement Test
}
@Test
void removeTasksForCrop() {
// TODO implement Test
}
}

View File

@ -0,0 +1,117 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.*;
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.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class JsonTaskListTest {
TaskList testDatabase;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private final URL dbDataSource = this.getClass().getResource("taskdb.json");
private final URL testFile = this.getClass().getResource("test-taskdb.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 JsonTaskList();
}
@Test
@DisplayName("Check if results are retrieved completely")
void getTasks() {
List<Task> taskList = null;
try {
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("31.05.2022", formatter));
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(3, taskList.size());
}
@Disabled("disabled until adding works.")
@Test
@DisplayName("Add task.")
void addTask() {
Task task = new Task("Testtask", "This is a test Task.", LocalDate.parse("01.05.2022", formatter), 1);
try {
testDatabase.saveTask(task);
List<Task> taskList = null;
try {
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("31.05.2022", formatter));
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(4, taskList.size());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
@DisplayName("Remove task.")
void removeTask() {
Task task = new Task("Dummy", "Dummy", LocalDate.parse("31.05.2022", formatter), 1).withId(2);
try {
testDatabase.removeTask(task);
List<Task> taskList = null;
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("31.05.2022", formatter));
Assertions.assertEquals(2, taskList.size());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
void getTaskForCrop() {
List<Task> taskList = null;
try {
taskList = testDatabase.getTaskForCrop(0);
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(6, taskList.size());
}
@Disabled("Disabled until removing works")
@Test
void removeTasksForCrop() {
List<Task> taskList = null;
try {
testDatabase.removeTasksForCrop(0);
taskList = testDatabase.getTaskForCrop(0);
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(0, taskList.size());
}
}

View File

@ -0,0 +1,124 @@
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.BeforeEach;
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.MonthDay;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.*;
public class GardenPlanModelTest {
private final URL dbDataSource = JsonCropList.class.getResource("user-crops.json");
private final URL testFile = JsonCropList.class.getResource("test-user-crops.json");
CropList cropList;
List<Crop> exampleCrops;
Crop exampleCropOnion;
Crop exampleCropCarrot;
Crop exampleCrop1;
Crop exampleCrop2;
Crop exampleCrop3;
Plant examplePlantOnion;
Plant examplePlantCarrot;
Garden model;
@BeforeEach
void setUp() throws IOException, URISyntaxException {
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,1));
exampleCrop3.withId(2);
exampleCrop3.withArea(1.0);
exampleCrops = new ArrayList<>();
exampleCrops.add(exampleCrop1);
exampleCrops.add(exampleCrop2);
exampleCrops.add(exampleCrop3);
cropList = mockCropList(exampleCrops);
// Reset Crop "database" before test
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
CropList testDatabase = new JsonCropList(dbDataSource);
GardenSchedule gardenSchedule = mock(GardenSchedule.class);
model = new Garden(gardenSchedule, testDatabase);
}
CropList mockCropList(List<Crop> cropList) throws IOException {
CropList croplist = mock(CropList.class);
when(croplist.getCrops()).thenReturn(cropList);
when(croplist.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
when(croplist.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
return croplist;
}
@Test
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
Optional<Crop> exampleCrop = model.getCrop(2L);
assertTrue(exampleCrop.isPresent());
assertEquals(model.getCrops().get(2), exampleCrop.get());
}
@Test
void removeCrop() throws IOException {
exampleCrop1.withId(2);
exampleCrop1.withArea(1.5);
model.removeCrop(exampleCrop1);
assertEquals(2,model.getCrops().size());
}
}

View File

@ -0,0 +1,197 @@
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class GardenScheduleTest {
LocalDate exampleStartDate = LocalDate.of(2020, 02, 02);
TaskList taskList;
PlantList plantList;
List<Plant> examplePlantList;
List<Task> exampleTaskList;
List<TaskTemplate> exampleTaskTemplateList;
GardenSchedule model;
@BeforeEach
void setUp() throws IOException, HardinessZoneNotSetException {
createExampleTaskList();
createExampleTaskTemplateList();
createExamplePlantList();
taskList = mockTaskDatabase(exampleTaskList);
plantList = mockPlantDatabase(examplePlantList);
model = new GardenSchedule(taskList, plantList);
}
PlantList mockPlantDatabase(List<Plant> plantList) throws HardinessZoneNotSetException, IOException {
PlantList plantDatabase = mock(JsonPlantList.class);
when(plantDatabase.getPlantList(HardinessZone.ZONE_8A)).thenReturn(plantList);
when(plantDatabase.getPlantById(HardinessZone.ZONE_8A,20)).thenReturn(Optional.of(examplePlantList.get(0)));
return plantDatabase;
}
private TaskList mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
TaskList taskList = mock(JsonTaskList.class);
when(taskList.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
when(taskList.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
List<Task> pastTasks = new ArrayList<>();
pastTasks.add(exampleTaskList.get(0));
pastTasks.add(exampleTaskList.get(2));
pastTasks.add(exampleTaskList.get(4));
when(taskList.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
when(taskList.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
when(taskList.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
when(taskList.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
return taskList;
}
void createExampleTaskTemplateList(){
exampleTaskTemplateList = new ArrayList<>();
exampleTaskTemplateList.add(mock(TaskTemplate.class));
exampleTaskTemplateList.add(mock(TaskTemplate.class));
exampleTaskTemplateList.add(mock(TaskTemplate.class));
exampleTaskTemplateList.add(mock(TaskTemplate.class));
when(exampleTaskTemplateList.get(0).generateTask(exampleStartDate, 30)).thenReturn(exampleTaskList.get(0));
when(exampleTaskTemplateList.get(1).generateTask(exampleStartDate, 30)).thenReturn(exampleTaskList.get(1));
when(exampleTaskTemplateList.get(2).generateTask(exampleStartDate, 30)).thenReturn(exampleTaskList.get(2));
when(exampleTaskTemplateList.get(3).generateTask(exampleStartDate, 30)).thenReturn(exampleTaskList.get(3));
}
void createExamplePlantList(){
examplePlantList = new ArrayList<>();
examplePlantList.add(new Plant(
20,
"summertime onion",
"Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
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, List.of(
exampleTaskTemplateList.get(0),
exampleTaskTemplateList.get(1)
)),
new GrowthPhase(MonthDay.of(4, 3), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, List.of(
exampleTaskTemplateList.get(2),
exampleTaskTemplateList.get(3)
))
)));
}
void createExampleTaskList() {
exampleTaskList = new ArrayList<>();
exampleTaskList.add(new Task("name", "description", LocalDate.now().minusDays(1), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.now().plusDays(1), 2L));
exampleTaskList.add(new Task("name", "description", LocalDate.now(), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.of(9019, 5, 5), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.of(2019, 5, 5), 2L));
}
@Test
void addTask() throws IOException {
Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
model.addTask(taskToAdd);
verify(taskList, times(1)).saveTask(taskToAdd);
}
@Test
void removeTask() throws IOException {
Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
model.removeTask(taskToRemove);
verify(taskList, times(1)).removeTask(taskToRemove);
}
@Test
void getTaskList() throws IOException {
List<Task> listToCheck = model.getTaskList();
assertEquals(5, listToCheck.size());
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
assertEquals(exampleTaskList.get(1), listToCheck.get(3));
assertEquals(exampleTaskList.get(3), listToCheck.get(4));
}
@Test
void getFutureTasks() throws IOException {
List<Task> listToCheck = model.getFutureTasks();
assertEquals(3, listToCheck.size());
assertEquals(exampleTaskList.get(2), listToCheck.get(0));
assertEquals(exampleTaskList.get(1), listToCheck.get(1));
assertEquals(exampleTaskList.get(3), listToCheck.get(2));
}
@Test
void getPastTasks() throws IOException {
List<Task> listToCheck = model.getPastTasks();
assertEquals(3, listToCheck.size());
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
}
@Test
void getTasksUpcomingWeek() throws IOException {
List<List<Task>> dayList = model.getTasksUpcomingWeek();
assertEquals(7, dayList.size());
//Check day 0
assertEquals(1, dayList.get(0).size());
assertEquals(exampleTaskList.get(2), dayList.get(0).get(0));
//Check day 1
assertEquals(1, dayList.get(1).size());
assertEquals(exampleTaskList.get(1), dayList.get(1).get(0));
//Check day 2
assertEquals(0, dayList.get(2).size());
//Check day 3
assertEquals(0, dayList.get(3).size());
//Check day 4
assertEquals(0, dayList.get(4).size());
//Check day 5
assertEquals(0, dayList.get(5).size());
//Check day 6
assertEquals(0, dayList.get(6).size());
}
@Test
void removeTasksForCrop() throws IOException {
model.removeTasksForCrop(1L);
verify(taskList, times(1)).removeTasksForCrop(1L);
}
@Test
void planTasksForCrop() throws HardinessZoneNotSetException, PlantNotFoundException, IOException {
model.planTasksForCrop(new Crop(20, exampleStartDate).withId(30));
verify(exampleTaskTemplateList.get(0), times(1)).generateTask(exampleStartDate, 30);
verify(exampleTaskTemplateList.get(1), times(1)).generateTask(exampleStartDate, 30);
verify(exampleTaskTemplateList.get(2), times(1)).generateTask(exampleStartDate, 30);
verify(exampleTaskTemplateList.get(3), times(1)).generateTask(exampleStartDate, 30);
verify(taskList, times(1)).saveTask(exampleTaskList.get(0));
verify(taskList, times(1)).saveTask(exampleTaskList.get(1));
verify(taskList, times(1)).saveTask(exampleTaskList.get(2));
verify(taskList, times(1)).saveTask(exampleTaskList.get(3));
}
}

View File

@ -1,8 +1,9 @@
package ch.zhaw.gartenverwaltung.plantList; package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException; import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase; import ch.zhaw.gartenverwaltung.io.JsonPlantList;
import ch.zhaw.gartenverwaltung.io.PlantDatabase; import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.types.*; import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -22,15 +23,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
class PlantListModelTest { class PlantListModelTest {
PlantDatabase plantDatabase; PlantList plantList;
List<Plant> examplePlantList; List<Plant> examplePlantList;
PlantListModel model; PlantListModel model;
@BeforeEach @BeforeEach
void setUp() throws HardinessZoneNotSetException, IOException { void setUp() throws HardinessZoneNotSetException, IOException {
createExamplePlantList(); createExamplePlantList();
plantDatabase = mockPlantDatabase(examplePlantList); plantList = mockPlantDatabase(examplePlantList);
model = new PlantListModel(plantDatabase); model = new PlantListModel(plantList);
} }
@AfterEach @AfterEach
@ -79,8 +80,8 @@ class PlantListModelTest {
); );
} }
PlantDatabase mockPlantDatabase(List<Plant> plantList) throws HardinessZoneNotSetException, IOException { PlantList mockPlantDatabase(List<Plant> plantList) throws HardinessZoneNotSetException, IOException {
PlantDatabase plantDatabase = mock(JsonPlantDatabase.class); PlantList plantDatabase = mock(JsonPlantList.class);
when(plantDatabase.getPlantList(HardinessZone.ZONE_8A)).thenReturn(plantList); when(plantDatabase.getPlantList(HardinessZone.ZONE_8A)).thenReturn(plantList);
when(plantDatabase.getPlantList(HardinessZone.ZONE_1A)).thenReturn(new ArrayList<>()); when(plantDatabase.getPlantList(HardinessZone.ZONE_1A)).thenReturn(new ArrayList<>());
when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 0)).thenReturn(Optional.of(plantList.get(1))); when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 0)).thenReturn(Optional.of(plantList.get(1)));
@ -173,6 +174,8 @@ class PlantListModelTest {
assertEquals(examplePlantList.get(1), plantListResult.get(0)); assertEquals(examplePlantList.get(1), plantListResult.get(0));
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "apple"); plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "apple");
assertEquals(0, plantListResult.size()); assertEquals(0, plantListResult.size());
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "");
assertEquals(3, plantListResult.size());
} }
@Test @Test

View File

@ -1,158 +0,0 @@
package ch.zhaw.gartenverwaltung.taskList;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class TaskListModelTest {
TaskDatabase taskDatabase;
PlantDatabase plantDatabase;
List<Task> exampleTaskList;
Map<Long, Plant> examplePlantMap;
TaskListModel model;
@BeforeEach
void setUp() throws IOException {
createExampleTaskList();
taskDatabase = mockTaskDatabase(exampleTaskList);
plantDatabase = mockPlantDatabase(examplePlantMap);
model = new TaskListModel(taskDatabase, plantDatabase);
}
private TaskDatabase mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
List<Task> pastTasks = new ArrayList<>();
pastTasks.add(exampleTaskList.get(0));
pastTasks.add(exampleTaskList.get(2));
pastTasks.add(exampleTaskList.get(4));
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
when(taskDatabase.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
when(taskDatabase.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
return taskDatabase;
}
private PlantDatabase mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
return new PlantDatabase() {
@Override
public List<Plant> getPlantList(HardinessZone zone) {
return null;
}
@Override
public Optional<Plant> getPlantById(HardinessZone zone, long id) {
return Optional.ofNullable(examplePlantMap.get(id));
}
};
}
void createExampleTaskList() {
exampleTaskList = new ArrayList<>();
exampleTaskList.add(new Task("name", "description", LocalDate.now().minusDays(1), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.now().plusDays(1), 2L));
exampleTaskList.add(new Task("name", "description", LocalDate.now(), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.of(9019, 5, 5), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.of(2019, 5, 5), 2L));
}
void createExamplePlantMap() {
examplePlantMap = new HashMap<>();
}
@Test
void addTask() throws IOException {
Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
model.addTask(taskToAdd);
verify(taskDatabase, times(1)).saveTask(taskToAdd);
}
@Test
void removeTask() throws IOException {
Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
model.removeTask(taskToRemove);
verify(taskDatabase, times(1)).removeTask(taskToRemove);
}
@Test
void getTaskList() throws IOException {
List<Task> listToCheck = model.getTaskList();
assertEquals(5, listToCheck.size());
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
assertEquals(exampleTaskList.get(1), listToCheck.get(3));
assertEquals(exampleTaskList.get(3), listToCheck.get(4));
}
@Test
void getFutureTasks() throws IOException {
List<Task> listToCheck = model.getFutureTasks();
assertEquals(3, listToCheck.size());
assertEquals(exampleTaskList.get(2), listToCheck.get(0));
assertEquals(exampleTaskList.get(1), listToCheck.get(1));
assertEquals(exampleTaskList.get(3), listToCheck.get(2));
}
@Test
void getPastTasks() throws IOException {
List<Task> listToCheck = model.getPastTasks();
assertEquals(3, listToCheck.size());
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
}
@Test
void getTasksUpcomingWeek() throws IOException {
List<List<Task>> dayList = model.getTasksUpcomingWeek();
assertEquals(7, dayList.size());
//Check day 0
assertEquals(1, dayList.get(0).size());
assertEquals(exampleTaskList.get(2), dayList.get(0).get(0));
//Check day 1
assertEquals(1, dayList.get(1).size());
assertEquals(exampleTaskList.get(1), dayList.get(1).get(0));
//Check day 2
assertEquals(0, dayList.get(2).size());
//Check day 3
assertEquals(0, dayList.get(3).size());
//Check day 4
assertEquals(0, dayList.get(4).size());
//Check day 5
assertEquals(0, dayList.get(5).size());
//Check day 6
assertEquals(0, dayList.get(6).size());
}
@Test
void removeTasksForCrop() throws IOException {
model.removeTasksForCrop(1L);
verify(taskDatabase, times(1)).removeTasksForCrop(1L);
}
}

View File

@ -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"
}
]
}
]

View File

@ -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
}
]

View File

@ -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"
}
]
}
]

View File

@ -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"
}
]

View File

@ -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
}
]

View File

@ -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
}
]

View File

@ -0,0 +1,2 @@
[
]