Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0959f535b | ||
|
|
c79386ec88 | ||
|
|
f36826ef29 | ||
|
|
c9ba9c1234 | ||
|
|
04e4ea1dea | ||
|
|
1f049f86ab | ||
|
|
9b08113ff9 | ||
|
|
f389cfcdfd | ||
|
|
e22cb0b24d | ||
|
|
1c83fc4694 | ||
|
|
df19edb25a | ||
|
|
6b7a6f095d | ||
|
|
b2c0886e46 | ||
|
|
775e35a70b | ||
|
|
d7b9095050 | ||
|
|
bcb36b89c7 | ||
|
|
f7105f041c | ||
|
|
b0369e3174 | ||
|
|
8c20dcaadb | ||
|
|
95b0f7e13d | ||
|
|
2312149256 | ||
|
|
ffc7f5174e | ||
|
|
8d3fbc06ad | ||
|
|
dcb97f1c55 | ||
|
|
ee67c83b22 | ||
|
|
2963872237 | ||
|
|
7920bdff28 | ||
|
|
e96280cd0c | ||
|
|
bfe3fcfb79 | ||
|
|
8e3af2ba32 | ||
|
|
b70a758099 | ||
|
|
3a69119eb7 | ||
|
|
165dc6d901 | ||
|
|
384dc2d853 | ||
|
|
2f69c48800 | ||
|
|
86a9eeaf2e | ||
|
|
9ba252b828 | ||
|
|
90d2de65de | ||
|
|
05e7bcc2e8 | ||
|
|
09e582b8a2 | ||
|
|
2b7cec7e6a | ||
|
|
5ef3f6c587 | ||
|
|
15279838b7 | ||
|
|
4f80a0a3e0 | ||
|
|
a5e1acc7c3 | ||
|
|
a8efa8fc2b | ||
|
|
6737e67cda | ||
|
|
a7fa58344c | ||
|
|
6523e1d791 | ||
|
|
c653005652 | ||
|
|
bee517317a | ||
|
|
2be9df6094 | ||
|
|
5faf61089a | ||
|
|
8e23124c6b | ||
|
|
78a27499a8 | ||
|
|
0e40bc6304 | ||
|
|
ced2645bd7 | ||
|
|
802f238d69 | ||
|
|
096abfd148 | ||
|
|
590049b9cf | ||
|
|
ad05e9e95a | ||
|
|
60c6dcd0d9 | ||
|
|
52ae2b02bc | ||
|
|
00db602904 | ||
|
|
5e206ace39 | ||
|
|
ea4a1ecc6a | ||
|
|
b7d08944a6 | ||
|
|
3077e02b32 | ||
|
|
96fdc64105 | ||
|
|
e92538fbb4 | ||
|
|
2cf7f55215 | ||
|
|
f6ec411b7a | ||
|
|
8408af175d | ||
|
|
e91773b360 | ||
|
|
2361afd537 | ||
|
|
38288f8561 | ||
|
|
a43a23427c | ||
|
|
d45b8e116e | ||
|
|
fb0c50a715 | ||
|
|
83bc011870 | ||
|
|
ce93531ab8 | ||
|
|
efc95dbcc6 | ||
|
|
eea530931e | ||
|
|
541217c281 | ||
|
|
bf5c5c8439 | ||
|
|
5bfebdc92c | ||
|
|
0e4e207581 | ||
|
|
82eab6d5cd | ||
|
|
5411ac69ae | ||
|
|
146c43d5d9 | ||
|
|
160880d4f5 | ||
|
|
ceb448c1ff | ||
|
|
8c651aec2b | ||
|
|
56fac84e12 | ||
|
|
8b3a0c1570 | ||
|
|
516a185546 |
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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 {
|
||||||
@@ -57,4 +67,4 @@ jlink {
|
|||||||
|
|
||||||
jlinkZip {
|
jlinkZip {
|
||||||
group = 'distribution'
|
group = 'distribution'
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 286 KiB |
@@ -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><<Interface>>
|
||||||
|
PlantDatabase
|
||||||
|
--
|
||||||
|
+ getPlantList(zone: HardinessZone): List<Plant>
|
||||||
|
+ getPlantById(zone: HardinessZone id: long): Optional<Plant></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><<Record>>
|
||||||
|
Plant
|
||||||
|
--
|
||||||
|
+ id: long
|
||||||
|
+ name: String
|
||||||
|
+ description: String
|
||||||
|
+ spacing: int
|
||||||
|
+ lifecycle: List<GrowthPhase>
|
||||||
|
--
|
||||||
|
+ 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<Task>
|
||||||
|
- taskDb: TaskDatabase
|
||||||
|
--
|
||||||
|
+ getTask(id: long): Optional<Task>
|
||||||
|
+ 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<Crop>
|
||||||
|
- 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<int>
|
||||||
|
+ getEndDate(): Optional<Date></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><<Interface>>
|
||||||
|
TaskDatabase
|
||||||
|
--
|
||||||
|
+ getTaskList(start: Date, end: Date): List<Task>
|
||||||
|
+ 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><<Interface>>
|
||||||
|
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><<Record>>
|
||||||
|
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=<->>>>>
|
||||||
|
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 <<Record>> 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=<.
|
||||||
|
m1=*
|
||||||
|
m2=1
|
||||||
|
returns ></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=<-
|
||||||
|
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<Long></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=<.
|
||||||
|
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><<Enumeration>>
|
||||||
|
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=<->>>>
|
||||||
|
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=<->>>></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=<.
|
||||||
|
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=<->>>></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><<Interface>>
|
||||||
|
GardenPlan
|
||||||
|
--
|
||||||
|
+ getPlantings(): List<UserPlanting>
|
||||||
|
+ 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=<.
|
||||||
|
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=<.</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=<-
|
||||||
|
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=<-
|
||||||
|
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=<<-</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=<<-</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=<<-</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=<.</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=<.</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=<.</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=<.</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=<.</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=<-
|
||||||
|
m1=1\nexternal\ndatabase
|
||||||
|
m2=1\ninternal\nlist
|
||||||
|
accesses ></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 ></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=<->>>>>
|
||||||
|
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=<.
|
||||||
|
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=<-
|
||||||
|
delegates generating Tasks</panel_attributes><additional_attributes>380;20;10;20</additional_attributes></element></diagram>
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
|
||||||
|
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.beans.property.ListProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AppLoader appLoader;
|
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
|
||||||
|
private final ListProperty<Task> taskListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
private final ListProperty<Pest> pestListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
|
||||||
|
@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 Label location_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label light_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button location_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label soil_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label spacing_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button addTask_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<Task> taskList_listView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<Pest> pests_listView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void addTask() throws IOException {
|
||||||
|
createTaskDialog(true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close Window
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void goBack() {
|
||||||
|
Stage stage = (Stage) imageView.getScene().getWindow();
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open dialog to set area
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void setArea() throws IOException {
|
||||||
|
openTextFieldDialog("set Text Area", "Text Area", area_label.getText(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open dialog to set location
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void setLocation() throws IOException {
|
||||||
|
openTextFieldDialog("set Location", "Location", location_label.getText(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set labels and image from selected {@link Crop}
|
||||||
|
* set icons for buttons
|
||||||
|
* @param crop {@link Crop} which will be displayed
|
||||||
|
* @throws PlantNotFoundException exception
|
||||||
|
*/
|
||||||
|
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("");
|
||||||
|
|
||||||
|
setTaskListProperty(crop);
|
||||||
|
taskList_listView.itemsProperty().bind(taskListProperty);
|
||||||
|
|
||||||
|
pestListProperty.addAll(plant.pests());
|
||||||
|
pests_listView.itemsProperty().bind(pestListProperty);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (HardinessZoneNotSetException | IOException e) {
|
||||||
|
throw new PlantNotFoundException();
|
||||||
|
}
|
||||||
|
setIconToButton(addTask_button, "addIcon.png");
|
||||||
|
setIconToButton(area_button, "areaIcon.png");
|
||||||
|
setIconToButton(location_button, "locationIcon.png");
|
||||||
|
setCellFactoryPests();
|
||||||
|
setCellFactoryTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCellFactoryTasks() {
|
||||||
|
taskList_listView.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Task task, boolean empty) {
|
||||||
|
super.updateItem(task, empty);
|
||||||
|
|
||||||
|
if (empty || task == null) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText("");
|
||||||
|
setGraphic(createTaskHBox(task));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCellFactoryPests() {
|
||||||
|
pests_listView.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Pest pest, boolean empty) {
|
||||||
|
super.updateItem(pest, empty);
|
||||||
|
|
||||||
|
if (empty || pest == null) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText("");
|
||||||
|
setGraphic(createPestHBox(pest));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTaskListProperty(Crop crop) {
|
||||||
|
crop.getCropId().ifPresent(id -> {
|
||||||
|
List<Task> taskList;
|
||||||
|
try {
|
||||||
|
taskList = gardenSchedule.getTaskListForCrop(id);
|
||||||
|
taskListProperty.clear();
|
||||||
|
taskListProperty.addAll(taskList);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO: Alert
|
||||||
|
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createTaskHBox(Task task) {
|
||||||
|
HBox hBox = new HBox();
|
||||||
|
Label taskName = new Label(task.getName()+": ");
|
||||||
|
taskName.setStyle("-fx-font-weight: bold");
|
||||||
|
Label taskDescription = new Label(task.getDescription());
|
||||||
|
taskDescription.setWrapText(true);
|
||||||
|
taskDescription.setMaxWidth(2000);
|
||||||
|
HBox.setHgrow(taskDescription, Priority.ALWAYS);
|
||||||
|
|
||||||
|
Button edit = new Button();
|
||||||
|
Button delete = new Button();
|
||||||
|
setIconToButton(edit, "editIcon.png");
|
||||||
|
setIconToButton(delete, "deleteIcon.png");
|
||||||
|
edit.setOnAction(getEditTaskEvent(task));
|
||||||
|
delete.setOnAction(deleteTask(task));
|
||||||
|
|
||||||
|
hBox.getChildren().addAll(taskName, taskDescription, edit, delete);
|
||||||
|
return hBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createPestHBox(Pest pest) {
|
||||||
|
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);
|
||||||
|
Button button = new Button("Get Counter Measures");
|
||||||
|
hBox.getChildren().addAll(label, label1, button);
|
||||||
|
return hBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds icon to button
|
||||||
|
* @param button the button which get the icon
|
||||||
|
* @param iconFileName file name of icon
|
||||||
|
*/
|
||||||
|
private void setIconToButton(Button button, String iconFileName) {
|
||||||
|
Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
|
||||||
|
ImageView imageView = new ImageView(img);
|
||||||
|
imageView.setFitHeight(20);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
button.setGraphic(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHandler<ActionEvent> getEditTaskEvent(Task task) {
|
||||||
|
return (event) -> {
|
||||||
|
try {
|
||||||
|
createTaskDialog(false, task);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHandler<ActionEvent> deleteTask(Task task) {
|
||||||
|
return (event) -> {
|
||||||
|
showDeleteTask(task);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTaskDialog(boolean newTask, Task givenTask) throws IOException {
|
||||||
|
Dialog<Task> dialog = new Dialog<>();
|
||||||
|
dialog.setTitle("Set Task");
|
||||||
|
dialog.setHeaderText("Add/Edit Task:");
|
||||||
|
dialog.setResizable(false);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dialog.getDialogPane();
|
||||||
|
|
||||||
|
ButtonType saveTask;
|
||||||
|
if(newTask) {
|
||||||
|
saveTask = new ButtonType("Add", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
} else {
|
||||||
|
saveTask = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
}
|
||||||
|
dialogPane.getButtonTypes().addAll(saveTask, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("TaskFormular.fxml", dialogPane) instanceof TaskFormularController controller) {
|
||||||
|
controller.setCorp(this.crop);
|
||||||
|
controller.initSaveButton((Button) dialogPane.lookupButton(saveTask));
|
||||||
|
if (!newTask) {
|
||||||
|
controller.setTaskValue(givenTask);
|
||||||
|
}
|
||||||
|
dialog.setResultConverter(button -> button.equals(saveTask) ? controller.returnResult() : null);
|
||||||
|
|
||||||
|
dialog.showAndWait()
|
||||||
|
.ifPresent(task -> {
|
||||||
|
if (newTask) {
|
||||||
|
try {
|
||||||
|
gardenSchedule.addTask(task);
|
||||||
|
setTaskListProperty(this.crop);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//ToDo method to edit task
|
||||||
|
setTaskListProperty(this.crop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openTextFieldDialog(String title, String labelDescription, String value, boolean isLocation) throws IOException {
|
||||||
|
Dialog<String> dialog = new Dialog<>();
|
||||||
|
dialog.setTitle(title);
|
||||||
|
dialog.setHeaderText(title);
|
||||||
|
dialog.setResizable(false);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dialog.getDialogPane();
|
||||||
|
|
||||||
|
ButtonType save = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
dialogPane.getButtonTypes().addAll(save, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("TextFieldFormular.fxml", dialogPane) instanceof TextFieldFormularController controller) {
|
||||||
|
controller.setDescription_label(labelDescription);
|
||||||
|
controller.setValueTextArea(value);
|
||||||
|
controller.initSaveButton((Button) dialogPane.lookupButton(save));
|
||||||
|
|
||||||
|
dialog.setResultConverter(button -> button.equals(save) ? controller.getValue() : null);
|
||||||
|
|
||||||
|
dialog.showAndWait()
|
||||||
|
.ifPresent(string -> {
|
||||||
|
if (isLocation) {
|
||||||
|
System.out.println(string);
|
||||||
|
//ToDo method to set location
|
||||||
|
location_label.setText(string);
|
||||||
|
} else {
|
||||||
|
System.out.println(string);
|
||||||
|
//ToDo method to set area of crop in garden
|
||||||
|
area_label.setText(string);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDeleteTask(Task task) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setTitle("Delete " + task.getName());
|
||||||
|
alert.setHeaderText("Are you sure want to delete this Task?");
|
||||||
|
|
||||||
|
alert.showAndWait()
|
||||||
|
.ifPresent(buttonType -> {
|
||||||
|
if (buttonType == ButtonType.OK) {
|
||||||
|
try {
|
||||||
|
gardenSchedule.removeTask(task);
|
||||||
|
setTaskListProperty(this.crop);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO: Show error alert
|
||||||
|
LOG.log(Level.SEVERE, "Could not remove crop.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
|
|
||||||
public class HelloController {
|
|
||||||
@FXML
|
|
||||||
private Label welcomeText;
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
protected void onHelloButtonClick() {
|
|
||||||
welcomeText.setText("Welcome to JavaFX Application!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,47 @@
|
|||||||
package ch.zhaw.gartenverwaltung;
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
public class HomeController
|
import javafx.fxml.FXML;
|
||||||
{
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class HomeController implements Initializable {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewDavid;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewElias;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewGian;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewPhilippe;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageViewRoman;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
setImages(imageViewDavid, "");
|
||||||
|
setImages(imageViewElias, "");
|
||||||
|
setImages(imageViewGian, "");
|
||||||
|
setImages(imageViewRoman, "");
|
||||||
|
setImages(imageViewPhilippe, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setImages(ImageView imageView, String photoName) {
|
||||||
|
Image img;
|
||||||
|
if (photoName.equals("")) {
|
||||||
|
img = new Image(String.valueOf(getClass().getResource("icons/userIcon.png")));
|
||||||
|
} else {
|
||||||
|
img = new Image(String.valueOf(getClass().getResource("icons/" + photoName)));
|
||||||
|
}
|
||||||
|
imageView.setImage(img);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
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.scene.control.*;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.stage.WindowEvent;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.util.logging.Level;
|
||||||
import java.util.HashMap;
|
import java.util.logging.Logger;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
public class MainFXMLController implements Initializable {
|
public class MainFXMLController {
|
||||||
/**
|
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
|
||||||
* Caching the panes
|
|
||||||
*/
|
@Inject
|
||||||
private final Map<String, AnchorPane> panes = new HashMap<>();
|
AppLoader appLoader;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button home_button;
|
private Button home_button;
|
||||||
@@ -28,36 +32,92 @@ 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;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button plants_button;
|
private Button settings_button;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void goToHome(ActionEvent event) throws IOException {
|
private Button tutorial_button;
|
||||||
loadPane("Home.fxml");
|
|
||||||
|
private final Stage tutorialModal = new Stage();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to home pane
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void goToHome() {
|
||||||
|
showPaneAsMainView("Home.fxml");
|
||||||
styleChangeButton(home_button);
|
styleChangeButton(home_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to my garden pane
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goToMyPlants(ActionEvent event) throws IOException {
|
void goToMyPlants() {
|
||||||
loadPane("MyPlants.fxml");
|
showPaneAsMainView("MyGarden.fxml");
|
||||||
styleChangeButton(myPlants_button);
|
styleChangeButton(myGarden_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to the schedule pane
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goToMySchedule(ActionEvent event) throws IOException {
|
void goToMySchedule() {
|
||||||
loadPane("MySchedule.fxml");
|
showPaneAsMainView("MySchedule.fxml");
|
||||||
styleChangeButton(mySchedule_button);
|
styleChangeButton(mySchedule_button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open dialog of the settings
|
||||||
|
* @throws IOException exception
|
||||||
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void goToPlants(ActionEvent event) throws IOException {
|
public void openSettings() throws IOException {
|
||||||
loadPane("Plants.fxml");
|
Dialog<ButtonType> dialog = new Dialog<>();
|
||||||
styleChangeButton(plants_button);
|
dialog.setTitle("Settings");
|
||||||
|
dialog.setHeaderText("Settings");
|
||||||
|
dialog.setResizable(false);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dialog.getDialogPane();
|
||||||
|
|
||||||
|
ButtonType saveSettings = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
dialogPane.getButtonTypes().addAll(saveSettings, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("Settings.fxml", dialogPane) instanceof SettingsController controller) {
|
||||||
|
|
||||||
|
dialog.showAndWait()
|
||||||
|
.ifPresent(button -> {
|
||||||
|
if (button.equals(saveSettings)) {
|
||||||
|
controller.saveSettings();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the tutorial window
|
||||||
|
*/
|
||||||
|
public void showTutorial() {
|
||||||
|
if (!tutorialModal.isShowing()) {
|
||||||
|
if (tutorialModal.getScene() == null) {
|
||||||
|
try {
|
||||||
|
appLoader.loadSceneToStage("Tutorial.fxml", tutorialModal);
|
||||||
|
tutorialModal.initModality(Modality.NONE);
|
||||||
|
tutorialModal.setResizable(false);
|
||||||
|
tutorialModal.sizeToScene();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.SEVERE, "Could not load Tutorial");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tutorialModal.show();
|
||||||
|
}
|
||||||
|
tutorialModal.requestFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,25 +125,31 @@ 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) {
|
||||||
|
try {
|
||||||
|
Pane anchorPane = appLoader.loadPane(fxmlFile);
|
||||||
|
mainPane.getChildren().setAll(anchorPane);
|
||||||
|
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
|
||||||
|
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
|
||||||
|
|
||||||
AnchorPane anchorPane = panes.get(fxmlFile);
|
anchorPane.removeEventHandler(ChangeViewEvent.CHANGE_MAIN_VIEW, changeMainViewHandler);
|
||||||
if (anchorPane == null) {
|
anchorPane.addEventHandler(ChangeViewEvent.CHANGE_MAIN_VIEW, changeMainViewHandler);
|
||||||
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
|
} catch (IOException e) {
|
||||||
anchorPane = loader.load();
|
LOG.log(Level.SEVERE, "Could not load pane.", e);
|
||||||
panes.put(fxmlFile, anchorPane);
|
|
||||||
|
|
||||||
if(fxmlFile.equals("MyPlants.fxml")) {
|
|
||||||
MyPlantsController myPlantsController = loader.getController();
|
|
||||||
myPlantsController.getMainController(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mainPane.getChildren().setAll(anchorPane);
|
}
|
||||||
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
|
|
||||||
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
|
|
||||||
|
|
||||||
|
private final EventHandler<ChangeViewEvent> changeMainViewHandler = (ChangeViewEvent event) -> showPaneAsMainView(event.view());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* preload all menu bar panes
|
||||||
|
* @throws IOException exception
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
@@ -94,14 +160,36 @@ 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();
|
||||||
styleChangeButton(home_button);
|
showPaneAsMainView("MyGarden.fxml");
|
||||||
|
styleChangeButton(myGarden_button);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);
|
||||||
}
|
}
|
||||||
|
mainPane.getScene().getWindow().setOnCloseRequest(this::closeWindowHandler);
|
||||||
|
setIconToButton(home_button, "homeIcon.png");
|
||||||
|
setIconToButton(settings_button, "settingsIcon.png");
|
||||||
|
tutorial_button.visibleProperty().bind(Settings.getInstance().getShowTutorialProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeWindowHandler(WindowEvent windowEvent) {
|
||||||
|
tutorialModal.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds icon to given button
|
||||||
|
* @param button the button which get the icon
|
||||||
|
* @param iconFileName file name of icon
|
||||||
|
*/
|
||||||
|
private void setIconToButton(Button button, String iconFileName) {
|
||||||
|
Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
|
||||||
|
ImageView imageView = new ImageView(img);
|
||||||
|
imageView.setFitHeight(20);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
button.setGraphic(imageView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
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.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
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 Button addPlant_button;
|
||||||
|
@FXML
|
||||||
|
private ListView<Crop> myGarden_listView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize crop list
|
||||||
|
* add listener for crop list
|
||||||
|
* set icon for button
|
||||||
|
*/
|
||||||
|
@AfterInject
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public void init() {
|
||||||
|
System.out.println("once");
|
||||||
|
setIconToButton(addPlant_button, "addIcon.png");
|
||||||
|
myGarden_listView.itemsProperty().bind(garden.getPlantedCrops());
|
||||||
|
setCellFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* redirect to plant fxml file
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void addPlant() {
|
||||||
|
myGardenRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "Plants.fxml"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set cell factory to load {@link HBox} as list view content
|
||||||
|
*/
|
||||||
|
private void setCellFactory() {
|
||||||
|
myGarden_listView.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Crop crop, boolean empty) {
|
||||||
|
super.updateItem(crop, empty);
|
||||||
|
|
||||||
|
if (empty || crop == null) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
setText("");
|
||||||
|
setGraphic(createHBoxForListView(crop));
|
||||||
|
} catch (HardinessZoneNotSetException | IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not get plant for Cell", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns HBox of the crop
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @return {@link HBox} of the {@link Crop}
|
||||||
|
* @throws HardinessZoneNotSetException exception
|
||||||
|
* @throws IOException exception
|
||||||
|
*/
|
||||||
|
private HBox createHBoxForListView(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();
|
||||||
|
Button delete = new Button();
|
||||||
|
setIconToButton(details, "detailsIcon.png");
|
||||||
|
setIconToButton(delete, "deleteIcon.png");
|
||||||
|
details.setOnAction(getGoToCropDetailEvent(crop));
|
||||||
|
delete.setOnAction(getDeleteCropEvent(crop));
|
||||||
|
|
||||||
|
hBox.getChildren().addAll(imageView, label, details, delete);
|
||||||
|
return hBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds icon to button
|
||||||
|
* @param button the button which get the icon
|
||||||
|
* @param iconFileName file name of icon
|
||||||
|
*/
|
||||||
|
private void setIconToButton(Button button, String iconFileName) {
|
||||||
|
Image img = new Image(String.valueOf(getClass().getResource("icons/" + iconFileName)));
|
||||||
|
ImageView imageView = new ImageView(img);
|
||||||
|
imageView.setFitHeight(20);
|
||||||
|
imageView.setPreserveRatio(true);
|
||||||
|
button.setGraphic(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open detail window of the selected {@link Crop}
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @return {@link EventHandler} for button
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open alert for deleting the selected {@link Crop}
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @return {@link EventHandler} for button
|
||||||
|
*/
|
||||||
|
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
|
||||||
|
return (event) -> {
|
||||||
|
try {
|
||||||
|
showConfirmation(crop);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert to confirm that the crop can be deleted.
|
||||||
|
* @param crop {@link Crop} which is selected
|
||||||
|
* @throws IOException exception
|
||||||
|
* @throws HardinessZoneNotSetException exception
|
||||||
|
*/
|
||||||
|
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
|
||||||
|
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setTitle("Delete " + plant.name());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,51 @@
|
|||||||
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 ch.zhaw.gartenverwaltung.types.Seasons;
|
import ch.zhaw.gartenverwaltung.types.Seasons;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.ListProperty;
|
import javafx.beans.property.ListProperty;
|
||||||
import javafx.beans.property.SimpleListProperty;
|
import javafx.beans.property.SimpleListProperty;
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Bounds;
|
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.input.KeyEvent;
|
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.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());
|
private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public AnchorPane plantsRoot;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private VBox seasons;
|
private VBox seasons;
|
||||||
|
|
||||||
@@ -52,18 +62,41 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* saves the current selected plant in new JSON database
|
* open new window to select sow or harvest day to save the crop
|
||||||
* @param event event
|
|
||||||
*/
|
*/
|
||||||
@FXML
|
@FXML
|
||||||
void saveToMyPlant(ActionEvent event) {
|
void selectSowDate() throws IOException {
|
||||||
//ToDo model save selectedPlant to mySelectedPlant(IO)
|
Dialog<LocalDate> dateSelection = new Dialog<>();
|
||||||
|
dateSelection.setTitle("Select Date");
|
||||||
|
dateSelection.setHeaderText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name()));
|
||||||
|
dateSelection.setResizable(false);
|
||||||
|
|
||||||
|
DialogPane dialogPane = dateSelection.getDialogPane();
|
||||||
|
|
||||||
|
ButtonType sowButton = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
|
||||||
|
dialogPane.getButtonTypes().addAll(sowButton, ButtonType.CANCEL);
|
||||||
|
|
||||||
|
if (appLoader.loadPaneToDialog("SelectSowDay.fxml", dialogPane) instanceof SelectSowDayController controller) {
|
||||||
|
controller.initSaveButton((Button) dialogPane.lookupButton(sowButton));
|
||||||
|
controller.setSelectedPlant(selectedPlant);
|
||||||
|
dateSelection.setResultConverter(button -> button.equals(sowButton) ? controller.retrieveResult() : null);
|
||||||
|
|
||||||
|
dateSelection.showAndWait()
|
||||||
|
.ifPresent(date -> {
|
||||||
|
try {
|
||||||
|
garden.plantAsCrop(selectedPlant, date);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
|
||||||
|
LOG.log(Level.SEVERE, "Couldn't save Crop", e);
|
||||||
|
}
|
||||||
|
plantsRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "MyGarden.fxml"));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,30 +106,34 @@ public class PlantsController implements Initializable {
|
|||||||
* create event listener for selected list entry and search by query
|
* create event listener for selected list entry and search by query
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@AfterInject
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
@SuppressWarnings("unused")
|
||||||
|
public void init() {
|
||||||
setListCellFactory();
|
setListCellFactory();
|
||||||
fillPlantListWithHardinessZone();
|
fillPlantListWithHardinessZone();
|
||||||
list_plants.itemsProperty().bind(plantListProperty);
|
list_plants.itemsProperty().bind(plantListProperty);
|
||||||
|
|
||||||
description_plant.setText("");
|
description_plant.setText("");
|
||||||
saveToMyPlant_button.setDisable(true);
|
selectSowDay_button.setDisable(true);
|
||||||
|
|
||||||
createFilterSeasons();
|
createFilterSeasons();
|
||||||
createFilterHardinessZone();
|
createFilterHardinessZone();
|
||||||
lookForSelectedListEntry();
|
lookForSelectedListEntry();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
viewFilteredListBySearch();
|
viewFilteredListBySearch();
|
||||||
} catch (HardinessZoneNotSetException | IOException e) {
|
} catch (HardinessZoneNotSetException e) {
|
||||||
e.printStackTrace();
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set text of list view to plant name
|
* set text of list view to plant name
|
||||||
*/
|
*/
|
||||||
private void setListCellFactory() {
|
private void setListCellFactory() {
|
||||||
list_plants.setCellFactory(param -> new ListCell<Plant>() {
|
list_plants.setCellFactory(param -> new ListCell<>() {
|
||||||
@Override
|
@Override
|
||||||
protected void updateItem(Plant plant, boolean empty) {
|
protected void updateItem(Plant plant, boolean empty) {
|
||||||
super.updateItem(plant, empty);
|
super.updateItem(plant, empty);
|
||||||
@@ -113,9 +150,10 @@ public class PlantsController implements Initializable {
|
|||||||
/**
|
/**
|
||||||
* get plant list according to param season and hardiness zone
|
* get plant list according to param season and hardiness zone
|
||||||
* fill list view with plant list
|
* fill list view with plant list
|
||||||
|
*
|
||||||
* @param season enum of seasons
|
* @param season enum of seasons
|
||||||
* @throws HardinessZoneNotSetException throws exception
|
* @throws HardinessZoneNotSetException throws exception
|
||||||
* @throws IOException throws exception
|
* @throws IOException throws exception
|
||||||
*/
|
*/
|
||||||
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
|
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
|
||||||
clearListView();
|
clearListView();
|
||||||
@@ -125,20 +163,23 @@ public class PlantsController implements Initializable {
|
|||||||
/**
|
/**
|
||||||
* get plant list filtered by search plant entry and hardiness zone
|
* get plant list filtered by search plant entry and hardiness zone
|
||||||
* fill list view with plant list
|
* fill list view with plant list
|
||||||
|
*
|
||||||
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
|
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
|
||||||
* @throws IOException throws exception
|
* @throws IOException throws exception
|
||||||
*/
|
*/
|
||||||
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
|
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
|
||||||
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
|
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
if (newValue.isEmpty()) {
|
if (newValue.isEmpty()) {
|
||||||
fillPlantListWithHardinessZone();
|
fillPlantListWithHardinessZone();
|
||||||
}else {
|
} else {
|
||||||
try {
|
try {
|
||||||
List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
|
List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
|
||||||
clearListView();
|
clearListView();
|
||||||
plantListProperty.addAll(filteredPlants);
|
plantListProperty.addAll(filteredPlants);
|
||||||
} catch (HardinessZoneNotSetException | IOException e) {
|
} catch (HardinessZoneNotSetException e) {
|
||||||
e.printStackTrace();
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -152,8 +193,10 @@ public class PlantsController implements Initializable {
|
|||||||
try {
|
try {
|
||||||
clearListView();
|
clearListView();
|
||||||
plantListProperty.addAll(plantListModel.getPlantList(plantListModel.getCurrentZone()));
|
plantListProperty.addAll(plantListModel.getPlantList(plantListModel.getCurrentZone()));
|
||||||
} catch (HardinessZoneNotSetException | IOException e) {
|
} catch (HardinessZoneNotSetException e) {
|
||||||
e.printStackTrace();
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,16 +210,13 @@ public class PlantsController implements Initializable {
|
|||||||
for (HardinessZone zone : HardinessZone.values()) {
|
for (HardinessZone zone : HardinessZone.values()) {
|
||||||
RadioButton radioButton = new RadioButton(zone.name());
|
RadioButton radioButton = new RadioButton(zone.name());
|
||||||
radioButton.setToggleGroup(hardinessGroup);
|
radioButton.setToggleGroup(hardinessGroup);
|
||||||
radioButton.setPadding(new Insets(0,0,10,0));
|
radioButton.setPadding(new Insets(0, 0, 10, 0));
|
||||||
if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
|
if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
|
||||||
radioButton.setSelected(true);
|
radioButton.setSelected(true);
|
||||||
}
|
}
|
||||||
radioButton.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
@Override
|
plantListModel.setCurrentZone(zone);
|
||||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
fillPlantListWithHardinessZone();
|
||||||
plantListModel.setCurrentZone(zone);
|
|
||||||
fillPlantListWithHardinessZone();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
climate_zones.getChildren().add(radioButton);
|
climate_zones.getChildren().add(radioButton);
|
||||||
}
|
}
|
||||||
@@ -190,26 +230,25 @@ public class PlantsController implements Initializable {
|
|||||||
private void createFilterSeasons() {
|
private void createFilterSeasons() {
|
||||||
ToggleGroup seasonGroup = new ToggleGroup();
|
ToggleGroup seasonGroup = new ToggleGroup();
|
||||||
for (Seasons season : Seasons.values()) {
|
for (Seasons season : Seasons.values()) {
|
||||||
RadioButton radioButton = new RadioButton(season.name());
|
RadioButton radioButton = new RadioButton(season.getName());
|
||||||
radioButton.setToggleGroup(seasonGroup);
|
radioButton.setToggleGroup(seasonGroup);
|
||||||
radioButton.setPadding(new Insets(0,0,10,0));
|
radioButton.setPadding(new Insets(0, 0, 10, 0));
|
||||||
if (season.equals(Seasons.AllSEASONS)) {
|
if (season.equals(Seasons.AllSEASONS)) {
|
||||||
radioButton.setSelected(true);
|
radioButton.setSelected(true);
|
||||||
}
|
}
|
||||||
radioButton.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
@Override
|
if (season.equals(Seasons.AllSEASONS)) {
|
||||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
fillPlantListWithHardinessZone();
|
||||||
if (season.equals(Seasons.AllSEASONS)) {
|
} else {
|
||||||
fillPlantListWithHardinessZone();
|
try {
|
||||||
} else {
|
viewFilteredListBySeason(season);
|
||||||
try {
|
} catch (HardinessZoneNotSetException e) {
|
||||||
viewFilteredListBySeason(season);
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
} catch (HardinessZoneNotSetException | IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
seasons.getChildren().add(radioButton);
|
seasons.getChildren().add(radioButton);
|
||||||
}
|
}
|
||||||
@@ -221,33 +260,47 @@ public class PlantsController implements Initializable {
|
|||||||
* image of the plant
|
* 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);
|
||||||
if(newValue != null) {
|
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
selectedPlant = newValue;
|
img_plant.setImage(img);
|
||||||
description_plant.setText(selectedPlant.description());
|
list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
saveToMyPlant_button.setDisable(false);
|
if (newValue != null) {
|
||||||
Image img;
|
selectedPlant = newValue;
|
||||||
if(selectedPlant.image() != null) {
|
description_plant.setText(getPlantDescription());
|
||||||
img = selectedPlant.image();
|
selectSowDay_button.setDisable(false);
|
||||||
} else {
|
Image img1;
|
||||||
img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
if (selectedPlant.image() != null) {
|
||||||
}
|
img1 = selectedPlant.image();
|
||||||
img_plant.setImage(img);
|
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
selectedPlant = null;
|
img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
description_plant.setText("");
|
|
||||||
saveToMyPlant_button.setDisable(true);
|
|
||||||
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
|
||||||
img_plant.setImage(img);
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates {@link String} of the plant information.
|
||||||
|
* @return return {@link Plant} description
|
||||||
|
*/
|
||||||
|
private String getPlantDescription() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Name: ").append(selectedPlant.name())
|
||||||
|
.append("\nDescription:\n").append(selectedPlant.description())
|
||||||
|
.append("\nLight Level: ").append(selectedPlant.light())
|
||||||
|
.append("\nSoil: ").append(selectedPlant.soil())
|
||||||
|
.append("\nSpacing: ").append(selectedPlant.spacing());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* clears the ListView of entries
|
* clears the ListView of entries
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if sow date radio button was selected return sow date
|
||||||
|
* if sow date was not selected get sow from harvest day and return sow date
|
||||||
|
* @return {@link LocalDate} of the sow date
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable save button when date picker is empty
|
||||||
|
* @param saveButton {@link Button} to be disabled
|
||||||
|
*/
|
||||||
|
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;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
|
||||||
|
public class Settings {
|
||||||
|
private HardinessZone currentHardinessZone = HardinessZone.ZONE_8A;
|
||||||
|
private static Settings instance;
|
||||||
|
private final BooleanProperty showTutorial = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShowTutorial (boolean showTutorial) {
|
||||||
|
this.showTutorial.setValue(showTutorial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty getShowTutorialProperty() {
|
||||||
|
return this.showTutorial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getShowTutorial() {
|
||||||
|
return this.showTutorial.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.CheckBox;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class SettingsController implements Initializable {
|
||||||
|
Settings settings = Settings.getInstance();
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<HardinessZone> selectHardinessZone_comboBox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CheckBox showTutorial_checkBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save selected values to {@link Settings}
|
||||||
|
*/
|
||||||
|
public void saveSettings() {
|
||||||
|
settings.setShowTutorial(showTutorial_checkBox.isSelected());
|
||||||
|
settings.setCurrentHardinessZone(selectHardinessZone_comboBox.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save default values form {@link Settings}
|
||||||
|
* @param location location
|
||||||
|
* @param resources resources
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
showTutorial_checkBox.setSelected(settings.getShowTutorial());
|
||||||
|
selectHardinessZone_comboBox.getItems().addAll(HardinessZone.values());
|
||||||
|
selectHardinessZone_comboBox.setValue(settings.getCurrentHardinessZone());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import javafx.beans.binding.Binding;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class TaskFormularController implements Initializable {
|
||||||
|
private Crop crop;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextArea description_area;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private DatePicker end_datePicker;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField interval_field;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private DatePicker start_datePicker;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField taskName_field;
|
||||||
|
|
||||||
|
|
||||||
|
public Task returnResult() {
|
||||||
|
Task task = new Task(taskName_field.getText(), description_area.getText(),
|
||||||
|
start_datePicker.getValue(), end_datePicker.getValue(),
|
||||||
|
Integer.parseInt(interval_field.getText()), crop.getCropId().get());
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCorp(Crop crop) {
|
||||||
|
this.crop = crop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskValue(Task task) {
|
||||||
|
taskName_field.setText(task.getName());
|
||||||
|
description_area.setText(task.getDescription());
|
||||||
|
start_datePicker.setValue(task.getStartDate());
|
||||||
|
end_datePicker.setValue(task.getEndDate().orElse(null));
|
||||||
|
if(task.getInterval().orElse(null)!=null) {
|
||||||
|
interval_field.setText(task.getInterval().get().toString());
|
||||||
|
} else {
|
||||||
|
interval_field.setText(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Callback<DatePicker, DateCell> getDayCellFactoryStartDate() {
|
||||||
|
|
||||||
|
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 && item.compareTo(crop.getStartDate()) > 0) {
|
||||||
|
setDisable(false);
|
||||||
|
setStyle("-fx-background-color: #32CD32;");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_datePicker.getValue() != null && item.compareTo(end_datePicker.getValue()) > 0) {
|
||||||
|
setDisable(true);
|
||||||
|
setStyle("-fx-background-color: #ffc0cb;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Callback<DatePicker, DateCell> getDayCellFactoryEndDate() {
|
||||||
|
|
||||||
|
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 && item.compareTo(crop.getStartDate()) > 0) {
|
||||||
|
setDisable(false);
|
||||||
|
setStyle("-fx-background-color: #32CD32;");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_datePicker.getValue() != null && item.compareTo(start_datePicker.getValue()) < 0) {
|
||||||
|
setDisable(true);
|
||||||
|
setStyle("-fx-background-color: #ffc0cb;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initSaveButton(Button button) {
|
||||||
|
interval_field.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (!newValue.matches("\\d*")) {
|
||||||
|
interval_field.setText(newValue.replaceAll("[^\\d]", ""));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
button.disableProperty().bind(start_datePicker.valueProperty().isNull()
|
||||||
|
.or(end_datePicker.valueProperty().isNull())
|
||||||
|
.or(taskName_field.textProperty().isEmpty())
|
||||||
|
.or(description_area.textProperty().isEmpty())
|
||||||
|
.or(interval_field.textProperty().isEmpty()));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
start_datePicker.setDayCellFactory(getDayCellFactoryStartDate());
|
||||||
|
start_datePicker.setEditable(false);
|
||||||
|
|
||||||
|
end_datePicker.setDayCellFactory(getDayCellFactoryEndDate());
|
||||||
|
end_datePicker.setEditable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
|
||||||
|
public class TextFieldFormularController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label description_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField text_area;
|
||||||
|
|
||||||
|
|
||||||
|
public void setDescription_label(String string) {
|
||||||
|
description_label.setText(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueTextArea(String string) {
|
||||||
|
text_area.setText(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return text_area.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initSaveButton(Button button) {
|
||||||
|
button.disableProperty().bind(text_area.textProperty().isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public class TutorialController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public Button previousPageButton;
|
||||||
|
@FXML
|
||||||
|
public Button nextPageButton;
|
||||||
|
@FXML
|
||||||
|
public StackPane tourPages;
|
||||||
|
public ImageView imgAddNewPlant;
|
||||||
|
public ImageView imgTaskList;
|
||||||
|
public ImageView imgSelectDate;
|
||||||
|
|
||||||
|
private int page = 0;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
switchViews();
|
||||||
|
setButtonAbilities();
|
||||||
|
|
||||||
|
Image placeholder = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
|
imgAddNewPlant.setImage(placeholder);
|
||||||
|
imgSelectDate.setImage(placeholder);
|
||||||
|
imgTaskList.setImage(placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void viewNextPage() {
|
||||||
|
page++;
|
||||||
|
switchViews();
|
||||||
|
setButtonAbilities();
|
||||||
|
}
|
||||||
|
public void viewPreviousPage() {
|
||||||
|
page--;
|
||||||
|
switchViews();
|
||||||
|
setButtonAbilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setButtonAbilities() {
|
||||||
|
previousPageButton.setDisable(page <= 0);
|
||||||
|
nextPageButton.setDisable(page >= tourPages.getChildren().size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchViews() {
|
||||||
|
tourPages.getChildren().forEach(node -> node.setOpacity(0));
|
||||||
|
tourPages.getChildren().get(page).setOpacity(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeTutorial() {
|
||||||
|
Stage root = (Stage) tourPages.getScene().getWindow();
|
||||||
|
root.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 annotated with {@link Inject}
|
||||||
|
* have been injected.
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
public @interface AfterInject { }
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class responsible for bootstrapping the application wide dependencies
|
||||||
|
* and injecting them into JavaFX Controllers.
|
||||||
|
*/
|
||||||
|
public class AppLoader {
|
||||||
|
/**
|
||||||
|
* Caching the panes
|
||||||
|
*/
|
||||||
|
private final Map<String, Pane> panes = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application-wide dependencies
|
||||||
|
*/
|
||||||
|
private final Map<String, Object> dependencies = new HashMap<>();
|
||||||
|
|
||||||
|
public AppLoader() throws IOException {
|
||||||
|
PlantList plantList = new JsonPlantList();
|
||||||
|
CropList cropList = new JsonCropList();
|
||||||
|
TaskList taskList = new JsonTaskList();
|
||||||
|
GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList);
|
||||||
|
|
||||||
|
dependencies.put(PlantList.class.getSimpleName(), plantList);
|
||||||
|
dependencies.put(CropList.class.getSimpleName(), cropList);
|
||||||
|
dependencies.put(TaskList.class.getSimpleName(), taskList);
|
||||||
|
|
||||||
|
dependencies.put(PlantListModel.class.getSimpleName(), new PlantListModel(plantList));
|
||||||
|
|
||||||
|
dependencies.put(GardenSchedule.class.getSimpleName(), gardenSchedule);
|
||||||
|
dependencies.put(Garden.class.getSimpleName(), new Garden(gardenSchedule, cropList));
|
||||||
|
dependencies.put(AppLoader.class.getSimpleName(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
Scene scene = new Scene(root);
|
||||||
|
String css = Objects.requireNonNull(this.getClass().getResource("styles.css")).toExternalForm();
|
||||||
|
appendee.setScene(scene);
|
||||||
|
scene.getStylesheets().add(css);
|
||||||
|
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) && method.getParameterCount() == 0)
|
||||||
|
.forEach(afterInjectMethod -> {
|
||||||
|
try {
|
||||||
|
afterInjectMethod.invoke(controller);
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
|
// TODO: Log
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getAppDependency(Class<?> type) {
|
||||||
|
return dependencies.get(type.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung.bootstrap;
|
||||||
|
|
||||||
|
import javafx.event.Event;
|
||||||
|
import javafx.event.EventType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an event that should lead to a view being changed.
|
||||||
|
*/
|
||||||
|
public class ChangeViewEvent extends Event {
|
||||||
|
private final String view;
|
||||||
|
|
||||||
|
public static final EventType<ChangeViewEvent> CHANGE_MAIN_VIEW = new EventType<>("CHANGE_MAIN_VIEW");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Event that should lead to the main view being changed.
|
||||||
|
*
|
||||||
|
* @param eventType The {@link EventType<ChangeViewEvent>} specifying which view should be changed.
|
||||||
|
* @param view The filename of the View to be changed to
|
||||||
|
*/
|
||||||
|
public ChangeViewEvent(EventType<? extends Event> eventType, String view) {
|
||||||
|
super(eventType);
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String view() {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 { }
|
||||||
@@ -6,7 +6,11 @@ import java.io.IOException;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface GardenPlan {
|
/**
|
||||||
|
* Represents a List of {@link Crop}s.
|
||||||
|
* The interface specifies the operations to add/update and remove entries.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
*
|
*
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
package ch.zhaw.gartenverwaltung.io;
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides an sequential ID starting from the given initial Value.
|
||||||
|
*/
|
||||||
public class IdProvider {
|
public class IdProvider {
|
||||||
private long currentId;
|
private long currentId;
|
||||||
public IdProvider(long initialValue) {
|
public IdProvider(long initialValue) {
|
||||||
currentId = initialValue;
|
currentId = initialValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Yields the next ID in the sequence.
|
||||||
|
* @return The next ID
|
||||||
|
*/
|
||||||
public long incrementAndGet() {
|
public long incrementAndGet() {
|
||||||
return ++currentId;
|
return ++currentId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,13 @@ 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 {
|
/**
|
||||||
private final URL dataSource = getClass().getResource("user-crops.json");
|
* Implements the {@link CropList} interface for reading and writing {@link Crop} objects
|
||||||
|
* from and to a local JSON file.
|
||||||
|
* The reads are cached to minimize file-io operations.
|
||||||
|
*/
|
||||||
|
public class JsonCropList implements CropList {
|
||||||
|
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 +45,22 @@ public class JsonGardenPlan implements GardenPlan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see GardenPlan#getCrops()
|
* Default constructor. Uses a file from the app resources.
|
||||||
|
*/
|
||||||
|
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 +71,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 +82,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 +99,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 +151,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,12 +20,12 @@ 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 reading {@link Plant} objects
|
||||||
* from a JSON file.
|
* from a local 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;
|
||||||
|
|
||||||
private HardinessZone currentZone;
|
private HardinessZone currentZone;
|
||||||
private Map<Long, Plant> plantMap = Collections.emptyMap();
|
private Map<Long, Plant> plantMap = Collections.emptyMap();
|
||||||
@@ -44,12 +44,27 @@ public class JsonPlantDatabase implements PlantDatabase {
|
|||||||
imageModule.addDeserializer(Image.class, new PlantImageDeserializer());
|
imageModule.addDeserializer(Image.class, new PlantImageDeserializer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor. Uses a file from the app resources.
|
||||||
|
*/
|
||||||
|
public JsonPlantList() {
|
||||||
|
this.dataSource = getClass().getResource("plantdb.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 JsonPlantList(URL dataSource) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If no data is currently loaded, or the specified zone differs
|
* If no data is currently loaded, or the specified zone differs
|
||||||
* 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 +75,7 @@ public class JsonPlantDatabase implements PlantDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see PlantDatabase#getPlantById(long)
|
* @see PlantList#getPlantById(HardinessZone, 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 +104,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) -> {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
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;
|
||||||
@@ -14,22 +13,24 @@ 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;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
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 reading and writing {@link Task} objects
|
||||||
* from and to a JSON file.
|
* from and to a local 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;
|
||||||
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
||||||
|
|
||||||
private Map<Long, Task> taskMap = Collections.emptyMap();
|
private Map<Long, Task> taskMap = Collections.emptyMap();
|
||||||
|
private final List<TaskListObserver> subscribers = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creating constant objects required to deserialize the {@link LocalDate} classes
|
* Creating constant objects required to deserialize the {@link LocalDate} classes
|
||||||
@@ -44,11 +45,26 @@ public class JsonTaskDatabase implements TaskDatabase{
|
|||||||
timeModule.addSerializer(LocalDate.class, dateSerializer);
|
timeModule.addSerializer(LocalDate.class, dateSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor. Uses a file from the app resources.
|
||||||
|
*/
|
||||||
|
public JsonTaskList() {
|
||||||
|
this.dataSource = getClass().getResource("taskdb.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 JsonTaskList(URL dataSource) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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{
|
||||||
@@ -58,14 +74,30 @@ public class JsonTaskDatabase implements TaskDatabase{
|
|||||||
return taskMap.values().stream().filter(task -> task.isInTimePeriode(start, end)).toList();
|
return taskMap.values().stream().filter(task -> task.isInTimePeriode(start, end)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method get all Tasks for a specific Crop
|
||||||
|
* @param cropId the cropId
|
||||||
|
* @return List of Tasks for given Crop
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<Task> getTaskForCrop(Crop crop) {
|
public List<Task> getTaskForCrop(long cropId) throws IOException {
|
||||||
return null; //TODO implement
|
if(taskMap.isEmpty()) {
|
||||||
|
loadTaskListFromFile();
|
||||||
|
}
|
||||||
|
return taskMap.values().stream().filter(task -> task.getCropId() == cropId).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method remove all Tasks for a specific Crop
|
||||||
|
* @param cropId the crop
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void removeTasksForCrop(Crop crop) {
|
public void removeTasksForCrop(long cropId) throws IOException {
|
||||||
// TODO implement
|
if(taskMap.isEmpty()) {
|
||||||
|
loadTaskListFromFile();
|
||||||
|
}
|
||||||
|
taskMap.values().removeIf(task -> task.getCropId() == cropId);
|
||||||
|
notifySubscribers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,17 +107,18 @@ 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 {
|
||||||
if(taskMap.isEmpty()) {
|
if(taskMap.isEmpty()) {
|
||||||
loadTaskListFromFile();
|
loadTaskListFromFile();
|
||||||
}
|
}
|
||||||
if(task.getId() == 0) {
|
long id = task.getId().orElse(idProvider.incrementAndGet());
|
||||||
task.withId(idProvider.incrementAndGet());
|
|
||||||
}
|
taskMap.put(id, task.withId(id));
|
||||||
writeTaskListToFile();
|
writeTaskListToFile();
|
||||||
|
notifySubscribers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,17 +126,37 @@ 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 {
|
||||||
if(taskMap.isEmpty()) {
|
if(taskMap.isEmpty()) {
|
||||||
loadTaskListFromFile();
|
loadTaskListFromFile();
|
||||||
}
|
}
|
||||||
if(taskMap.containsKey(task.getId())){
|
Long taskId = task.getId().orElseThrow(IOException::new);
|
||||||
taskMap.remove(task.getId());
|
if(taskMap.containsKey(taskId)){
|
||||||
|
taskMap.remove(taskId);
|
||||||
writeTaskListToFile();
|
writeTaskListToFile();
|
||||||
}
|
}
|
||||||
|
notifySubscribers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @param observer The change handler
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void subscribe(TaskListObserver observer) {
|
||||||
|
subscribers.add(observer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the change handler method on all registered observers.
|
||||||
|
*/
|
||||||
|
private void notifySubscribers() {
|
||||||
|
for (TaskListObserver subscriber : subscribers) {
|
||||||
|
subscriber.onChange(taskMap.values().stream().toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,8 +194,11 @@ public class JsonTaskDatabase implements TaskDatabase{
|
|||||||
|
|
||||||
taskMap = result.stream()
|
taskMap = result.stream()
|
||||||
.collect(HashMap::new,
|
.collect(HashMap::new,
|
||||||
(res, task) -> res.put(task.getId(), task),
|
(res, task) -> res.put(task.getId().orElse(0L), task),
|
||||||
(existing, replacement) -> {});
|
(existing, replacement) -> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Long maxId = taskMap.isEmpty() ? 0L : Collections.max(taskMap.keySet());
|
||||||
|
idProvider = new IdProvider(maxId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,10 +8,10 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A database of {@link Plant}s.
|
* A List 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}
|
||||||
*
|
*
|
||||||
@@ -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.
|
* Represents a List of {@link Task}s.
|
||||||
* The interface specifies the minimal required operations.
|
* The interface specifies the operations to add/update and remove entries.
|
||||||
*/
|
*/
|
||||||
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.
|
||||||
*
|
*
|
||||||
@@ -25,9 +21,20 @@ public interface TaskDatabase {
|
|||||||
*/
|
*/
|
||||||
List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException;
|
List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException;
|
||||||
|
|
||||||
List<Task> getTaskForCrop(Crop crop); //TODO Javadoc
|
/**
|
||||||
|
* Method get all Tasks for a specific Crop
|
||||||
|
* @param cropId the cropId
|
||||||
|
* @return List of Tasks for given Crop
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
List<Task> getTaskForCrop(long cropId) throws IOException;
|
||||||
|
|
||||||
void removeTasksForCrop(Crop crop); // TODO Javadoc
|
/**
|
||||||
|
* Method remove all Tasks for a specific Crop
|
||||||
|
* @param cropId the cropId
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
void removeTasksForCrop(long cropId) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the {@link Task} in the Cache.
|
* Saves the {@link Task} in the Cache.
|
||||||
@@ -44,4 +51,22 @@ public interface TaskDatabase {
|
|||||||
* @throws IOException If the database cannot be accessed
|
* @throws IOException If the database cannot be accessed
|
||||||
*/
|
*/
|
||||||
void removeTask(Task task) throws IOException;
|
void removeTask(Task task) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an observer to be notified of Changes in the TaskList
|
||||||
|
* @param observer The change handler
|
||||||
|
*/
|
||||||
|
void subscribe(TaskListObserver observer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies an observer for a TaskList
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
interface TaskListObserver {
|
||||||
|
/**
|
||||||
|
* Method which will be called when changes occur.
|
||||||
|
* @param newTaskList The new values
|
||||||
|
*/
|
||||||
|
void onChange(List<Task> newTaskList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,9 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the Jackson Library to deserialize a String to a {@link GrowthPhaseType}
|
||||||
|
*/
|
||||||
public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType> {
|
public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType> {
|
||||||
public GrowthPhaseTypeDeserializer(Class<?> vc) {
|
public GrowthPhaseTypeDeserializer(Class<?> vc) {
|
||||||
super(vc);
|
super(vc);
|
||||||
@@ -16,13 +19,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the Jackson Library to deserialize a String to a {@link HardinessZone}
|
||||||
|
*/
|
||||||
public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
|
public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
|
||||||
public HardinessZoneDeserializer(Class<?> vc) {
|
public HardinessZoneDeserializer(Class<?> vc) {
|
||||||
super(vc);
|
super(vc);
|
||||||
@@ -17,13 +20,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
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;
|
||||||
@@ -13,19 +13,20 @@ import java.io.InputStream;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the Jackson Library to deserialize a String to a {@link Image}
|
||||||
|
*/
|
||||||
public class PlantImageDeserializer extends JsonDeserializer<Image> {
|
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(new File(imageUrl.toURI()))) {
|
try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) {
|
||||||
result = new Image(is);
|
result = new Image(is);
|
||||||
} catch (IllegalArgumentException | URISyntaxException 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;
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
package ch.zhaw.gartenverwaltung.gardenplan;
|
package ch.zhaw.gartenverwaltung.models;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.io.GardenPlan;
|
import ch.zhaw.gartenverwaltung.io.CropList;
|
||||||
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
import ch.zhaw.gartenverwaltung.io.JsonGardenPlan;
|
|
||||||
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
|
|
||||||
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
|
||||||
import ch.zhaw.gartenverwaltung.types.Crop;
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
import ch.zhaw.gartenverwaltung.types.Plant;
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
import ch.zhaw.gartenverwaltung.types.Task;
|
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.io.IOException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -20,21 +18,24 @@ import java.util.Optional;
|
|||||||
/**
|
/**
|
||||||
* The Gardenplan model manages the crops in the gardenplan.
|
* The Gardenplan model manages the crops in the gardenplan.
|
||||||
*/
|
*/
|
||||||
public class Gardenplanmodel {
|
public class Garden {
|
||||||
private GardenPlan gardenPlan;
|
private final CropList cropList;
|
||||||
private List<Crop> cropList;
|
private final ListProperty<Crop> plantedCrops = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
private TaskListModel taskListModel;
|
private final GardenSchedule gardenSchedule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of Gardenplan model
|
* Constructor of Gardenplan model
|
||||||
*
|
*
|
||||||
* @param taskListModel holds a reference to the task list object.
|
* @param gardenSchedule holds a reference to the task list object.
|
||||||
*/
|
*/
|
||||||
public Gardenplanmodel(TaskListModel taskListModel) throws IOException {
|
public Garden(GardenSchedule gardenSchedule, CropList cropList) throws IOException {
|
||||||
this.taskListModel = taskListModel;
|
this.gardenSchedule = gardenSchedule;
|
||||||
gardenPlan = new JsonGardenPlan();
|
this.cropList = cropList;
|
||||||
cropList = new ArrayList<>();
|
plantedCrops.addAll(cropList.getCrops());
|
||||||
cropList = gardenPlan.getCrops();
|
}
|
||||||
|
|
||||||
|
public ListProperty<Crop> getPlantedCrops() {
|
||||||
|
return plantedCrops;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,12 +49,13 @@ public class Gardenplanmodel {
|
|||||||
* @throws PlantNotFoundException If the plant is not found in the database.
|
* @throws PlantNotFoundException If the plant is not found in the database.
|
||||||
*/
|
*/
|
||||||
public void plantAsCrop(Plant plant, LocalDate plantingDate) throws IOException, HardinessZoneNotSetException, PlantNotFoundException {
|
public void plantAsCrop(Plant plant, LocalDate plantingDate) throws IOException, HardinessZoneNotSetException, PlantNotFoundException {
|
||||||
Crop crop = new Crop(plant.id(),plantingDate);
|
Crop crop = new Crop(plant.id(), plantingDate);
|
||||||
//TODO Add Area to Plant
|
//TODO Add Area to Plant
|
||||||
//crop.withArea(0);
|
//crop.withArea(0);
|
||||||
gardenPlan.saveCrop(crop);
|
cropList.saveCrop(crop);
|
||||||
taskListModel.planTasksForCrop(crop);
|
gardenSchedule.planTasksForCrop(crop);
|
||||||
cropList = gardenPlan.getCrops();
|
plantedCrops.clear();
|
||||||
|
plantedCrops.addAll(cropList.getCrops());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,10 +65,10 @@ public class Gardenplanmodel {
|
|||||||
* @throws IOException If the database cannot be accessed
|
* @throws IOException If the database cannot be accessed
|
||||||
*/
|
*/
|
||||||
public void removeCrop(Crop crop) throws IOException {
|
public void removeCrop(Crop crop) throws IOException {
|
||||||
|
cropList.removeCrop(crop);
|
||||||
gardenPlan.removeCrop(crop);
|
gardenSchedule.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new));
|
||||||
taskListModel.removeTasksForCrop(crop);
|
plantedCrops.clear();
|
||||||
cropList = gardenPlan.getCrops();
|
plantedCrops.addAll(cropList.getCrops());
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns a list of {@link Crop}s which are currently in the gardenplan.
|
* Returns a list of {@link Crop}s which are currently in the gardenplan.
|
||||||
@@ -74,10 +76,7 @@ public class Gardenplanmodel {
|
|||||||
* @throws IOException If the database cannot be accessed
|
* @throws IOException If the database cannot be accessed
|
||||||
*/
|
*/
|
||||||
public List<Crop> getCrops() throws IOException {
|
public List<Crop> getCrops() throws IOException {
|
||||||
if(!cropList.isEmpty()){
|
return cropList.getCrops();
|
||||||
cropList = gardenPlan.getCrops();
|
|
||||||
}
|
|
||||||
return cropList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,6 +86,6 @@ public class Gardenplanmodel {
|
|||||||
* @throws IOException If the database cannot be accessed
|
* @throws IOException If the database cannot be accessed
|
||||||
*/
|
*/
|
||||||
public Optional<Crop> getCrop(Long cropId) throws IOException {
|
public Optional<Crop> getCrop(Long cropId) throws IOException {
|
||||||
return gardenPlan.getCropById(cropId);
|
return cropList.getCropById(cropId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package ch.zhaw.gartenverwaltung.models;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.Settings;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.*;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class GardenSchedule {
|
||||||
|
private final TaskList taskList;
|
||||||
|
private final PlantList plantList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparators to create sorted Task List
|
||||||
|
*/
|
||||||
|
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
|
||||||
|
static final Comparator<Task> sortByNextExecution = Comparator.comparing(Task::getNextExecution);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to create Database Objects.
|
||||||
|
*/
|
||||||
|
public GardenSchedule(TaskList taskList, PlantList plantList) {
|
||||||
|
this.taskList = taskList;
|
||||||
|
this.plantList = plantList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to save a new Task to Task Database
|
||||||
|
* @param task the Task to save
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public void addTask(Task task) throws IOException {
|
||||||
|
taskList.saveTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to add all Tasks for a new crop
|
||||||
|
* @param crop the crop which is added
|
||||||
|
* @throws PlantNotFoundException if the plantId in the crop doesn't exist in Plant Database
|
||||||
|
* @throws HardinessZoneNotSetException If there is no Hardiness Zone Set in Plant Database
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public void planTasksForCrop(Crop crop) throws PlantNotFoundException, HardinessZoneNotSetException, IOException {
|
||||||
|
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).orElseThrow(PlantNotFoundException::new);
|
||||||
|
int growPhaseGroup = plant.getGrowphaseGroupForDate(crop.getStartDate());
|
||||||
|
for (GrowthPhase growthPhase : plant.lifecycleForGroup(growPhaseGroup)) {
|
||||||
|
for (TaskTemplate taskTemplate : growthPhase.taskTemplates()) {
|
||||||
|
addTask(taskTemplate.generateTask(crop.getStartDate(), crop.getCropId().orElse(0L)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to remove all Tasks for a specific Crop
|
||||||
|
* @param cropId The crop which is removed
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public void removeTasksForCrop(long cropId) throws IOException {
|
||||||
|
taskList.removeTasksForCrop(cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to remove a Task from Database
|
||||||
|
* @param task the Task to remove
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public void removeTask(Task task) throws IOException {
|
||||||
|
taskList.removeTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
|
||||||
|
return taskList.stream().filter(task -> task.getCropId() == cropId).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks
|
||||||
|
* @return List of all Tasks
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getTaskList() throws IOException {
|
||||||
|
return getFilteredTaskList(LocalDate.MIN, LocalDate.MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks for specific Crop
|
||||||
|
* @return List of all Tasks for with given CropID
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getTaskListForCrop(Long cropId) throws IOException {
|
||||||
|
return filterListByCrop(getTaskList(), cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks which are today or in future
|
||||||
|
* @return List of Tasks
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getFutureTasks() throws IOException {
|
||||||
|
return getFilteredTaskList(LocalDate.now(), LocalDate.MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks which are today or in future for specific Crop
|
||||||
|
* @return List of Tasks with given crop ID
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getFutureTasksForCrop(Long cropId) throws IOException {
|
||||||
|
return filterListByCrop(getFutureTasks(), cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks which are today or in past
|
||||||
|
* @return List of Tasks
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getPastTasks() throws IOException {
|
||||||
|
return getFilteredTaskList(LocalDate.MIN, LocalDate.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks which are today or in past for specifc crop
|
||||||
|
* @return List of Tasks with given grop id
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getPastTasksForCrop(Long cropId) throws IOException {
|
||||||
|
return filterListByCrop(getPastTasks(), cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get an List of 7 Tasklists for the next 7 days. Index 0 is Tasklist for Today.
|
||||||
|
* @return List with length 7 (List<List<Task>>)
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<List<Task>> getTasksUpcomingWeek() throws IOException {
|
||||||
|
final int listLength = 7;
|
||||||
|
List<Task> weekTasks = taskList.getTaskList(LocalDate.now(), LocalDate.now().plusDays(listLength - 1));
|
||||||
|
List<List<Task>> dayTaskList = new ArrayList<>();
|
||||||
|
for(int i = 0; i < listLength; i++) {
|
||||||
|
LocalDate date = LocalDate.now().plusDays(i);
|
||||||
|
dayTaskList.add(new ArrayList<>());
|
||||||
|
final int finalI = i;
|
||||||
|
weekTasks.forEach(task -> {
|
||||||
|
LocalDate checkDate = task.getNextExecution();
|
||||||
|
do {
|
||||||
|
if (date.equals(checkDate) && !date.isAfter(task.getEndDate().orElse(LocalDate.MIN))) {
|
||||||
|
dayTaskList.get(finalI).add(task);
|
||||||
|
}
|
||||||
|
checkDate = checkDate.plusDays(task.getInterval().orElse(0));
|
||||||
|
} while (task.getInterval().isPresent() && checkDate.isBefore(LocalDate.now().plusDays(listLength)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return dayTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get an List of 7 Tasklists for the next 7 days. (Filtered Index 0 is Tasklist for Today.
|
||||||
|
* @return List with length 7 (List<List<Task>>)
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<List<Task>> getTasksUpcomingWeekForCrop(Long cropId) throws IOException {
|
||||||
|
List<List<Task>> dayTaskList = getTasksUpcomingWeek();
|
||||||
|
dayTaskList.forEach(taskList -> taskList.removeIf(task -> task.getCropId() != cropId));
|
||||||
|
return dayTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get Tasklist filtered by date.
|
||||||
|
* @param start the start date for the filter
|
||||||
|
* @param end the end date for the filter
|
||||||
|
* @return List of Tasks matched by the filter
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Task> getFilteredTaskList(LocalDate start, LocalDate end) throws IOException {
|
||||||
|
return getSortedTaskList(taskList.getTaskList(start, end), sortByNextExecution);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to sort a Tasklist by a given Comparator
|
||||||
|
* @param taskList The Tasklist to sort
|
||||||
|
* @param comparator the comparator to sort
|
||||||
|
* @return a sorted coppy of the given Tasklist
|
||||||
|
*/
|
||||||
|
private List<Task> getSortedTaskList(List<Task> taskList, Comparator<Task> comparator) {
|
||||||
|
return taskList.stream().sorted(comparator).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlantListModel(PlantDatabase plantDatabase) {
|
|
||||||
this.plantDatabase = plantDatabase;
|
|
||||||
setDefaultZone();
|
setDefaultZone();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDefaultZone() {
|
private void setDefaultZone() {
|
||||||
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config
|
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentZone(HardinessZone currentZone) {
|
public void setCurrentZone(HardinessZone currentZone) {
|
||||||
@@ -72,7 +66,7 @@ public class PlantListModel {
|
|||||||
*/
|
*/
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +95,7 @@ public class PlantListModel {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,16 +110,17 @@ public class PlantListModel {
|
|||||||
if (searchString.length() == 0) {
|
if (searchString.length() == 0) {
|
||||||
return getPlantList(zone);
|
return getPlantList(zone);
|
||||||
} else if (searchString.charAt(0) == '#') {
|
} else if (searchString.charAt(0) == '#') {
|
||||||
try {
|
if (isPositiveIntegral(searchString.substring(1))) {
|
||||||
return getFilteredPlantListById(zone, Long.parseLong(searchString.substring(1)));
|
Long searchId = Long.parseLong(searchString.substring(1));
|
||||||
} catch (NumberFormatException e) {
|
return getFilteredPlantListById(zone, searchId);
|
||||||
|
} else {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String caseInsensitiveSearchString = searchString.toLowerCase();
|
String caseInsensitiveSearchString = searchString.toLowerCase();
|
||||||
return getFilteredPlantList(zone, plant ->
|
return getFilteredPlantList(zone, plant ->
|
||||||
plant.name().toLowerCase().contains(caseInsensitiveSearchString) ||
|
plant.name().toLowerCase().contains(caseInsensitiveSearchString) ||
|
||||||
plant.description().toLowerCase().contains(caseInsensitiveSearchString)
|
plant.description().toLowerCase().contains(caseInsensitiveSearchString)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,15 +164,24 @@ public class PlantListModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @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> getFilteredPlantListBySaisonWithoutGrowthPhase(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
|
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)));
|
return getFilteredPlantList(zone, plant -> plant.lifecycle().stream().anyMatch(growthPhase -> growthPhase.startDate().compareTo(from) >= 0 && (growthPhase.startDate().compareTo(to) <= 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string can safely be parsed as a positive Integral value (short/int/long)
|
||||||
|
*
|
||||||
|
* @param subject The string to be tested
|
||||||
|
* @return Whether the string contains only digits
|
||||||
|
*/
|
||||||
|
private boolean isPositiveIntegral(String subject) {
|
||||||
|
return subject != null && subject.matches("[0-9]+");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,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() {
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package ch.zhaw.gartenverwaltung.taskList;
|
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
|
||||||
import ch.zhaw.gartenverwaltung.io.JsonTaskDatabase;
|
|
||||||
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
|
|
||||||
import ch.zhaw.gartenverwaltung.io.TaskDatabase;
|
|
||||||
import ch.zhaw.gartenverwaltung.types.Crop;
|
|
||||||
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
|
||||||
import ch.zhaw.gartenverwaltung.types.Plant;
|
|
||||||
import ch.zhaw.gartenverwaltung.types.Task;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class TaskListModel {
|
|
||||||
private TaskDatabase taskDatabase;
|
|
||||||
private PlantDatabase plantDatabase;
|
|
||||||
|
|
||||||
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
|
|
||||||
|
|
||||||
public TaskListModel(){
|
|
||||||
taskDatabase = new JsonTaskDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskListModel(TaskDatabase taskDatabase, PlantDatabase plantDatabase) {
|
|
||||||
this.taskDatabase = taskDatabase;
|
|
||||||
this.plantDatabase = plantDatabase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addTask(Task task) throws IOException {
|
|
||||||
taskDatabase.saveTask(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void planTasksForCrop(Crop crop) throws PlantNotFoundException, HardinessZoneNotSetException, IOException {
|
|
||||||
Plant plant = plantDatabase.getPlantById(HardinessZone.ZONE_8A, crop.getPlantId()).orElseThrow(PlantNotFoundException::new);
|
|
||||||
// TODO new exception
|
|
||||||
// TODO HArdiness Zone
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeTasksForCrop(Crop crop) {
|
|
||||||
//TODO implement
|
|
||||||
taskDatabase.removeTasksForCrop(crop);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeTask(Task task) throws IOException {
|
|
||||||
taskDatabase.removeTask(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Task> getTaskList() throws IOException {
|
|
||||||
return getFilteredTaskList(LocalDate.MIN, LocalDate.MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Task> getFutureTasks() throws IOException {
|
|
||||||
return getFilteredTaskList(LocalDate.now(), LocalDate.MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Task> getPastTasks() throws IOException {
|
|
||||||
return getFilteredTaskList(LocalDate.MIN, LocalDate.now());
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Task>[] getTasksUpcomingWeek() throws IOException {
|
|
||||||
List<Task>[] listArray = new List[7];
|
|
||||||
for(int i = 0; i < 7; i++) {
|
|
||||||
LocalDate date = LocalDate.now().plusDays(i);
|
|
||||||
listArray[i] = taskDatabase.getTaskList(date, date);
|
|
||||||
}
|
|
||||||
return listArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Task> getFilteredTaskList(LocalDate start, LocalDate end) throws IOException {
|
|
||||||
return getSortedTaskList(taskDatabase.getTaskList(start, end), sortByStartDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Task> getSortedTaskList(List<Task> taskList, Comparator<Task> comparator) {
|
|
||||||
return taskList.stream().sorted(comparator).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 ]",
|
||||||
|
|||||||
@@ -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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,20 @@ package ch.zhaw.gartenverwaltung.types;
|
|||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
|
|
||||||
public enum Seasons {
|
public enum Seasons {
|
||||||
AllSEASONS("--01-01", "--12-31"),
|
AllSEASONS("--01-01", "--12-31", "All Seasons"),
|
||||||
SPRING("--03-01", "--05-30"),
|
SPRING("--03-01", "--05-30", "Spring"),
|
||||||
SOMMER("--06-01", "--08-30"),
|
SUMMER("--06-01", "--08-30", "Summer"),
|
||||||
AUTUM("--09-01", "--11-30"),
|
AUTUMN("--09-01", "--11-30", "Autumn"),
|
||||||
WINTER("--12-01", "--02-28");
|
WINTER("--12-01", "--02-28", "Winter");
|
||||||
|
|
||||||
public final String startDate;
|
public final String startDate;
|
||||||
public final String endDate;
|
public final String endDate;
|
||||||
|
public final String name;
|
||||||
|
|
||||||
Seasons(String startDate, String endDate) {
|
Seasons(String startDate, String endDate, String name) {
|
||||||
this.startDate = startDate;
|
this.startDate = startDate;
|
||||||
this.endDate = endDate;
|
this.endDate = endDate;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MonthDay getStartDate() {
|
public MonthDay getStartDate() {
|
||||||
@@ -23,4 +25,7 @@ public enum Seasons {
|
|||||||
public MonthDay getEndDate() {
|
public MonthDay getEndDate() {
|
||||||
return MonthDay.parse(this.endDate);
|
return MonthDay.parse(this.endDate);
|
||||||
}
|
}
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ import java.util.Optional;
|
|||||||
* May be created using the builder pattern.
|
* May be created using the builder pattern.
|
||||||
*/
|
*/
|
||||||
public class Task {
|
public class Task {
|
||||||
private long id;
|
private Long id;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String description;
|
private final String description;
|
||||||
private final LocalDate startDate;
|
private final LocalDate startDate;
|
||||||
private Integer interval;
|
private Integer interval;
|
||||||
private LocalDate endDate;
|
private LocalDate endDate;
|
||||||
private Crop cropId;
|
private LocalDate nextExecution;
|
||||||
|
private LocalDate nextNotification;
|
||||||
|
private long cropId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* default constructor
|
* default constructor
|
||||||
@@ -25,20 +27,27 @@ public class Task {
|
|||||||
name= "";
|
name= "";
|
||||||
description= "";
|
description= "";
|
||||||
startDate = LocalDate.now();
|
startDate = LocalDate.now();
|
||||||
|
endDate = startDate;
|
||||||
|
nextExecution = startDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task(String name, String description, LocalDate startDate) {
|
public Task(String name, String description, LocalDate startDate, long cropId) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.startDate = startDate;
|
this.startDate = startDate;
|
||||||
|
this.endDate = startDate;
|
||||||
|
nextExecution = startDate;
|
||||||
|
this.cropId = cropId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval) {
|
public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval, long cropId) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.startDate = startDate;
|
this.startDate = startDate;
|
||||||
|
nextExecution = startDate;
|
||||||
this.endDate = endDate;
|
this.endDate = endDate;
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
|
this.cropId = cropId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder-pattern-style setters
|
// Builder-pattern-style setters
|
||||||
@@ -56,17 +65,37 @@ public class Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInTimePeriode(LocalDate searchStartDate, LocalDate searchEndDate){
|
public boolean isInTimePeriode(LocalDate searchStartDate, LocalDate searchEndDate){
|
||||||
if(startDate.isAfter(searchStartDate) &&startDate.isBefore(searchEndDate)){
|
return endDate.isAfter(searchStartDate) && startDate.isBefore(searchEndDate);
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
public void done(){
|
||||||
|
if(interval != null && !nextExecution.plusDays(interval).isAfter(endDate)){
|
||||||
|
nextExecution = nextExecution.plusDays(interval);
|
||||||
|
} else {
|
||||||
|
nextExecution = null;
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
public boolean isDone(){
|
||||||
|
return nextExecution == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextExecution(LocalDate nextExecution) {
|
||||||
|
this.nextExecution = nextExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextNotification(LocalDate nextNotification) {
|
||||||
|
this.nextNotification = nextNotification;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
public long getId() { return id; }
|
public LocalDate getNextNotification() { return nextNotification; }
|
||||||
|
public LocalDate getNextExecution() { return nextExecution; }
|
||||||
|
public Optional<Long> getId() { return Optional.ofNullable(id); }
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public String getDescription() { return description; }
|
public String getDescription() { return description; }
|
||||||
public LocalDate getStartDate() { return startDate; }
|
public LocalDate getStartDate() { return startDate; }
|
||||||
|
public long getCropId() { return cropId; }
|
||||||
|
|
||||||
public Optional<Integer> getInterval() {
|
public Optional<Integer> getInterval() {
|
||||||
return Optional.ofNullable(interval);
|
return Optional.ofNullable(interval);
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ public class TaskTemplate {
|
|||||||
@JsonProperty
|
@JsonProperty
|
||||||
private Integer interval;
|
private Integer interval;
|
||||||
|
|
||||||
// TODO: reconsider if we need this
|
|
||||||
@JsonProperty
|
|
||||||
private boolean isOptional = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor
|
* Default constructor
|
||||||
* (Used by deserializer)
|
* (Used by deserializer)
|
||||||
@@ -34,6 +30,7 @@ public class TaskTemplate {
|
|||||||
public void setRelativeEndDate(Integer relativeEndDate) {
|
public void setRelativeEndDate(Integer relativeEndDate) {
|
||||||
this.relativeEndDate = relativeEndDate;
|
this.relativeEndDate = relativeEndDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInterval(Integer interval) {
|
public void setInterval(Integer interval) {
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
}
|
}
|
||||||
@@ -44,14 +41,15 @@ public class TaskTemplate {
|
|||||||
this.relativeStartDate = relativeStartDate;
|
this.relativeStartDate = relativeStartDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task generateTask(LocalDate realStartDate) {
|
public Task generateTask(LocalDate realStartDate, long cropId) {
|
||||||
Task task = new Task(name, description, realStartDate.plusDays(relativeStartDate));
|
LocalDate endDate = relativeEndDate != null ? realStartDate.plusDays(relativeEndDate) : null;
|
||||||
if (relativeEndDate != null) {
|
|
||||||
task.withEndDate(realStartDate.plusDays(relativeEndDate));
|
if (interval == null) {
|
||||||
|
this.interval = 0;
|
||||||
}
|
}
|
||||||
if (interval != null) {
|
|
||||||
task.withInterval(interval);
|
return new Task(name, description, realStartDate.plusDays(relativeStartDate), cropId)
|
||||||
}
|
.withInterval(interval)
|
||||||
return task;
|
.withEndDate(endDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?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.ListView?>
|
||||||
|
<?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="781.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="Tasks:">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<ListView fx:id="taskList_listView" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="877.0" VBox.vgrow="ALWAYS">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</ListView>
|
||||||
|
<Button fx:id="addTask_button" mnemonicParsing="false" onAction="#addTask" prefHeight="25.0" prefWidth="45.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Button>
|
||||||
|
<Label text="Pests:" />
|
||||||
|
<ListView fx:id="pests_listView" maxHeight="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</ListView>
|
||||||
|
<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="Set 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="Set Location" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@@ -2,42 +2,210 @@
|
|||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.Pane?>
|
<?import javafx.scene.layout.Pane?>
|
||||||
<?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="1091.0" prefWidth="1060.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.HomeController">
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="729.0" prefWidth="1060.0"
|
|
||||||
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.HomeController">
|
|
||||||
<children>
|
<children>
|
||||||
<VBox layoutX="75.0" layoutY="73.0" prefHeight="729.0" prefWidth="1060.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<ScrollPane fitToWidth="true" prefHeight="1157.0" prefWidth="1060.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<content>
|
||||||
<Label text="Gartenverwaltung">
|
<VBox prefHeight="1091.0" prefWidth="1058.0">
|
||||||
<font>
|
|
||||||
<Font size="34.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Label prefHeight="106.0" prefWidth="1040.0" text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." wrapText="true" />
|
|
||||||
<Label text="Tutorial">
|
|
||||||
<font>
|
|
||||||
<Font name="System Bold" size="18.0" />
|
|
||||||
</font>
|
|
||||||
</Label>
|
|
||||||
<Pane prefHeight="200.0" prefWidth="200.0">
|
|
||||||
<children>
|
<children>
|
||||||
<VBox prefHeight="200.0" prefWidth="1040.0">
|
<Pane prefHeight="1085.0" prefWidth="1018.0">
|
||||||
<children>
|
<children>
|
||||||
<Label text="Task 1" wrapText="true" />
|
<VBox prefHeight="1047.0" prefWidth="1019.0">
|
||||||
<Label text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." wrapText="true" />
|
<children>
|
||||||
|
<Label text="Garden Management">
|
||||||
|
<font>
|
||||||
|
<Font size="34.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="30.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label alignment="TOP_LEFT" prefHeight="22.0" prefWidth="1039.0" text="This Application was created to help the user manage his or her garden. For this the Application has many functionalities:" wrapText="true">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="Base Functionalities:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label text="- The user can select a plant he wants to cultivate.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="62.0" text="- The user can filter the plants according to seasons, hardiness zone and search query">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="62.0" text="- The user can select the harverst or sow date. ">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="102.0" text="- The user can get a detailed information of the plant he wants to harvest.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="122.0" text="- The user can get view the task list of the given plant.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="142.0" text="- The user can get the tasks of the next seven days in the scheduler.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="42.0" text="Advanced Functionalities:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="62.0" text="- The user can edit the task list and add custom tasks.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="212.0" text="- The user can set the area (sqare meter) for the plants.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="232.0" text="- The user can set the location (PLZ) for the plants.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="162.0" text="- The user can set the pesticide which will be used, which will create additonal tasks.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="192.0" text="Weather Forcast:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="212.0" text="- According to the location the weather forcast will crate or delete tasks.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label layoutX="10.0" layoutY="272.0" text="- The user receives notifications that aditional tasks werde created or some tasks were deleted.">
|
||||||
|
<font>
|
||||||
|
<Font size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="Created by:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="14.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewElias" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Elias Csomor" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewPhilippe" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Philippe Giavarini" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewDavid" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="David Guler" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewGian" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Gian-Andrea Hutter" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fx:id="imageViewRoman" fitHeight="100.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="30.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Roman Schenk" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Pane>
|
||||||
</children>
|
</children>
|
||||||
</Pane>
|
<padding>
|
||||||
</children>
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
<padding>
|
</padding>
|
||||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
</VBox>
|
||||||
</padding>
|
</content>
|
||||||
</VBox>
|
</ScrollPane>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
<children>
|
<children>
|
||||||
<HBox maxWidth="35.0" minHeight="35.0" prefHeight="38.0" prefWidth="1110.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<HBox maxWidth="35.0" minHeight="35.0" prefHeight="38.0" prefWidth="1110.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<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="40.0" 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="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
|
||||||
<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="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="My Schedule" />
|
||||||
<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="tutorial_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#showTutorial" prefHeight="38.0" prefWidth="121.0" text="Tutorial" />
|
||||||
<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" />
|
||||||
|
<Button fx:id="settings_button" maxHeight="1.7976931348623157E308" mnemonicParsing="false" onAction="#openSettings" prefHeight="38.0" prefWidth="40.0" HBox.hgrow="NEVER" />
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<AnchorPane fx:id="mainPane" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />
|
<AnchorPane fx:id="mainPane" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />
|
||||||
|
|||||||
@@ -3,17 +3,16 @@
|
|||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.text.Font?>
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane fx:id="myGardenRoot" 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">
|
||||||
<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 +20,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" />
|
<ListView fx:id="myGarden_listView" maxHeight="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.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" />
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
<?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.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?>
|
||||||
@@ -17,7 +15,8 @@
|
|||||||
|
|
||||||
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
|
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
|
||||||
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
|
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
|
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>
|
||||||
@@ -33,15 +32,8 @@
|
|||||||
<TextField fx:id="search_plants" 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>
|
||||||
@@ -71,7 +63,7 @@
|
|||||||
<Font name="System Bold" size="17.0" />
|
<Font name="System Bold" size="17.0" />
|
||||||
</font>
|
</font>
|
||||||
</Label>
|
</Label>
|
||||||
<TitledPane animated="false" text="Saison">
|
<TitledPane animated="false" text="Seasons">
|
||||||
<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>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.ComboBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?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="80.0" prefWidth="374.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.SettingsController">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="14.0" layoutY="14.0" prefHeight="73.0" prefWidth="374.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<HBox prefHeight="23.0" prefWidth="334.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Show Tutorial Menu" HBox.hgrow="ALWAYS" />
|
||||||
|
<CheckBox fx:id="showTutorial_checkBox" mnemonicParsing="false" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets left="10.0" right="10.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="20.0" layoutY="20.0" prefHeight="23.0" prefWidth="334.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Select Default Hardiness Zone" HBox.hgrow="ALWAYS" />
|
||||||
|
<ComboBox fx:id="selectHardinessZone_comboBox" prefWidth="150.0" promptText="Hardniness Zone" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.DatePicker?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?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="259.0" prefWidth="390.0" xmlns="http://javafx.com/javafx/17"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TaskFormularController">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="14.0" layoutY="14.0" prefHeight="272.0" prefWidth="390.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Task name:" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextField fx:id="taskName_field" promptText="Task Name" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="77.0" prefWidth="350.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Description:" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextArea fx:id="description_area" prefHeight="73.0" prefWidth="206.0" promptText="Description" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="30.0" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Start Date:" HBox.hgrow="ALWAYS" />
|
||||||
|
<DatePicker fx:id="start_datePicker" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="143.0" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="End Date:" HBox.hgrow="ALWAYS" />
|
||||||
|
<DatePicker fx:id="end_datePicker" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="30.0" layoutY="30.0" prefHeight="35.0" prefWidth="560.0">
|
||||||
|
<children>
|
||||||
|
<Label maxWidth="1.7976931348623157E308" text="Interval:" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextField fx:id="interval_field" promptText="Interval (e.g. 0, 1, 3 ...)" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="64.0" prefWidth="298.0" xmlns="http://javafx.com/javafx/17"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TextFieldFormularController">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="CENTER" layoutX="153.0" layoutY="33.0" prefHeight="100.0" prefWidth="200.0" spacing="10.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="description_label" maxWidth="1.7976931348623157E308" text="Label" HBox.hgrow="ALWAYS" />
|
||||||
|
<TextField fx:id="text_area" HBox.hgrow="NEVER" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.control.CheckBox?>
|
||||||
|
<?import javafx.scene.control.Separator?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.BorderPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
<?import javafx.scene.text.Text?>
|
||||||
|
<?import javafx.scene.text.TextFlow?>
|
||||||
|
|
||||||
|
<BorderPane maxHeight="470.0" maxWidth="724.0" minHeight="470.0" minWidth="724.0" prefHeight="470.0" prefWidth="724.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.TutorialController">
|
||||||
|
<bottom>
|
||||||
|
<HBox alignment="CENTER" prefHeight="0.0" prefWidth="724.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<CheckBox mnemonicParsing="false" text="Don't show again" />
|
||||||
|
<Separator prefWidth="50.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ButtonBar prefHeight="40.0" prefWidth="200.0">
|
||||||
|
<buttons>
|
||||||
|
<Button cancelButton="true" contentDisplay="CENTER" graphicTextGap="5.0" mnemonicParsing="false" text="Close" onAction="#closeTutorial"/>
|
||||||
|
<Button fx:id="previousPageButton" mnemonicParsing="false" text="Previous" onAction="#viewPreviousPage"/>
|
||||||
|
<Button fx:id="nextPageButton" defaultButton="true" mnemonicParsing="false" text="Next" onAction="#viewNextPage" />
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</bottom>
|
||||||
|
<center>
|
||||||
|
<StackPane fx:id="tourPages" prefHeight="150.0" prefWidth="200.0" BorderPane.alignment="CENTER">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="30.0" layoutY="26.0" opacity="0.0" prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Crops">
|
||||||
|
<font>
|
||||||
|
<Font size="24.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Separator prefWidth="200.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Separator>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="In the "My Garden" tab, you can see all of the plants you're planning on growing.">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Text>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="90.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="To get started, click on the "Add new Plant" button in the bottom left. This will open a list of all the plants you can grow. Use the search bar at the top, the filters on the left, or simply browse the list until you've found a plant you want to grow in your garden." wrappingWidth="345.80316162109375" />
|
||||||
|
</children>
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="imgAddNewPlant" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="200.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Once you've found what you're looking for, click the "Select Sow/Harvest Date" button at the bottom of the window. In the subsequently shown dialog, you can select the date you'd like to sow or harvest the crop. The date selector shows possible dates in green. Confirm your selection with the "Save" button." />
|
||||||
|
</children>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="imgSelectDate" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
</VBox>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Managing Your Tasks">
|
||||||
|
<font>
|
||||||
|
<Font size="24.0" />
|
||||||
|
</font>
|
||||||
|
</Text>
|
||||||
|
<Separator prefWidth="200.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="15.0" top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Separator>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="In the "My Schedule" tab, you can see all of the tasks you need to complete for a successful harvest.">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Text>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="90.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="You can view the tasks for a specific crop by selecting it from the left sidebar. To see all tasks again, click the "Clear Filter" button at the bottom." wrappingWidth="345.80316162109375" />
|
||||||
|
</children>
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets bottom="20.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="imgTaskList" fitHeight="98.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox prefHeight="100.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<TextFlow lineSpacing="4.0" prefHeight="200.0" prefWidth="500.0">
|
||||||
|
<children>
|
||||||
|
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="You can also add your own custom tasks, by clicking the "Add Task" button. In the subsequently shown dialog, you can enter the corresponding details. Note: If you want to make a task recurring, you need to set both an interval (in days) AND an end date, so the task won't repeat for all eternity." />
|
||||||
|
</children>
|
||||||
|
</TextFlow>
|
||||||
|
<Separator prefWidth="200.0" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" />
|
||||||
|
</children></HBox>
|
||||||
|
</children>
|
||||||
|
<opaqueInsets>
|
||||||
|
<Insets />
|
||||||
|
</opaqueInsets>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
<BorderPane.margin>
|
||||||
|
<Insets />
|
||||||
|
</BorderPane.margin>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</padding>
|
||||||
|
</StackPane>
|
||||||
|
</center>
|
||||||
|
</BorderPane>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.button{
|
||||||
|
-fx-text-fill: rgb(49, 89, 23);
|
||||||
|
-fx-border-color: rgb(49, 89, 23);
|
||||||
|
-fx-border-radius: 5;
|
||||||
|
-fx-padding: 3 6 6 6;
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -31,9 +31,8 @@
|
|||||||
"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
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -53,9 +52,8 @@
|
|||||||
"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
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -76,8 +74,7 @@
|
|||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": null,
|
"relativeEndDate": null,
|
||||||
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
|
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
|
||||||
"interval": null,
|
"interval": null
|
||||||
"isOptional": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -94,6 +91,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,
|
||||||
@@ -105,8 +103,7 @@
|
|||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": 0,
|
"relativeEndDate": 0,
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
"interval": null,
|
"interval": null
|
||||||
"isOptional": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -115,6 +112,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,
|
||||||
@@ -128,8 +126,7 @@
|
|||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": null,
|
"relativeEndDate": null,
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
"interval": 15,
|
"interval": 15
|
||||||
"isOptional": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -138,6 +135,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,
|
||||||
@@ -149,8 +147,7 @@
|
|||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": 14,
|
"relativeEndDate": 14,
|
||||||
"description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.",
|
"description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.",
|
||||||
"interval": null,
|
"interval": null
|
||||||
"isOptional": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -181,17 +178,15 @@
|
|||||||
"litersPerSqM": 15,
|
"litersPerSqM": 15,
|
||||||
"interval": 4,
|
"interval": 4,
|
||||||
"notes": [
|
"notes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"taskTemplates": [
|
"taskTemplates": [
|
||||||
{
|
{
|
||||||
"name": "hilling",
|
"name": "Plant Sets",
|
||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": 0,
|
"relativeEndDate": 0,
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
"description": "Plant the sets about 5cm deep into the soil.",
|
||||||
"interval": null,
|
"interval": null
|
||||||
"isOptional": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -214,8 +209,7 @@
|
|||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": null,
|
"relativeEndDate": null,
|
||||||
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
"interval": 15,
|
"interval": 15
|
||||||
"isOptional": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -229,7 +223,6 @@
|
|||||||
"litersPerSqM": 0,
|
"litersPerSqM": 0,
|
||||||
"interval": null,
|
"interval": null,
|
||||||
"notes": [
|
"notes": [
|
||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"taskTemplates": [
|
"taskTemplates": [
|
||||||
@@ -238,8 +231,7 @@
|
|||||||
"relativeStartDate": 0,
|
"relativeStartDate": 0,
|
||||||
"relativeEndDate": 14,
|
"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",
|
"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,
|
"interval": null
|
||||||
"isOptional": false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,68 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"id" : 1,
|
"id": 1,
|
||||||
"name" : "sow plant",
|
"name": "sow plant",
|
||||||
"description": "Plant the seeds, crops in de bed.",
|
"description": "Plant the seeds, crops in de bed.",
|
||||||
"startDate" : "2022-05-01",
|
"startDate": "2022-05-01",
|
||||||
"endDate" : "2022-05-01",
|
"nextExecution": "2022-05-01",
|
||||||
"interval" : 0,
|
"nextNotification": "2022-05-01",
|
||||||
"cropID" : 0
|
"endDate": "2022-05-01",
|
||||||
|
"interval": null,
|
||||||
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 2,
|
"id": 2,
|
||||||
"name" : "water plant",
|
"name": "water plant",
|
||||||
"description": "water the plant, so that the soil is wet around the plant.",
|
"description": "water the plant, so that the soil is wet around the plant.",
|
||||||
"startDate" : "2022-05-01",
|
"startDate": "2022-05-01",
|
||||||
"endDate" : "2022-09-01",
|
"nextExecution": "2022-05-01",
|
||||||
"interval" : 2,
|
"nextNotification": "2022-05-01",
|
||||||
"cropID" : 0
|
"endDate": "2022-09-01",
|
||||||
|
"interval": 2,
|
||||||
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 3,
|
"id": 3,
|
||||||
"name" : "fertilize plant",
|
"name": "fertilize plant",
|
||||||
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
||||||
"startDate" : "2022-06-01",
|
"startDate": "2022-06-01",
|
||||||
"endDate" : "2022-08-01",
|
"nextExecution": "2022-06-01",
|
||||||
"interval" : 28,
|
"nextNotification": "2022-06-01",
|
||||||
"cropID" : 0
|
"endDate": "2022-08-01",
|
||||||
|
"interval": 28,
|
||||||
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 4,
|
"id": 4,
|
||||||
"name" : "covering plant",
|
"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",
|
"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",
|
"startDate": "2022-07-01",
|
||||||
"endDate" : "2022-07-01",
|
"nextExecution": "2022-07-01",
|
||||||
"interval" : 0,
|
"nextNotification": "2022-07-01",
|
||||||
"cropID" : 0
|
"endDate": "2022-07-01",
|
||||||
|
"interval": null,
|
||||||
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 5,
|
"id": 5,
|
||||||
"name" : "look after plant",
|
"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",
|
"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",
|
"startDate": "2022-05-01",
|
||||||
"endDate" : "2022-09-01",
|
"nextExecution": "2022-05-01",
|
||||||
"interval" : 5,
|
"nextNotification": "2022-05-01",
|
||||||
"cropID" : 0
|
"endDate": "2022-09-01",
|
||||||
|
"interval": 5,
|
||||||
|
"cropId": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : 6,
|
"id": 6,
|
||||||
"name" : "harvest plant",
|
"name": "harvest plant",
|
||||||
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
||||||
"startDate" : "2022-09-01",
|
"startDate": "2022-09-01",
|
||||||
"endDate" : "2022-09-01",
|
"nextExecution": "2022-09-01",
|
||||||
"interval" : 0,
|
"nextNotification": "2022-09-01",
|
||||||
"cropID" : 0
|
"endDate": "2022-09-01",
|
||||||
|
"interval": null,
|
||||||
|
"cropId": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -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("test-user-crops.json");
|
||||||
|
private final URL testFile = this.getClass().getResource("template-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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8,25 +8,28 @@ 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.net.URL;
|
||||||
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 {
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
PlantDatabase testDatabase;
|
|
||||||
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
|
public class JsonPlantListTest {
|
||||||
|
private final URL testFile = this.getClass().getResource("test-plantdb.json");
|
||||||
|
PlantList testDatabase;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void connectToDb() {
|
void connectToDb() {
|
||||||
testDatabase = new JsonPlantDatabase();
|
assertNotNull(testFile);
|
||||||
|
testDatabase = new JsonPlantList(testFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@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 +43,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 +70,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);
|
||||||
}
|
}
|
||||||
@@ -1,85 +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.time.LocalDate;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class JsonTaskDatabaseTest {
|
|
||||||
|
|
||||||
TaskDatabase testDatabase;
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("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(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 idProvider works")
|
|
||||||
@Test
|
|
||||||
@DisplayName("Add task.")
|
|
||||||
void addTask(){
|
|
||||||
Task task = new Task("Testtask","This is a test Task.",LocalDate.parse("01.05.2022",formatter));
|
|
||||||
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)).withId(2);
|
|
||||||
try {
|
|
||||||
testDatabase.removeTask(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(2,taskList.size());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
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("yyyy-MM-dd");
|
||||||
|
|
||||||
|
private final URL dbDataSource = this.getClass().getResource("test-taskdb.json");
|
||||||
|
private final URL testFile = this.getClass().getResource("template-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(dbDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check if results are retrieved completely")
|
||||||
|
void getTasks() {
|
||||||
|
|
||||||
|
List<Task> taskList;
|
||||||
|
try {
|
||||||
|
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter),
|
||||||
|
LocalDate.parse("2022-05-31", 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;
|
||||||
|
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("2022-05-31", formatter), 1)
|
||||||
|
.withId(2);
|
||||||
|
try {
|
||||||
|
testDatabase.removeTask(task);
|
||||||
|
List<Task> taskList;
|
||||||
|
|
||||||
|
taskList = testDatabase.getTaskList(LocalDate.parse("2022-04-30", formatter),
|
||||||
|
LocalDate.parse("2022-05-31", formatter));
|
||||||
|
|
||||||
|
Assertions.assertEquals(2, taskList.size());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getTaskForCrop() {
|
||||||
|
List<Task> taskList;
|
||||||
|
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;
|
||||||
|
try {
|
||||||
|
testDatabase.removeTasksForCrop(0);
|
||||||
|
taskList = testDatabase.getTaskForCrop(0);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.assertEquals(0, taskList.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +1,32 @@
|
|||||||
package ch.zhaw.gartenverwaltung.gardenplan;
|
package ch.zhaw.gartenverwaltung.models;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.io.*;
|
import ch.zhaw.gartenverwaltung.io.*;
|
||||||
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
|
|
||||||
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
|
||||||
import ch.zhaw.gartenverwaltung.types.*;
|
import ch.zhaw.gartenverwaltung.types.*;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.internal.stubbing.answers.DoesNothing;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
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.LocalDate;
|
||||||
import java.time.MonthDay;
|
import java.time.MonthDay;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
public class GardenPlanModelTest {
|
public class GardenPlanModelTest {
|
||||||
GardenPlan gardenPlan;
|
private final URL dbDataSource = JsonCropListTest.class.getResource("test-user-crops.json");
|
||||||
List<Crop> cropList;
|
private final URL testFile = JsonCropListTest.class.getResource("template-user-crops.json");
|
||||||
|
|
||||||
|
CropList cropList;
|
||||||
|
List<Crop> exampleCrops;
|
||||||
Crop exampleCropOnion;
|
Crop exampleCropOnion;
|
||||||
Crop exampleCropCarrot;
|
Crop exampleCropCarrot;
|
||||||
Crop exampleCrop1;
|
Crop exampleCrop1;
|
||||||
@@ -30,10 +34,12 @@ public class GardenPlanModelTest {
|
|||||||
Crop exampleCrop3;
|
Crop exampleCrop3;
|
||||||
Plant examplePlantOnion;
|
Plant examplePlantOnion;
|
||||||
Plant examplePlantCarrot;
|
Plant examplePlantCarrot;
|
||||||
Gardenplanmodel model;
|
Garden model;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() throws IOException {
|
void setUp() throws IOException, URISyntaxException {
|
||||||
|
|
||||||
|
|
||||||
examplePlantOnion = new Plant(
|
examplePlantOnion = new Plant(
|
||||||
0,
|
0,
|
||||||
"summertime onion",
|
"summertime onion",
|
||||||
@@ -49,8 +55,8 @@ public class GardenPlanModelTest {
|
|||||||
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(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<>())));
|
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.now());
|
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023, 3, 1))
|
||||||
exampleCropOnion.withId(13);
|
.withId(3);
|
||||||
examplePlantCarrot = new Plant(
|
examplePlantCarrot = new Plant(
|
||||||
1,
|
1,
|
||||||
"Early Carrot",
|
"Early Carrot",
|
||||||
@@ -61,49 +67,58 @@ public class GardenPlanModelTest {
|
|||||||
"sandy to loamy, loose soil, free of stones",
|
"sandy to loamy, loose soil, free of stones",
|
||||||
new ArrayList<>(),
|
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<>())));
|
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 = new Crop(examplePlantCarrot.id(), LocalDate.now())
|
||||||
exampleCropCarrot.withId(5);
|
.withId(5);
|
||||||
|
|
||||||
exampleCrop1 = new Crop(1, LocalDate.of(2023,2,25));
|
exampleCrop1 = new Crop(1, LocalDate.of(2023, 2, 25))
|
||||||
exampleCrop1.withId(0);
|
.withId(0)
|
||||||
exampleCrop1.withArea(0.5);
|
.withArea(0.5);
|
||||||
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
|
exampleCrop2 = new Crop(1, LocalDate.of(2023, 3, 1))
|
||||||
exampleCrop2.withId(1);
|
.withId(1)
|
||||||
exampleCrop2.withArea(0.5);
|
.withArea(0.5);
|
||||||
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,25));
|
exampleCrop3 = new Crop(0, LocalDate.of(2023, 3, 1))
|
||||||
exampleCrop3.withId(2);
|
.withId(2)
|
||||||
exampleCrop3.withArea(1.5);
|
.withArea(1.0);
|
||||||
|
|
||||||
cropList = new ArrayList<>();
|
exampleCrops = new ArrayList<>();
|
||||||
cropList.add(exampleCrop1);
|
exampleCrops.add(exampleCrop1);
|
||||||
cropList.add(exampleCrop2);
|
exampleCrops.add(exampleCrop2);
|
||||||
cropList.add(exampleCrop3);
|
exampleCrops.add(exampleCrop3);
|
||||||
gardenPlan = mockGardenPlan(cropList);
|
cropList = mockCropList(exampleCrops);
|
||||||
|
|
||||||
TaskListModel taskListModel = new TaskListModel(new JsonTaskDatabase(), new JsonPlantDatabase());
|
// Reset Crop "database" before test
|
||||||
model = new Gardenplanmodel(taskListModel);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
GardenPlan mockGardenPlan(List<Crop> cropList) throws IOException {
|
CropList mockCropList(List<Crop> cropList) throws IOException {
|
||||||
GardenPlan gardenPlan = mock(GardenPlan.class);
|
CropList croplist = mock(CropList.class);
|
||||||
when(gardenPlan.getCrops()).thenReturn(cropList);
|
when(croplist.getCrops()).thenReturn(cropList);
|
||||||
when(gardenPlan.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
|
when(croplist.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
|
||||||
when(gardenPlan.getCropById(13)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
|
when(croplist.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
|
||||||
return gardenPlan;
|
return croplist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
||||||
|
|
||||||
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
|
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023, 3, 1));
|
||||||
assertTrue(model.getCrops().contains(exampleCropOnion));
|
Optional<Crop> exampleCrop = model.getCrop(2L);
|
||||||
|
assertTrue(exampleCrop.isPresent());
|
||||||
|
assertEquals(model.getCrops().get(2), exampleCrop.get());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void removeCrop() throws IOException {
|
void removeCrop() throws IOException {
|
||||||
exampleCrop1.withId(2);
|
exampleCrop1.withId(2);
|
||||||
exampleCrop1.withArea(1.500000);
|
exampleCrop1.withArea(1.5);
|
||||||
model.removeCrop(exampleCrop1);
|
model.removeCrop(exampleCrop1);
|
||||||
assertEquals(2,model.getCrops().size());
|
assertEquals(2, model.getCrops().size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
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, 2, 2);
|
||||||
|
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.now().plusDays(7 - 1))).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.now().minusDays(10), LocalDate.now().plusDays(4),2, 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(2, dayList.get(0).size());
|
||||||
|
assertTrue(dayList.get(0).contains(exampleTaskList.get(2)));
|
||||||
|
assertTrue(dayList.get(0).contains(exampleTaskList.get(4)));
|
||||||
|
|
||||||
|
//Check day 1
|
||||||
|
assertEquals(1, dayList.get(1).size());
|
||||||
|
assertTrue(dayList.get(1).contains(exampleTaskList.get(1)));
|
||||||
|
|
||||||
|
//Check day 2
|
||||||
|
assertEquals(1, dayList.get(2).size());
|
||||||
|
assertTrue(dayList.get(2).contains(exampleTaskList.get(4)));
|
||||||
|
//Check day 3
|
||||||
|
assertEquals(0, dayList.get(3).size());
|
||||||
|
//Check day 4
|
||||||
|
assertEquals(1, dayList.get(4).size());
|
||||||
|
assertTrue(dayList.get(4).contains(exampleTaskList.get(4)));
|
||||||
|
//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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)));
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id" : "1",
|
|
||||||
"name" : "sow plant",
|
|
||||||
"description": "Plant the seeds/ crops in de bed.",
|
|
||||||
"startDate" : "01.05.2022",
|
|
||||||
"endDate" : "01.05.2022",
|
|
||||||
"interval" : "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id" : "2",
|
|
||||||
"name" : "water plant",
|
|
||||||
"description": "water the plant, so that the soil is wet around the plant.",
|
|
||||||
"startDate" : "01.05.2022",
|
|
||||||
"endDate" : "01.09.2022",
|
|
||||||
"interval" : "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id" : "3",
|
|
||||||
"name" : "fertilize plant",
|
|
||||||
"description": "The fertilizer has to be mixed with water. Then fertilize the plant's soil with the mixture",
|
|
||||||
"startDate" : "01.06.2022",
|
|
||||||
"endDate" : "01.08.2022",
|
|
||||||
"interval" : "28"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id" : "4",
|
|
||||||
"name" : "covering plant",
|
|
||||||
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
|
|
||||||
"startDate" : "15.07.2022",
|
|
||||||
"endDate" : "15.07.2022",
|
|
||||||
"interval" : "null"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id" : "5",
|
|
||||||
"name" : "look after plant",
|
|
||||||
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
|
|
||||||
"startDate" : "01.05.2022",
|
|
||||||
"endDate" : "01.09.2022",
|
|
||||||
"interval" : "5"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id" : "6",
|
|
||||||
"name" : "harvest plant",
|
|
||||||
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
|
||||||
"startDate" : "01.09.2022",
|
|
||||||
"endDate" : "01.09.2022",
|
|
||||||
"interval" : "null"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id" : 1,
|
||||||
|
"name" : "sow plant",
|
||||||
|
"description": "Plant the seeds, crops in de bed.",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-05-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 2,
|
||||||
|
"name" : "water plant",
|
||||||
|
"description": "water the plant, so that the soil is wet around the plant.",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 2,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 3,
|
||||||
|
"name" : "fertilize plant",
|
||||||
|
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
||||||
|
"startDate" : "2022-06-01",
|
||||||
|
"endDate" : "2022-08-01",
|
||||||
|
"interval" : 28,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 4,
|
||||||
|
"name" : "covering plant",
|
||||||
|
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
|
||||||
|
"startDate" : "2022-07-01",
|
||||||
|
"endDate" : "2022-07-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 5,
|
||||||
|
"name" : "look after plant",
|
||||||
|
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 5,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 6,
|
||||||
|
"name" : "harvest plant",
|
||||||
|
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
||||||
|
"startDate" : "2022-09-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"cropId": 0,
|
||||||
|
"plantId": 1,
|
||||||
|
"startDate": "2023-02-25",
|
||||||
|
"area": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cropId": 1,
|
||||||
|
"plantId": 1,
|
||||||
|
"startDate": "2023-03-01",
|
||||||
|
"area": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cropId": 2,
|
||||||
|
"plantId": 0,
|
||||||
|
"startDate": "2023-03-25",
|
||||||
|
"area": 1.5
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"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 enough so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.",
|
||||||
|
"interval": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"group": 0,
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "03-10",
|
||||||
|
"endDate": "05-10",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "PLANT",
|
||||||
|
"group": 0,
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "05-10",
|
||||||
|
"endDate": "05-20",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"group": 0,
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "15,30,2",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[
|
||||||
|
]
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[
|
||||||
|
]
|
||||||