commit
c653005652
|
@ -27,3 +27,6 @@ These branches are for bugfixes.
|
||||||
|
|
||||||
These branches are for javadoc and project documentation (such as the readme, class diagrams etc.).
|
These branches are for javadoc and project documentation (such as the readme, class diagrams etc.).
|
||||||
|
|
||||||
|
|
||||||
|
## User Manual
|
||||||
|
- Search Plant List: if first Char is '#': only exact match in ID.
|
||||||
|
|
|
@ -5,7 +5,7 @@ plugins {
|
||||||
id 'org.beryx.jlink' version '2.25.0'
|
id 'org.beryx.jlink' version '2.25.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'ch.zhaw.pm3'
|
group 'ch.zhaw.gartenverwaltung'
|
||||||
version '1.0-SNAPSHOT'
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -37,6 +37,10 @@ dependencies {
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
|
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
||||||
|
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.4'
|
||||||
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4'
|
||||||
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4'
|
||||||
|
testImplementation 'org.mockito:mockito-core:4.3.+'
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
<diagram program="umletino" version="15.0.0"><zoom_level>10</zoom_level><element><id>UMLPackage</id><coordinates><x>390</x><y>60</y><w>340</w><h>100</h></coordinates><panel_attributes>UI</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>390</x><y>180</y><w>460</w><h>120</h></coordinates><panel_attributes>Domain</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>390</x><y>330</y><w>460</w><h>120</h></coordinates><panel_attributes>Technical Services</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>400</x><y>90</y><w>100</w><h>60</h></coordinates><panel_attributes>Views (JFX)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>400</x><y>220</y><w>100</w><h>60</h></coordinates><panel_attributes>IO</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>400</x><y>370</y><w>100</w><h>60</h></coordinates><panel_attributes>Jackson</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>510</x><y>220</y><w>100</w><h>60</h></coordinates><panel_attributes>Types</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>620</x><y>220</y><w>100</w><h>60</h></coordinates><panel_attributes>Models</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>510</x><y>90</y><w>110</w><h>60</h></coordinates><panel_attributes>Controllers (JFX)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>510</x><y>370</y><w>100</w><h>60</h></coordinates><panel_attributes>Logging</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>620</x><y>370</y><w>100</w><h>60</h></coordinates><panel_attributes>JavaFX</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>730</x><y>220</y><w>110</w><h>60</h></coordinates><panel_attributes>Services</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLPackage</id><coordinates><x>730</x><y>370</y><w>110</w><h>60</h></coordinates><panel_attributes>HTTP/API</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>570</x><y>150</y><w>90</w><h>70</h></coordinates><panel_attributes>lt=.>
|
||||||
|
</panel_attributes><additional_attributes>70;10;10;50</additional_attributes></element><element><id>Relation</id><coordinates><x>570</x><y>290</y><w>110</w><h>80</h></coordinates><panel_attributes>lt=.>
|
||||||
|
</panel_attributes><additional_attributes>90;10;10;60</additional_attributes></element><element><id>Relation</id><coordinates><x>340</x><y>120</y><w>350</w><h>400</h></coordinates><panel_attributes>lt=.>
|
||||||
|
</panel_attributes><additional_attributes>60;10;10;10;10;380;330;380;330;310</additional_attributes></element><element><id>Relation</id><coordinates><x>770</x><y>270</y><w>30</w><h>120</h></coordinates><panel_attributes>lt=.>
|
||||||
|
</panel_attributes><additional_attributes>10;10;10;100</additional_attributes></element><element><id>Relation</id><coordinates><x>360</x><y>250</y><w>60</w><h>180</h></coordinates><panel_attributes>lt=.>
|
||||||
|
</panel_attributes><additional_attributes>40;10;10;10;10;160;40;160</additional_attributes></element><element><id>Relation</id><coordinates><x>610</x><y>120</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=.>
|
||||||
|
</panel_attributes><additional_attributes>10;10;50;10;50;100</additional_attributes></element></diagram>
|
Binary file not shown.
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,19 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
private static HardinessZone currentHardinessZone;
|
||||||
|
|
||||||
|
static {
|
||||||
|
currentHardinessZone = HardinessZone.ZONE_8A;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HardinessZone getCurrentHardinessZone() {
|
||||||
|
return currentHardinessZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
|
||||||
|
Config.currentHardinessZone = currentHardinessZone;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Pest;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CropDetailController {
|
||||||
|
private Crop crop = null;
|
||||||
|
private final PlantListModel plantListModel = new PlantListModel();
|
||||||
|
private final TaskListModel taskListModel = new TaskListModel();
|
||||||
|
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView imageView;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button area_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label area_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label cropName_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label description_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox growthPhases_vbox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label location_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label light_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button location_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox pests_vbox;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label soil_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label spacing_label;
|
||||||
|
|
||||||
|
public CropDetailController() throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void editTaskList(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void goBack(ActionEvent event) {
|
||||||
|
Stage stage = (Stage) imageView.getScene().getWindow();
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void setArea(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void setLocation(ActionEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException {
|
||||||
|
this.crop = crop;
|
||||||
|
Plant plant = plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0);
|
||||||
|
cropName_label.setText(plant.name());
|
||||||
|
description_label.setText(plant.description());
|
||||||
|
light_label.setText(String.valueOf(plant.light()));
|
||||||
|
soil_label.setText(plant.soil());
|
||||||
|
spacing_label.setText(plant.spacing());
|
||||||
|
if (plant.image() != null) {
|
||||||
|
imageView.setImage(plant.image());
|
||||||
|
}
|
||||||
|
area_label.setText("");
|
||||||
|
location_label.setText("");
|
||||||
|
createTaskLists(crop);
|
||||||
|
createPestList(plant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTaskLists(Crop crop) throws IOException {
|
||||||
|
List<Task> taskList = taskListModel.getTaskListForCrop(crop.getCropId().get());
|
||||||
|
for (Task task : taskList) {
|
||||||
|
Label label = new Label(task.getDescription());
|
||||||
|
growthPhases_vbox.getChildren().add(label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPestList(Plant plant) {
|
||||||
|
List<Pest> pests = plant.pests();
|
||||||
|
for (Pest pest : pests) {
|
||||||
|
Label label = new Label(pest.name() + ":");
|
||||||
|
label.setStyle("-fx-font-weight: bold");
|
||||||
|
HBox hBox = new HBox();
|
||||||
|
hBox.fillHeightProperty();
|
||||||
|
Label label1 = new Label(pest.description());
|
||||||
|
label1.setAlignment(Pos.TOP_LEFT);
|
||||||
|
label1.setWrapText(true);
|
||||||
|
label1.setMaxWidth(600);
|
||||||
|
label1.setMaxHeight(100);
|
||||||
|
Button button = new Button("Get Counter Measures");
|
||||||
|
hBox.getChildren().addAll(label1, button);
|
||||||
|
pests_vbox.getChildren().addAll(label, hBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,9 +10,9 @@ 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("hello-view.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("MainFXML.fxml"));
|
||||||
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
|
Scene scene = new Scene(fxmlLoader.load());
|
||||||
stage.setTitle("Hello!");
|
stage.setTitle("Gartenverwaltung");
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
public class HomeController
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class MainFXMLController implements Initializable {
|
||||||
|
/**
|
||||||
|
* Caching the panes
|
||||||
|
*/
|
||||||
|
private final Map<String, AnchorPane> panes = new HashMap<>();
|
||||||
|
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button home_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private AnchorPane mainPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button myPlants_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button mySchedule_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button plants_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void goToHome(ActionEvent event) throws IOException {
|
||||||
|
loadPane("Home.fxml");
|
||||||
|
styleChangeButton(home_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void goToMyPlants(ActionEvent event) throws IOException {
|
||||||
|
loadPane("MyPlants.fxml");
|
||||||
|
styleChangeButton(myPlants_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void goToMySchedule(ActionEvent event) throws IOException {
|
||||||
|
loadPane("MySchedule.fxml");
|
||||||
|
styleChangeButton(mySchedule_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void goToPlants(ActionEvent event) throws IOException {
|
||||||
|
loadPane("Plants.fxml");
|
||||||
|
styleChangeButton(plants_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the mainPane with the selected fxml file.
|
||||||
|
* set HGrow and VGrow to parent AnchorPane.
|
||||||
|
* Sends MainController to other Controllers.
|
||||||
|
* @param fxmlFile string of fxml file
|
||||||
|
* @throws IOException exception when file does not exist
|
||||||
|
*/
|
||||||
|
public void loadPane(String fxmlFile) throws IOException {
|
||||||
|
|
||||||
|
AnchorPane anchorPane = panes.get(fxmlFile);
|
||||||
|
if (anchorPane == null) {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
|
||||||
|
anchorPane = loader.load();
|
||||||
|
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 void styleChangeButton(Button button) {
|
||||||
|
//ToDo changeStyle of the menu buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loads the default FXML File
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
try {
|
||||||
|
loadPane("Home.fxml");
|
||||||
|
styleChangeButton(home_button);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class MyPlantsController implements Initializable {
|
||||||
|
MainFXMLController mainController;
|
||||||
|
private final TaskListModel taskListModel = new TaskListModel();
|
||||||
|
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
|
||||||
|
private final PlantListModel plantListModel = new PlantListModel();
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox myPlants_vbox;
|
||||||
|
|
||||||
|
public MyPlantsController() throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void addPlant(ActionEvent event) throws IOException {
|
||||||
|
mainController.loadPane("Plants.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
//ToDo update, when new crops are added
|
||||||
|
try {
|
||||||
|
loadCropList();
|
||||||
|
} catch (HardinessZoneNotSetException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCropList() throws HardinessZoneNotSetException, IOException {
|
||||||
|
List<Crop> cropList = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
cropList = getCropList();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
createPlantView(cropList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
|
||||||
|
myPlants_vbox.getChildren().clear();
|
||||||
|
for(Crop crop : crops) {
|
||||||
|
HBox hBox = createPlantView(crop);
|
||||||
|
myPlants_vbox.getChildren().add(hBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getMainController(MainFXMLController controller) {
|
||||||
|
mainController = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Crop> getCropList() throws IOException {
|
||||||
|
List<Crop> cropList;
|
||||||
|
cropList = gardenplanmodel.getCrops();
|
||||||
|
return cropList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
|
||||||
|
//ToDo add better design
|
||||||
|
Plant plant = plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0);
|
||||||
|
HBox hBox = new HBox(10);
|
||||||
|
ImageView imageView = new ImageView();
|
||||||
|
imageView.setPreserveRatio(false);
|
||||||
|
imageView.setFitHeight(100);
|
||||||
|
imageView.setFitWidth(100);
|
||||||
|
imageView.maxHeight(100);
|
||||||
|
if (plant.image() != null) {
|
||||||
|
imageView.setImage(plant.image());
|
||||||
|
}
|
||||||
|
hBox.setMinHeight(100);
|
||||||
|
Label label = new Label(plant.name());
|
||||||
|
label.setMaxWidth(2000);
|
||||||
|
HBox.setHgrow(label, Priority.ALWAYS);
|
||||||
|
Button details = new Button("Details");
|
||||||
|
Button delete = new Button("delete");
|
||||||
|
details.setOnAction(getGoToCropDetailEvent(crop));
|
||||||
|
delete.setOnAction(getDeleteCropEvent(crop));
|
||||||
|
hBox.getChildren().addAll(imageView, label, details, delete);
|
||||||
|
return hBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
|
||||||
|
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
Parent root;
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("CropDetail.fxml")));
|
||||||
|
try {
|
||||||
|
root = fxmlLoader.load();
|
||||||
|
CropDetailController controller = fxmlLoader.getController();
|
||||||
|
controller.setPlantFromCrop(crop);
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setScene(new Scene(root));
|
||||||
|
stage.initModality(Modality.APPLICATION_MODAL);
|
||||||
|
stage.setResizable(true);
|
||||||
|
stage.showAndWait();
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
|
||||||
|
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
try {
|
||||||
|
showConfirmation(crop);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setTitle("Delete Crop");
|
||||||
|
alert.setHeaderText("Are you sure want to delete this Crop?");
|
||||||
|
alert.setContentText("placeholder");
|
||||||
|
|
||||||
|
Optional<ButtonType> option = alert.showAndWait();
|
||||||
|
|
||||||
|
if (option.get() == ButtonType.OK) {
|
||||||
|
gardenplanmodel.removeCrop(crop);
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import javafx.beans.property.ListProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class MyScheduleController implements Initializable {
|
||||||
|
private Crop selectedCrop = null;
|
||||||
|
private final TaskListModel taskListModel = new TaskListModel();
|
||||||
|
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
|
||||||
|
private final PlantListModel plantListModel = new PlantListModel();
|
||||||
|
|
||||||
|
private final ListProperty<Crop> cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day1_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day1_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day2_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day2_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day3_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day3_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day4_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day4_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day5_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day5_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day6_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day6_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label day7_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane day7_pane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label information_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<Crop> scheduledPlants_listview;
|
||||||
|
|
||||||
|
public MyScheduleController() throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
List<Crop> cropList;
|
||||||
|
try {
|
||||||
|
cropList = gardenplanmodel.getCrops();
|
||||||
|
cropListProperty.addAll(cropList);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
setCellFactoryListView();
|
||||||
|
scheduledPlants_listview.itemsProperty().bind(cropListProperty);
|
||||||
|
lookForSelectedListEntries();
|
||||||
|
setDayLabels();
|
||||||
|
information_label.setText("");
|
||||||
|
try {
|
||||||
|
loadTaskList();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lookForSelectedListEntries() {
|
||||||
|
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Crop>() {
|
||||||
|
@Override
|
||||||
|
public void changed(ObservableValue<? extends Crop> observable, Crop oldValue, Crop newValue) {
|
||||||
|
selectedCrop = newValue;
|
||||||
|
try {
|
||||||
|
loadTaskList();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDayLabels() {
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
day1_label.setText(today.getDayOfWeek().toString());
|
||||||
|
day2_label.setText(today.plusDays(1).getDayOfWeek().toString());
|
||||||
|
day3_label.setText(today.plusDays(2).getDayOfWeek().toString());
|
||||||
|
day4_label.setText(today.plusDays(3).getDayOfWeek().toString());
|
||||||
|
day5_label.setText(today.plusDays(4).getDayOfWeek().toString());
|
||||||
|
day6_label.setText(today.plusDays(5).getDayOfWeek().toString());
|
||||||
|
day7_label.setText(today.plusDays(6).getDayOfWeek().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCellFactoryListView() {
|
||||||
|
scheduledPlants_listview.setCellFactory(param -> new ListCell<Crop>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Crop crop, boolean empty) {
|
||||||
|
super.updateItem(crop, empty);
|
||||||
|
|
||||||
|
if (empty || crop == null) {
|
||||||
|
setText(null);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
setText(plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0).name());
|
||||||
|
} catch (HardinessZoneNotSetException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTaskList() throws IOException {
|
||||||
|
List<List<Task>> taskLists = new LinkedList<>();
|
||||||
|
if (selectedCrop != null) {
|
||||||
|
taskLists = taskListModel.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
|
||||||
|
} else {
|
||||||
|
taskLists = taskListModel.getTasksUpcomingWeek();
|
||||||
|
}
|
||||||
|
if (!taskLists.isEmpty()) {
|
||||||
|
viewTaskListOfDay(day1_pane, taskLists.get(0));
|
||||||
|
viewTaskListOfDay(day2_pane, taskLists.get(1));
|
||||||
|
viewTaskListOfDay(day3_pane, taskLists.get(2));
|
||||||
|
viewTaskListOfDay(day4_pane, taskLists.get(3));
|
||||||
|
viewTaskListOfDay(day5_pane, taskLists.get(4));
|
||||||
|
viewTaskListOfDay(day6_pane, taskLists.get(5));
|
||||||
|
viewTaskListOfDay(day7_pane, taskLists.get(6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void viewTaskListOfDay(Pane pane, List<Task> tasks) {
|
||||||
|
//ToDo update pane with task list
|
||||||
|
VBox vBox = new VBox();
|
||||||
|
for (Task task : tasks) {
|
||||||
|
Label label = new Label(task.getDescription());
|
||||||
|
vBox.getChildren().add(label);
|
||||||
|
}
|
||||||
|
pane.getChildren().add(vBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Seasons;
|
||||||
|
import javafx.beans.property.ListProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class PlantsController implements Initializable {
|
||||||
|
private static final Logger LOG = Logger.getLogger(PlantsController.class.getName());
|
||||||
|
private final PlantListModel plantListModel = new PlantListModel();
|
||||||
|
private Plant selectedPlant = null;
|
||||||
|
private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;
|
||||||
|
|
||||||
|
// TODO: move to model
|
||||||
|
private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox seasons;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox climate_zones;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label description_plant;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ImageView img_plant;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<Plant> list_plants;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button selectSowDay_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField search_plants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open new window to select sow or harvest day to save the crop
|
||||||
|
* @param event event
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void selectSowDate(ActionEvent event) throws IOException {
|
||||||
|
Parent root;
|
||||||
|
FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("SelectSowDay.fxml")));
|
||||||
|
root = fxmlLoader.load();
|
||||||
|
SelectSowDayController controller = fxmlLoader.getController();
|
||||||
|
controller.getSelectedPlant(selectedPlant);
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setScene(new Scene(root));
|
||||||
|
stage.initModality(Modality.APPLICATION_MODAL);
|
||||||
|
stage.setResizable(false);
|
||||||
|
stage.showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fill list view with current hardiness zone
|
||||||
|
* set default values
|
||||||
|
* create filter of season and hardiness zone
|
||||||
|
* create event listener for selected list entry and search by query
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
setListCellFactory();
|
||||||
|
fillPlantListWithHardinessZone();
|
||||||
|
list_plants.itemsProperty().bind(plantListProperty);
|
||||||
|
|
||||||
|
description_plant.setText("");
|
||||||
|
selectSowDay_button.setDisable(true);
|
||||||
|
|
||||||
|
createFilterSeasons();
|
||||||
|
createFilterHardinessZone();
|
||||||
|
lookForSelectedListEntry();
|
||||||
|
try {
|
||||||
|
viewFilteredListBySearch();
|
||||||
|
} catch (HardinessZoneNotSetException e) {
|
||||||
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set text of list view to plant name
|
||||||
|
*/
|
||||||
|
private void setListCellFactory() {
|
||||||
|
list_plants.setCellFactory(param -> new ListCell<>() {
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Plant plant, boolean empty) {
|
||||||
|
super.updateItem(plant, empty);
|
||||||
|
|
||||||
|
if (empty || plant == null || plant.name() == null) {
|
||||||
|
setText(null);
|
||||||
|
} else {
|
||||||
|
setText(plant.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get plant list according to param season and hardiness zone
|
||||||
|
* fill list view with plant list
|
||||||
|
* @param season enum of seasons
|
||||||
|
* @throws HardinessZoneNotSetException throws exception
|
||||||
|
* @throws IOException throws exception
|
||||||
|
*/
|
||||||
|
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
|
||||||
|
clearListView();
|
||||||
|
plantListProperty.addAll(plantListModel.getFilteredPlantListBySaisonWithoutGrowthPhase(plantListModel.getCurrentZone(), season.getStartDate(), season.getEndDate()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get plant list filtered by search plant entry and hardiness zone
|
||||||
|
* fill list view with plant list
|
||||||
|
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
|
||||||
|
* @throws IOException throws exception
|
||||||
|
*/
|
||||||
|
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
|
||||||
|
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (newValue.isEmpty()) {
|
||||||
|
fillPlantListWithHardinessZone();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
|
||||||
|
clearListView();
|
||||||
|
plantListProperty.addAll(filteredPlants);
|
||||||
|
} catch (HardinessZoneNotSetException e) {
|
||||||
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get plant list of current hardiness zone
|
||||||
|
* fill list view with plant list
|
||||||
|
*/
|
||||||
|
private void fillPlantListWithHardinessZone() {
|
||||||
|
try {
|
||||||
|
clearListView();
|
||||||
|
plantListProperty.addAll(plantListModel.getPlantList(plantListModel.getCurrentZone()));
|
||||||
|
} catch (HardinessZoneNotSetException e) {
|
||||||
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates radio buttons for the hardiness zones defined in enum HardinessZone
|
||||||
|
* defines default value as selected
|
||||||
|
* when selected filter viewList according to hardiness zone
|
||||||
|
*/
|
||||||
|
private void createFilterHardinessZone() {
|
||||||
|
ToggleGroup hardinessGroup = new ToggleGroup();
|
||||||
|
for (HardinessZone zone : HardinessZone.values()) {
|
||||||
|
RadioButton radioButton = new RadioButton(zone.name());
|
||||||
|
radioButton.setToggleGroup(hardinessGroup);
|
||||||
|
radioButton.setPadding(new Insets(0,0,10,0));
|
||||||
|
if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
|
||||||
|
radioButton.setSelected(true);
|
||||||
|
}
|
||||||
|
radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
plantListModel.setCurrentZone(zone);
|
||||||
|
fillPlantListWithHardinessZone();
|
||||||
|
});
|
||||||
|
climate_zones.getChildren().add(radioButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates radio buttons for the seasons defined in enum Seasons
|
||||||
|
* defines default value as selected
|
||||||
|
* when selected filter viewList according to seasons
|
||||||
|
*/
|
||||||
|
private void createFilterSeasons() {
|
||||||
|
ToggleGroup seasonGroup = new ToggleGroup();
|
||||||
|
for (Seasons season : Seasons.values()) {
|
||||||
|
RadioButton radioButton = new RadioButton(season.getName());
|
||||||
|
radioButton.setToggleGroup(seasonGroup);
|
||||||
|
radioButton.setPadding(new Insets(0,0,10,0));
|
||||||
|
if (season.equals(Seasons.AllSEASONS)) {
|
||||||
|
radioButton.setSelected(true);
|
||||||
|
}
|
||||||
|
radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if (season.equals(Seasons.AllSEASONS)) {
|
||||||
|
fillPlantListWithHardinessZone();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
viewFilteredListBySeason(season);
|
||||||
|
} catch (HardinessZoneNotSetException e) {
|
||||||
|
LOG.log(Level.WARNING, "Hardiness Zone not set!");
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not retrieve data!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
seasons.getChildren().add(radioButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* observes changes in the selectedProperty of ListView and updates:
|
||||||
|
* the description label
|
||||||
|
* image of the plant
|
||||||
|
*/
|
||||||
|
private void lookForSelectedListEntry() {
|
||||||
|
selectedPlant = null;
|
||||||
|
description_plant.setText("");
|
||||||
|
selectSowDay_button.setDisable(true);
|
||||||
|
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
|
img_plant.setImage(img);
|
||||||
|
list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if(newValue != null) {
|
||||||
|
selectedPlant = newValue;
|
||||||
|
description_plant.setText(selectedPlant.description());
|
||||||
|
selectSowDay_button.setDisable(false);
|
||||||
|
Image img1;
|
||||||
|
if(selectedPlant.image() != null) {
|
||||||
|
img1 = selectedPlant.image();
|
||||||
|
} else {
|
||||||
|
img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
|
}
|
||||||
|
img_plant.setImage(img1);
|
||||||
|
} else {
|
||||||
|
selectedPlant = null;
|
||||||
|
description_plant.setText("");
|
||||||
|
selectSowDay_button.setDisable(true);
|
||||||
|
Image img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
|
||||||
|
img_plant.setImage(img1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clears the ListView of entries
|
||||||
|
*/
|
||||||
|
private void clearListView() {
|
||||||
|
plantListProperty.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
package ch.zhaw.gartenverwaltung;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class SelectSowDayController implements Initializable {
|
||||||
|
private Plant selectedPlant = null;
|
||||||
|
private final TaskListModel taskListModel = new TaskListModel();
|
||||||
|
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private DatePicker datepicker;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label popup_label;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button save_button;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private RadioButton sow_radio;
|
||||||
|
|
||||||
|
public SelectSowDayController() throws IOException {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close the date selector window
|
||||||
|
* @param event event
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void cancel(ActionEvent event) {
|
||||||
|
closeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get sow date from datePicker or calculate sow date from harvest date
|
||||||
|
* save selected plant and sow date
|
||||||
|
* @param event event
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
void save(ActionEvent event) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
||||||
|
LocalDate sowDate;
|
||||||
|
if (sow_radio.isSelected()) {
|
||||||
|
sowDate = datepicker.getValue();
|
||||||
|
} else {
|
||||||
|
//ToDo method to get current lifecycle group in plant
|
||||||
|
sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0);
|
||||||
|
}
|
||||||
|
gardenplanmodel.plantAsCrop(selectedPlant, sowDate);
|
||||||
|
closeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save the plant which will be planted and update label
|
||||||
|
* @param plant Plant
|
||||||
|
*/
|
||||||
|
public void getSelectedPlant(Plant plant) {
|
||||||
|
selectedPlant = plant;
|
||||||
|
popup_label.setText("Select Harvest/Sow Date for" + selectedPlant.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add listener and set default values
|
||||||
|
* {@inheritDoc}
|
||||||
|
* @param location location
|
||||||
|
* @param resources resources
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
clearDatePickerEntries();
|
||||||
|
|
||||||
|
Callback<DatePicker, DateCell> dayCellFactory= getDayCellFactory();
|
||||||
|
datepicker.setDayCellFactory(dayCellFactory);
|
||||||
|
datepicker.getEditor().setEditable(false);
|
||||||
|
|
||||||
|
enableDisableSaveButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear date picker editor when radio button is changed
|
||||||
|
*/
|
||||||
|
private void clearDatePickerEntries() {
|
||||||
|
sow_radio.selectedProperty().addListener(new ChangeListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean isNowSelected) {
|
||||||
|
datepicker.getEditor().clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* date picker disable/enable dates according to selected plant: sow or harvest day
|
||||||
|
* @return cellFactory of datePicker
|
||||||
|
*/
|
||||||
|
private Callback<DatePicker, DateCell> getDayCellFactory() {
|
||||||
|
|
||||||
|
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DateCell call(final DatePicker datePicker) {
|
||||||
|
return new DateCell() {
|
||||||
|
@Override
|
||||||
|
public void updateItem(LocalDate item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
setDisable(true);
|
||||||
|
setStyle("-fx-background-color: #ffc0cb;");
|
||||||
|
List<LocalDate> dates;
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
if (sow_radio.isSelected()) {
|
||||||
|
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
|
||||||
|
} else {
|
||||||
|
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST);
|
||||||
|
}
|
||||||
|
for (LocalDate date : dates) {
|
||||||
|
if (item.getMonth() == date.getMonth()
|
||||||
|
&& item.getDayOfMonth() == date.getDayOfMonth()
|
||||||
|
&& item.compareTo(today) > 0) {
|
||||||
|
setDisable(false);
|
||||||
|
setStyle("-fx-background-color: #32CD32;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((!sow_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
|
||||||
|
setDisable(true);
|
||||||
|
setStyle("-fx-background-color: #ffc0cb;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return dayCellFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* close date picker window
|
||||||
|
*/
|
||||||
|
private void closeWindow() {
|
||||||
|
Stage stage = (Stage) save_button.getScene().getWindow();
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* disable save button, when there is no date selected in date picker
|
||||||
|
*/
|
||||||
|
private void enableDisableSaveButton() {
|
||||||
|
save_button.setDisable(true);
|
||||||
|
datepicker.getEditor().textProperty().addListener(new ChangeListener<String>() {
|
||||||
|
@Override
|
||||||
|
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
|
||||||
|
if (newValue == null || newValue.equals("")) {
|
||||||
|
save_button.setDisable(true);
|
||||||
|
} else {
|
||||||
|
save_button.setDisable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.gardenplan;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.GardenPlan;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.JsonGardenPlan;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Gardenplan model manages the crops in the gardenplan.
|
||||||
|
*/
|
||||||
|
public class Gardenplanmodel {
|
||||||
|
private GardenPlan gardenPlan;
|
||||||
|
private List<Crop> cropList;
|
||||||
|
private TaskListModel taskListModel;
|
||||||
|
private Object IllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of Gardenplan model
|
||||||
|
*
|
||||||
|
* @param taskListModel holds a reference to the task list object.
|
||||||
|
*/
|
||||||
|
public Gardenplanmodel(TaskListModel taskListModel) throws IOException {
|
||||||
|
this.taskListModel = taskListModel;
|
||||||
|
gardenPlan = new JsonGardenPlan();
|
||||||
|
cropList = new ArrayList<>();
|
||||||
|
cropList = gardenPlan.getCrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Crop with a {@link Plant} and the planting date of the plant. Then let the Tasklistmodel create the
|
||||||
|
* gardening {@link Task} for the crop. Store the crop in the gardenplan file and the cache.
|
||||||
|
*
|
||||||
|
* @param plant The plant which is wnated to be planted
|
||||||
|
* @param plantingDate The date, when the plant is planted
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
* @throws HardinessZoneNotSetException If the hardinesszone could not be added
|
||||||
|
* @throws PlantNotFoundException If the plant is not found in the database.
|
||||||
|
*/
|
||||||
|
public void plantAsCrop(Plant plant, LocalDate plantingDate) throws IOException, HardinessZoneNotSetException, PlantNotFoundException {
|
||||||
|
Crop crop = new Crop(plant.id(), plantingDate);
|
||||||
|
//TODO Add Area to Plant
|
||||||
|
//crop.withArea(0);
|
||||||
|
gardenPlan.saveCrop(crop);
|
||||||
|
taskListModel.planTasksForCrop(crop);
|
||||||
|
cropList = gardenPlan.getCrops();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a {@link Crop} from the file and the cache
|
||||||
|
*
|
||||||
|
* @param crop The plant which is wnated to be planted
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public void removeCrop(Crop crop) throws IOException {
|
||||||
|
gardenPlan.removeCrop(crop);
|
||||||
|
taskListModel.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new));
|
||||||
|
cropList = gardenPlan.getCrops();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns a list of {@link Crop}s which are currently in the gardenplan.
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Crop> getCrops() throws IOException {
|
||||||
|
if(!cropList.isEmpty()){
|
||||||
|
cropList = gardenPlan.getCrops();
|
||||||
|
}
|
||||||
|
return cropList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Optional of {@link Crop} if the crop is the gardenplan
|
||||||
|
*
|
||||||
|
* @param cropId The date, when the plant is planted
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public Optional<Crop> getCrop(Long cropId) throws IOException {
|
||||||
|
return gardenPlan.getCropById(cropId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,42 @@
|
||||||
package ch.zhaw.gartenverwaltung.io;
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.types.UserPlanting;
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface GardenPlan {
|
public interface GardenPlan {
|
||||||
List<UserPlanting> getPlantings();
|
/**
|
||||||
void savePlanting(UserPlanting planting) throws IOException;
|
* Yields a list of all {@link Crop}s in the database.
|
||||||
void removePlanting(UserPlanting planting) throws IOException;
|
*
|
||||||
|
* @return A list of all {@link Crop}s in the database
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
List<Crop> getCrops() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve the {@link Crop} with the specified cropId.
|
||||||
|
*
|
||||||
|
* @param cropId The {@link Crop} to look for
|
||||||
|
* @return {@link Optional} of the found {@link Crop}, {@link Optional#empty()} if no entry matched the criteria
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
Optional<Crop> getCropById(long cropId) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a Crop to the Database.
|
||||||
|
*
|
||||||
|
* @param crop The {@link Crop} to be saved
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
void saveCrop(Crop crop) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a Crop from the Database.
|
||||||
|
*
|
||||||
|
* @param crop The {@link Crop} to be removed
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
void removeCrop(Crop crop) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
public class HardinessZoneNotSetException extends Exception {
|
||||||
|
public HardinessZoneNotSetException() {
|
||||||
|
super("HardinessZone must be set to retrieve plants!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
public class IdProvider {
|
||||||
|
private long currentId;
|
||||||
|
public IdProvider(long initialValue) {
|
||||||
|
currentId = initialValue;
|
||||||
|
}
|
||||||
|
public long incrementAndGet() {
|
||||||
|
return ++currentId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
class InvalidJsonException extends IOException {
|
||||||
|
public InvalidJsonException(String reason) {
|
||||||
|
super(reason);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class JsonGardenPlan implements GardenPlan {
|
||||||
|
private final URL dataSource;
|
||||||
|
|
||||||
|
private IdProvider idProvider;
|
||||||
|
private Map<Long, Crop> cropMap = Collections.emptyMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creating constant objects required to deserialize the {@link LocalDate} classes
|
||||||
|
*/
|
||||||
|
private final static JavaTimeModule timeModule = new JavaTimeModule();
|
||||||
|
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
||||||
|
static {
|
||||||
|
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat);
|
||||||
|
LocalDateSerializer dateSerializer = new LocalDateSerializer(dateFormat);
|
||||||
|
|
||||||
|
timeModule.addDeserializer(LocalDate.class, dateDeserializer);
|
||||||
|
timeModule.addSerializer(LocalDate.class, dateSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
*/
|
||||||
|
public JsonGardenPlan() {
|
||||||
|
this.dataSource = getClass().getResource("user-crops.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to use a specified {@link URL} as a {@link #dataSource}
|
||||||
|
* @param dataSource A {@link URL} to the file to be used as a data source
|
||||||
|
*/
|
||||||
|
public JsonGardenPlan(URL dataSource) {
|
||||||
|
this.dataSource = dataSource;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Crop> getCrops() throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
return cropMap.values().stream().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<Crop> getCropById(long id) throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(cropMap.get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*
|
||||||
|
* Saves a crop to the database.
|
||||||
|
* If no {@link Crop#cropId} is set, one will be generated and
|
||||||
|
* the Crop is stored as a new entry.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void saveCrop(Crop crop) throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
Long cropId = crop.getCropId().orElse(idProvider.incrementAndGet());
|
||||||
|
cropMap.put(cropId, crop.withId(cropId));
|
||||||
|
writeCropList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeCrop(Crop crop) throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
Optional<Long> cropId = crop.getCropId();
|
||||||
|
if (cropId.isPresent()) {
|
||||||
|
cropMap.remove(cropId.get());
|
||||||
|
writeCropList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the database from {@link #dataSource} and updates the cached data.
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed or invalid data was read.
|
||||||
|
*/
|
||||||
|
private void loadCropList() throws IOException {
|
||||||
|
if (dataSource != null) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(timeModule);
|
||||||
|
|
||||||
|
List<Crop> result;
|
||||||
|
result = mapper.readerForListOf(Crop.class).readValue(dataSource);
|
||||||
|
|
||||||
|
// Turn list into a HashMap with structure cropId => Crop
|
||||||
|
cropMap = new HashMap<>(result.size());
|
||||||
|
for (Crop crop : result) {
|
||||||
|
Long id = crop.getCropId()
|
||||||
|
.orElseThrow(() -> new InvalidJsonException(String.format("Invalid value for \"cropId\" in %s!", crop)));
|
||||||
|
cropMap.put(id, crop);
|
||||||
|
}
|
||||||
|
Long maxId = cropMap.isEmpty() ? 0L : Collections.max(cropMap.keySet());
|
||||||
|
idProvider = new IdProvider(maxId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the values from {@link #cropMap} to the {@link #dataSource}
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
private void writeCropList() throws IOException {
|
||||||
|
if (dataSource != null) {
|
||||||
|
try {
|
||||||
|
new ObjectMapper()
|
||||||
|
.registerModule(timeModule)
|
||||||
|
.registerModule(new Jdk8Module())
|
||||||
|
.writeValue(new File(dataSource.toURI()), cropMap.values());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new IOException(INVALID_DATASOURCE_MSG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.json.PlantImageDeserializer;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.MonthDayDeserializer;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.MonthDay;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the {@link PlantDatabase} interface for loading {@link Plant} objects
|
||||||
|
* from a JSON file.
|
||||||
|
* The reads are cached to minimize file-io operations.
|
||||||
|
*/
|
||||||
|
public class JsonPlantDatabase implements PlantDatabase {
|
||||||
|
private final URL dataSource = getClass().getResource("plantdb.json");
|
||||||
|
|
||||||
|
private HardinessZone currentZone;
|
||||||
|
private Map<Long, Plant> plantMap = Collections.emptyMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creating constant objects required to deserialize the {@link MonthDay} classes
|
||||||
|
*/
|
||||||
|
private final static JavaTimeModule timeModule = new JavaTimeModule();
|
||||||
|
private final static SimpleModule imageModule = new SimpleModule();
|
||||||
|
|
||||||
|
static {
|
||||||
|
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("MM-dd");
|
||||||
|
MonthDayDeserializer dateDeserializer = new MonthDayDeserializer(dateFormat);
|
||||||
|
timeModule.addDeserializer(MonthDay.class, dateDeserializer);
|
||||||
|
|
||||||
|
imageModule.addDeserializer(Image.class, new PlantImageDeserializer());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If no data is currently loaded, or the specified zone differs
|
||||||
|
* from the {@link #currentZone}, data is loaded from {@link #dataSource}.
|
||||||
|
* In any case, the values of {@link #plantMap} are returned.
|
||||||
|
*
|
||||||
|
* @see PlantDatabase#getPlantList(HardinessZone)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException {
|
||||||
|
if (plantMap.isEmpty() || zone != currentZone) {
|
||||||
|
loadPlantList(zone);
|
||||||
|
}
|
||||||
|
return plantMap.values().stream().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see PlantDatabase#getPlantById(long)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<Plant> getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException {
|
||||||
|
if (plantMap.isEmpty()) {
|
||||||
|
loadPlantList(zone);
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(plantMap.get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the database from {@link #dataSource} and updates the cached data.
|
||||||
|
*
|
||||||
|
* @param zone The {@link HardinessZone} for which data is to be loaded
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
private void loadPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException {
|
||||||
|
if (zone == null) {
|
||||||
|
throw new HardinessZoneNotSetException();
|
||||||
|
}
|
||||||
|
if (dataSource != null) {
|
||||||
|
currentZone = zone;
|
||||||
|
ObjectMapper mapper = new ObjectMapper()
|
||||||
|
.registerModule(timeModule)
|
||||||
|
.registerModule(imageModule);
|
||||||
|
|
||||||
|
List<Plant> result;
|
||||||
|
result = mapper.readerForListOf(Plant.class).readValue(dataSource);
|
||||||
|
|
||||||
|
plantMap = result.stream()
|
||||||
|
// Remove plants not in the current zone
|
||||||
|
.filter(plant -> {
|
||||||
|
plant.inZone(currentZone);
|
||||||
|
return !plant.lifecycle().isEmpty();
|
||||||
|
})
|
||||||
|
// Create Hashmap from results
|
||||||
|
.collect(HashMap::new,
|
||||||
|
(res, plant) -> res.put(plant.id(), plant),
|
||||||
|
(existing, replacement) -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the {@link TaskDatabase} interface for loading and writing {@link Task} objects
|
||||||
|
* from and to a JSON file.
|
||||||
|
* The reads are cached to minimize file-io operations.
|
||||||
|
*/
|
||||||
|
public class JsonTaskDatabase implements TaskDatabase{
|
||||||
|
IdProvider idProvider;
|
||||||
|
private final URL dataSource = getClass().getResource("taskdb.json");
|
||||||
|
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
||||||
|
|
||||||
|
private Map<Long, Task> taskMap = Collections.emptyMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creating constant objects required to deserialize the {@link LocalDate} classes
|
||||||
|
*/
|
||||||
|
private final static JavaTimeModule timeModule = new JavaTimeModule();
|
||||||
|
static {
|
||||||
|
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat);
|
||||||
|
LocalDateSerializer dateSerializer = new LocalDateSerializer(dateFormat);
|
||||||
|
|
||||||
|
timeModule.addDeserializer(LocalDate.class, dateDeserializer);
|
||||||
|
timeModule.addSerializer(LocalDate.class, dateSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If no data is currently loaded, data is loaded from {@link #dataSource}.
|
||||||
|
* In any case, the values of {@link #taskMap} are returned.
|
||||||
|
*
|
||||||
|
* @see TaskDatabase#getTaskList(LocalDate, LocalDate)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{
|
||||||
|
if(taskMap.isEmpty()) {
|
||||||
|
loadTaskListFromFile();
|
||||||
|
}
|
||||||
|
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
|
||||||
|
public List<Task> getTaskForCrop(long cropId) throws IOException {
|
||||||
|
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
|
||||||
|
public void removeTasksForCrop(long cropId) throws IOException {
|
||||||
|
if(taskMap.isEmpty()) {
|
||||||
|
loadTaskListFromFile();
|
||||||
|
}
|
||||||
|
taskMap.values().removeIf(task -> task.getCropId() == cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If no data is currently loaded, data is loaded from {@link #dataSource}.
|
||||||
|
* If the {@link Task} has an id than the task is added to the {@link #taskMap}
|
||||||
|
* otherwise the id is generated with the {@link IdProvider} before adding
|
||||||
|
* it to the {@link #taskMap}. In any case, the {@link #taskMap} is written
|
||||||
|
* to the {@link #dataSource}.
|
||||||
|
*
|
||||||
|
* @see TaskDatabase#saveTask(Task)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void saveTask(Task task) throws IOException {
|
||||||
|
if(taskMap.isEmpty()) {
|
||||||
|
loadTaskListFromFile();
|
||||||
|
}
|
||||||
|
if(task.getId() == 0) {
|
||||||
|
task.withId(idProvider.incrementAndGet());
|
||||||
|
}
|
||||||
|
writeTaskListToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If no data is currently loaded, data is loaded from {@link #dataSource}.
|
||||||
|
* If the {@link Task}s id is found in the {@link #taskMap}, the Task is removed
|
||||||
|
* from the {@link #taskMap}. Then the Task are written to the {@link #dataSource}.
|
||||||
|
*
|
||||||
|
* @see TaskDatabase#removeTask(Task)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeTask(Task task) throws IOException {
|
||||||
|
if(taskMap.isEmpty()) {
|
||||||
|
loadTaskListFromFile();
|
||||||
|
}
|
||||||
|
if(taskMap.containsKey(task.getId())){
|
||||||
|
taskMap.remove(task.getId());
|
||||||
|
writeTaskListToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes cached data to the {@link #dataSource}.
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
private void writeTaskListToFile() throws IOException {
|
||||||
|
if(dataSource != null) {
|
||||||
|
try {
|
||||||
|
new ObjectMapper()
|
||||||
|
.registerModule(timeModule)
|
||||||
|
.registerModule(new Jdk8Module())
|
||||||
|
.writeValue(new File(dataSource.toURI()), taskMap.values());
|
||||||
|
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new IOException(INVALID_DATASOURCE_MSG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the database from {@link #dataSource} and updates the cached data.
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
private void loadTaskListFromFile() throws IOException {
|
||||||
|
if (dataSource != null) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(timeModule)
|
||||||
|
.registerModule(new Jdk8Module());
|
||||||
|
|
||||||
|
List<Task> result;
|
||||||
|
result = mapper.readerForListOf(Task.class).readValue(dataSource);
|
||||||
|
|
||||||
|
taskMap = result.stream()
|
||||||
|
.collect(HashMap::new,
|
||||||
|
(res, task) -> res.put(task.getId(), task),
|
||||||
|
(existing, replacement) -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
Long maxId = taskMap.isEmpty() ? 0L : Collections.max(taskMap.keySet());
|
||||||
|
idProvider = new IdProvider(maxId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,10 +3,32 @@ package ch.zhaw.gartenverwaltung.io;
|
||||||
import ch.zhaw.gartenverwaltung.types.Plant;
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A database of {@link Plant}s.
|
||||||
|
* The interface specifies the minimal required operations.
|
||||||
|
*/
|
||||||
public interface PlantDatabase {
|
public interface PlantDatabase {
|
||||||
List<Plant> getPlantList(HardinessZone zone);
|
/**
|
||||||
Optional<Plant> getPlantById(long id);
|
* Yields a list of all {@link Plant}s in the database with only data relevant to the specfied {@link HardinessZone}
|
||||||
|
*
|
||||||
|
* @param zone The zone for which data should be fetched
|
||||||
|
* @return A list of {@link Plant}s with data for the specified zone
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
*/
|
||||||
|
List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve the {@link Plant} with the specified id.
|
||||||
|
*
|
||||||
|
* @param id The {@link Plant#id()} to look for
|
||||||
|
* @return {@link Optional} of the found {@link Plant}, {@link Optional#empty()} if no entry matched the criteria
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
*/
|
||||||
|
Optional<Plant> getPlantById(HardinessZone zone, long id) throws IOException, HardinessZoneNotSetException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,58 @@
|
||||||
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.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A database of {@link Task}s.
|
||||||
|
* The interface specifies the minimal required operations.
|
||||||
|
*/
|
||||||
public interface TaskDatabase {
|
public interface TaskDatabase {
|
||||||
List<Task> getTaskList(Date start, Date end);
|
/**
|
||||||
|
* Yields a list of all {@link Task}s in the database with the start and end date of a period of time.
|
||||||
|
*
|
||||||
|
* @param start The start date of the wanted time period
|
||||||
|
* @param end The end date of the wanted time period
|
||||||
|
* @return A list of {@link Task}s planned in the specified time period
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param task The {@link Task} which is wanted to be saved in the TaskList
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
void saveTask(Task task) throws IOException;
|
void saveTask(Task task) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the {@link Task}from the Cache.
|
||||||
|
*
|
||||||
|
* @param task The {@link Task} which has to be removed from the {@link Task} list.
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
void removeTask(Task task) throws IOException;
|
void removeTask(Task task) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.json;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType> {
|
||||||
|
public GrowthPhaseTypeDeserializer(Class<?> vc) {
|
||||||
|
super(vc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GrowthPhaseTypeDeserializer() { this(null); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
||||||
|
GrowthPhaseType result;
|
||||||
|
String token = parser.getText();
|
||||||
|
try {
|
||||||
|
result = GrowthPhaseType.valueOf(token.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IOException(String.format("Bad growth phase type \"%s\"\n", token));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.json;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
|
||||||
|
public HardinessZoneDeserializer(Class<?> vc) {
|
||||||
|
super(vc);
|
||||||
|
}
|
||||||
|
public HardinessZoneDeserializer() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
||||||
|
HardinessZone result;
|
||||||
|
String token = parser.getText();
|
||||||
|
try {
|
||||||
|
result = HardinessZone.valueOf(token.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IOException(String.format("Unknown Hardiness Zone \"%s\"\n", token), e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.json;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
|
||||||
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class PlantImageDeserializer extends JsonDeserializer<Image> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Image deserialize(JsonParser parser, DeserializationContext context) throws IOException {
|
||||||
|
Image result = null;
|
||||||
|
URL imageUrl = PlantDatabase.class.getResource(String.format("images/%s", parser.getText()));
|
||||||
|
if (imageUrl != null) {
|
||||||
|
try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) {
|
||||||
|
result = new Image(is);
|
||||||
|
} catch (IllegalArgumentException | URISyntaxException e) {
|
||||||
|
throw new IOException(String.format("Cannot find Image \"%s\"\n", imageUrl.getFile()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.plantList;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.MonthDay;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class PlantListModel {
|
||||||
|
private PlantDatabase plantDatabase;
|
||||||
|
private HardinessZone currentZone;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparators to create sorted Plant List
|
||||||
|
*/
|
||||||
|
static final Comparator<Plant> sortByName = Comparator.comparing(Plant::name);
|
||||||
|
static final Comparator<Plant> SortById = Comparator.comparingLong(Plant::id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to create Database Object.
|
||||||
|
*/
|
||||||
|
public PlantListModel() {
|
||||||
|
plantDatabase = new JsonPlantDatabase();
|
||||||
|
setDefaultZone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlantListModel(PlantDatabase plantDatabase) {
|
||||||
|
this.plantDatabase = plantDatabase;
|
||||||
|
setDefaultZone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDefaultZone() {
|
||||||
|
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentZone(HardinessZone currentZone) {
|
||||||
|
this.currentZone = currentZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HardinessZone getCurrentZone() {
|
||||||
|
return currentZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get actual Plant List in alphabetic Order
|
||||||
|
*
|
||||||
|
* @return actual Plant List in alphabetic Order
|
||||||
|
*/
|
||||||
|
public List<Plant> getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException {
|
||||||
|
setCurrentZone(zone);
|
||||||
|
return getSortedPlantList(zone, sortByName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get the actual Plant list in custom Order
|
||||||
|
*
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param comparator comparator to sort the list
|
||||||
|
* @return sorted list with plants in the given hardiness zone
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
*/
|
||||||
|
public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException {
|
||||||
|
setCurrentZone(zone);
|
||||||
|
return plantDatabase.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get Filtered plant list
|
||||||
|
*
|
||||||
|
* @param predicate predicate to filter the list
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @return filterd list with plants in the hardinness zone
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
*/
|
||||||
|
public List<Plant> getFilteredPlantList(HardinessZone zone, Predicate<Plant> predicate) throws HardinessZoneNotSetException, IOException {
|
||||||
|
setCurrentZone(zone);
|
||||||
|
return getPlantList(zone).stream().filter(predicate).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get Filtered plant list by id by exact match
|
||||||
|
*
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param id id of plant
|
||||||
|
* @return if id doesn't exist: empty List, else list with 1 plant entry.
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
*/
|
||||||
|
public List<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException {
|
||||||
|
setCurrentZone(zone);
|
||||||
|
List<Plant> plantList = new ArrayList<>();
|
||||||
|
plantDatabase.getPlantById(zone, id).ifPresent(plantList::add);
|
||||||
|
return plantList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param searchString the string to search plant List, set '#' as first char the search by id.
|
||||||
|
* @return List of plants found in Plant List which contain the search String in the name or description
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Plant> getFilteredPlantListByString(HardinessZone zone, String searchString) throws HardinessZoneNotSetException, IOException {
|
||||||
|
if (searchString.length() == 0) {
|
||||||
|
return getPlantList(zone);
|
||||||
|
} else if (searchString.charAt(0) == '#') {
|
||||||
|
if (isPositiveIntegral(searchString.substring(1))) {
|
||||||
|
Long searchId = Long.parseLong(searchString.substring(1));
|
||||||
|
return getFilteredPlantListById(zone, searchId);
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String caseInsensitiveSearchString = searchString.toLowerCase();
|
||||||
|
return getFilteredPlantList(zone, plant ->
|
||||||
|
plant.name().toLowerCase().contains(caseInsensitiveSearchString) ||
|
||||||
|
plant.description().toLowerCase().contains(caseInsensitiveSearchString)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type GrowPhaseType to filter
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param from the earliest date to for the filter
|
||||||
|
* @param to the lastest date for the filter
|
||||||
|
* @return List of Plants with selected saison
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
private List<Plant> getFilteredPlantListBySaison(GrowthPhaseType type, HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
|
||||||
|
return getFilteredPlantList(zone, plant -> plant.lifecycle().stream().anyMatch(growthPhase -> growthPhase.startDate().compareTo(from) >= 0 && (growthPhase.startDate().compareTo(to) <= 0) && growthPhase.type() == type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param from the earliest date to for the filter
|
||||||
|
* @param to the lastest date for the filter
|
||||||
|
* @return List of Plants with selected saison
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Plant> getFilteredPlantListByPlantingSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
|
||||||
|
return getFilteredPlantListBySaison(GrowthPhaseType.PLANT, zone, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param from the earliest date to for the filter
|
||||||
|
* @param to the lastest date for the filter
|
||||||
|
* @return List of Plants with selected saison
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Plant> getFilteredPlantListByHarvestSaison(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
|
||||||
|
return getFilteredPlantListBySaison(GrowthPhaseType.HARVEST, zone, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param zone selected hardiness zone
|
||||||
|
* @param from the earliest date to for the filter
|
||||||
|
* @param to the lastest date for the filter
|
||||||
|
* @return List of Plants with selected saison
|
||||||
|
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<Plant> getFilteredPlantListBySaisonWithoutGrowthPhase(HardinessZone zone, MonthDay from, MonthDay to) throws HardinessZoneNotSetException, IOException {
|
||||||
|
return getFilteredPlantList(zone, plant -> plant.lifecycle().stream().anyMatch(growthPhase -> growthPhase.startDate().compareTo(from) >= 0 && (growthPhase.startDate().compareTo(to) <= 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a string can safely be parsed as a positive Integral value (short/int/long)
|
||||||
|
*
|
||||||
|
* @param subject The string to be tested
|
||||||
|
* @return Whether the string contains only digits
|
||||||
|
*/
|
||||||
|
private boolean isPositiveIntegral(String subject) {
|
||||||
|
return subject != null && subject.matches("[0-9]+");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.taskList;
|
||||||
|
|
||||||
|
public class PlantNotFoundException extends Exception {
|
||||||
|
public PlantNotFoundException() {
|
||||||
|
super("The selected Plant was not found in Database!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.taskList;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.Config;
|
||||||
|
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.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class TaskListModel {
|
||||||
|
private TaskDatabase taskDatabase;
|
||||||
|
private PlantDatabase plantDatabase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparators to create sorted Task List
|
||||||
|
*/
|
||||||
|
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
|
||||||
|
|
||||||
|
public TaskListModel(){
|
||||||
|
taskDatabase = new JsonTaskDatabase();
|
||||||
|
plantDatabase = new JsonPlantDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor to create Database Objects.
|
||||||
|
*/
|
||||||
|
public TaskListModel(TaskDatabase taskDatabase, PlantDatabase plantDatabase) {
|
||||||
|
this.taskDatabase = taskDatabase;
|
||||||
|
this.plantDatabase = plantDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
taskDatabase.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 = plantDatabase.getPlantById(Config.getCurrentHardinessZone(), crop.getPlantId()).orElseThrow(PlantNotFoundException::new);
|
||||||
|
for (GrowthPhase growthPhase : plant.lifecycle()) {
|
||||||
|
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 {
|
||||||
|
taskDatabase.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 {
|
||||||
|
taskDatabase.removeTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
|
||||||
|
return taskList.stream().filter(task -> task.getCropId() == cropId).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get all Tasks
|
||||||
|
* @return List of all Tasks
|
||||||
|
* @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 {
|
||||||
|
List<List<Task>> dayTaskList = new ArrayList<>();
|
||||||
|
for(int i = 0; i < 7; i++) {
|
||||||
|
LocalDate date = LocalDate.now().plusDays(i);
|
||||||
|
dayTaskList.add(taskDatabase.getTaskList(date, date));
|
||||||
|
}
|
||||||
|
return dayTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get an List of 7 Tasklists for the next 7 days. (Filtered Index 0 is Tasklist for Today.
|
||||||
|
* @return List with length 7 (List<List<Task>>)
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
public List<List<Task>> getTasksUpcomingWeekForCrop(Long cropId) throws IOException {
|
||||||
|
List<List<Task>> dayTaskList = new ArrayList<>();
|
||||||
|
for(int i = 0; i < 7; i++) {
|
||||||
|
LocalDate date = LocalDate.now().plusDays(i);
|
||||||
|
dayTaskList.add(filterListByCrop(taskDatabase.getTaskList(date, date), cropId));
|
||||||
|
}
|
||||||
|
return dayTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get Tasklist filtered by date.
|
||||||
|
* @param start the start date for the filter
|
||||||
|
* @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(taskDatabase.getTaskList(start, end), sortByStartDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class Crop {
|
||||||
|
private Long cropId = null;
|
||||||
|
private final long plantId;
|
||||||
|
private final LocalDate startDate;
|
||||||
|
private double area = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default Constructor (needed for deserialization)
|
||||||
|
*/
|
||||||
|
public Crop() {
|
||||||
|
plantId = 0;
|
||||||
|
startDate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crop(long plantId, LocalDate startDate) {
|
||||||
|
this.plantId = plantId;
|
||||||
|
this.startDate = startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder-Style setter
|
||||||
|
public Crop withId(long cropId) {
|
||||||
|
this.cropId = cropId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Crop withArea(double area) {
|
||||||
|
this.area = area;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
public Optional<Long> getCropId() {
|
||||||
|
return Optional.ofNullable(cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPlantId() { return plantId; }
|
||||||
|
public LocalDate getStartDate() { return startDate; }
|
||||||
|
public double getArea() { return area; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) return true;
|
||||||
|
if (other instanceof Crop otherCrop) {
|
||||||
|
return Objects.equals(this.cropId, otherCrop.cropId) &&
|
||||||
|
plantId == otherCrop.plantId &&
|
||||||
|
startDate != null && startDate.equals(otherCrop.startDate) &&
|
||||||
|
area == otherCrop.area;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int startCode = startDate != null ? startDate.hashCode() : 0;
|
||||||
|
return (int) plantId ^ (startCode << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]",
|
||||||
|
cropId,
|
||||||
|
plantId,
|
||||||
|
startDate,
|
||||||
|
area);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,19 @@
|
||||||
package ch.zhaw.gartenverwaltung.types;
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
import java.util.Date;
|
import ch.zhaw.gartenverwaltung.json.GrowthPhaseTypeDeserializer;
|
||||||
|
import ch.zhaw.gartenverwaltung.json.HardinessZoneDeserializer;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
|
||||||
public record GrowthPhase(Date startDate,
|
import java.time.MonthDay;
|
||||||
Date endDate,
|
import java.util.List;
|
||||||
GrowthPhaseType type,
|
|
||||||
HardinessZone zone) {
|
|
||||||
|
public record GrowthPhase(
|
||||||
|
MonthDay startDate,
|
||||||
|
MonthDay endDate,
|
||||||
|
int group,
|
||||||
|
WateringCycle wateringCycle,
|
||||||
|
@JsonDeserialize(using = GrowthPhaseTypeDeserializer.class) GrowthPhaseType type,
|
||||||
|
@JsonDeserialize(using = HardinessZoneDeserializer.class) HardinessZone zone,
|
||||||
|
List<TaskTemplate> taskTemplates) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package ch.zhaw.gartenverwaltung.types;
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
public enum GrowthPhaseType {
|
public enum GrowthPhaseType {
|
||||||
SOW, PLANT, HARVEST
|
SOW, PLANT, REPLANT, HARVEST
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,6 @@ package ch.zhaw.gartenverwaltung.types;
|
||||||
* (Subject to later expansion)
|
* (Subject to later expansion)
|
||||||
*/
|
*/
|
||||||
public enum HardinessZone {
|
public enum HardinessZone {
|
||||||
|
ZONE_1A,
|
||||||
ZONE_8A
|
ZONE_8A
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
public record Pest(String name, String description, String measures) {
|
||||||
|
}
|
|
@ -1,11 +1,105 @@
|
||||||
package ch.zhaw.gartenverwaltung.types;
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.time.temporal.ChronoUnit.DAYS;
|
||||||
|
|
||||||
public record Plant(
|
public record Plant(
|
||||||
long id,
|
long id,
|
||||||
String name,
|
String name,
|
||||||
String description,
|
String description,
|
||||||
int spacing,
|
Image image,
|
||||||
|
String spacing,
|
||||||
|
int light,
|
||||||
|
String soil,
|
||||||
|
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) {
|
||||||
|
lifecycle.removeIf(growthPhase -> !growthPhase.zone().equals(zone));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get all growthPhases of lifecycle group
|
||||||
|
* @param group lifecycle group
|
||||||
|
* @return list of growthPhases
|
||||||
|
*/
|
||||||
|
public List<GrowthPhase> lifecycleForGroup(int group) {
|
||||||
|
return lifecycle.stream()
|
||||||
|
.filter(growthPhase -> growthPhase.group() == group)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get sow date from given harvest day from lifecycle group
|
||||||
|
* @param harvestDate date of the harvest
|
||||||
|
* @param group lifecycle group
|
||||||
|
* @return LocaleDate of sow date
|
||||||
|
*/
|
||||||
|
public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) {
|
||||||
|
return harvestDate.minusDays(timeToHarvest(group));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calculate the days between sow and harvest day for lifecycle group
|
||||||
|
* @param group the lifecycle group
|
||||||
|
* @return Integer number of dates between sow and harvest day
|
||||||
|
*/
|
||||||
|
public int timeToHarvest(int group) {
|
||||||
|
List<GrowthPhase> activeLifecycle = lifecycleForGroup(group);
|
||||||
|
GrowthPhase sow = activeLifecycle.stream()
|
||||||
|
.filter(growthPhase -> !growthPhase.type().equals(GrowthPhaseType.SOW))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
GrowthPhase harvest = activeLifecycle.stream()
|
||||||
|
.filter(growthPhase -> !growthPhase.type().equals(GrowthPhaseType.HARVEST))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
|
||||||
|
int currentYear = LocalDate.now().getYear();
|
||||||
|
return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter out the given growthPhase out of the lifecycle
|
||||||
|
* create list of dates for this growthPhase and return it
|
||||||
|
* @param growthPhase the wanted growthPhase
|
||||||
|
* @return a list of dates of the current year
|
||||||
|
*/
|
||||||
|
public List<LocalDate> getDateListOfGrowthPhase(GrowthPhaseType growthPhase) {
|
||||||
|
List<LocalDate> dates = new LinkedList<>();
|
||||||
|
for (GrowthPhase growth : lifecycle) {
|
||||||
|
if (growth.type().equals(growthPhase)) {
|
||||||
|
dates = addDatesFromMonthDay(growth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* transform monthDay value of the given growthPhase to localDate
|
||||||
|
* return a list of dates from start to end of growth phase
|
||||||
|
* @param growthPhase the current growthPhase
|
||||||
|
* @return a list of dates of the current year
|
||||||
|
*/
|
||||||
|
private List<LocalDate> addDatesFromMonthDay(GrowthPhase growthPhase) {
|
||||||
|
List<LocalDate> dates = new LinkedList<>();
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
LocalDate start = growthPhase.startDate().atYear(today.getYear());
|
||||||
|
LocalDate end = growthPhase.endDate().atYear(today.getYear());
|
||||||
|
while (!start.isAfter(end)) {
|
||||||
|
dates.add(start);
|
||||||
|
start = start.plusDays(1);
|
||||||
|
}
|
||||||
|
return dates;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
import java.time.MonthDay;
|
||||||
|
|
||||||
|
public enum Seasons {
|
||||||
|
AllSEASONS("--01-01", "--12-31", "All Seasons"),
|
||||||
|
SPRING("--03-01", "--05-30", "Spring"),
|
||||||
|
SUMMER("--06-01", "--08-30", "Summer"),
|
||||||
|
AUTUMN("--09-01", "--11-30", "Autumn"),
|
||||||
|
WINTER("--12-01", "--02-28", "Winter");
|
||||||
|
|
||||||
|
public final String startDate;
|
||||||
|
public final String endDate;
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
Seasons(String startDate, String endDate, String name) {
|
||||||
|
this.startDate = startDate;
|
||||||
|
this.endDate = endDate;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MonthDay getStartDate() {
|
||||||
|
return MonthDay.parse(this.startDate);
|
||||||
|
}
|
||||||
|
public MonthDay getEndDate() {
|
||||||
|
return MonthDay.parse(this.endDate);
|
||||||
|
}
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package ch.zhaw.gartenverwaltung.types;
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,14 +12,35 @@ 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 Date startDate;
|
private final LocalDate startDate;
|
||||||
private Integer interval;
|
private Integer interval;
|
||||||
private Date endDate;
|
private LocalDate endDate;
|
||||||
|
private long cropId;
|
||||||
|
|
||||||
public Task(String name, String description, Date startDate) {
|
/**
|
||||||
|
* default constructor
|
||||||
|
* (used by Json deserializer)
|
||||||
|
*/
|
||||||
|
public Task(){
|
||||||
|
name= "";
|
||||||
|
description= "";
|
||||||
|
startDate = LocalDate.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task(String name, String description, LocalDate startDate, long cropId) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.startDate = endDate;
|
this.startDate = startDate;
|
||||||
|
this.cropId = cropId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval, long cropId) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.startDate = startDate;
|
||||||
|
this.endDate = endDate;
|
||||||
|
this.interval = interval;
|
||||||
|
this.cropId = cropId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builder-pattern-style setters
|
// Builder-pattern-style setters
|
||||||
|
@ -30,21 +52,26 @@ public class Task {
|
||||||
this.interval = interval;
|
this.interval = interval;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public Task withEndDate(Date endDate) {
|
public Task withEndDate(LocalDate endDate) {
|
||||||
this.endDate = endDate;
|
this.endDate = endDate;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInTimePeriode(LocalDate searchStartDate, LocalDate searchEndDate){
|
||||||
|
return startDate.isAfter(searchStartDate) && startDate.isBefore(searchEndDate);
|
||||||
|
}
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
public long getId() { return id; }
|
public long getId() { return id; }
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public String getDescription() { return description; }
|
public String getDescription() { return description; }
|
||||||
public Date 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);
|
||||||
}
|
}
|
||||||
public Optional<Date> getEndDate() {
|
public Optional<LocalDate> getEndDate() {
|
||||||
return Optional.ofNullable(endDate);
|
return Optional.ofNullable(endDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
public class TaskTemplate {
|
||||||
|
@JsonProperty
|
||||||
|
private final String name;
|
||||||
|
@JsonProperty
|
||||||
|
private final String description;
|
||||||
|
@JsonProperty
|
||||||
|
private final int relativeStartDate;
|
||||||
|
@JsonProperty
|
||||||
|
private Integer relativeEndDate;
|
||||||
|
@JsonProperty
|
||||||
|
private Integer interval;
|
||||||
|
|
||||||
|
// TODO: reconsider if we need this
|
||||||
|
@JsonProperty
|
||||||
|
private boolean isOptional = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor
|
||||||
|
* (Used by deserializer)
|
||||||
|
*/
|
||||||
|
public TaskTemplate() {
|
||||||
|
this.name = "";
|
||||||
|
this.description = "";
|
||||||
|
this.relativeStartDate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
public void setRelativeEndDate(Integer relativeEndDate) {
|
||||||
|
this.relativeEndDate = relativeEndDate;
|
||||||
|
}
|
||||||
|
public void setInterval(Integer interval) {
|
||||||
|
this.interval = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TaskTemplate(String name, String description, int relativeStartDate) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.relativeStartDate = relativeStartDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task generateTask(LocalDate realStartDate, long cropId) {
|
||||||
|
Task task = new Task(name, description, realStartDate.plusDays(relativeStartDate), cropId);
|
||||||
|
if (relativeEndDate != null) {
|
||||||
|
task.withEndDate(realStartDate.plusDays(relativeEndDate));
|
||||||
|
}
|
||||||
|
if (interval != null) {
|
||||||
|
task.withInterval(interval);
|
||||||
|
}
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
package ch.zhaw.gartenverwaltung.types;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public record UserPlanting(
|
|
||||||
long plantId,
|
|
||||||
Date startDate,
|
|
||||||
int area
|
|
||||||
) {
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
public record WateringCycle(
|
||||||
|
int litersPerSqM,
|
||||||
|
int interval,
|
||||||
|
String[] notes
|
||||||
|
) {
|
||||||
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
module ch.zhaw.gartenverwaltung {
|
module ch.zhaw.gartenverwaltung {
|
||||||
requires javafx.controls;
|
requires javafx.controls;
|
||||||
requires javafx.fxml;
|
requires javafx.fxml;
|
||||||
|
requires com.fasterxml.jackson.databind;
|
||||||
|
requires com.fasterxml.jackson.datatype.jsr310;
|
||||||
|
requires com.fasterxml.jackson.datatype.jdk8;
|
||||||
|
requires java.logging;
|
||||||
|
|
||||||
opens ch.zhaw.gartenverwaltung to javafx.fxml;
|
opens ch.zhaw.gartenverwaltung to javafx.fxml;
|
||||||
|
opens ch.zhaw.gartenverwaltung.types to com.fasterxml.jackson.databind;
|
||||||
exports ch.zhaw.gartenverwaltung;
|
exports ch.zhaw.gartenverwaltung;
|
||||||
|
exports ch.zhaw.gartenverwaltung.types;
|
||||||
|
exports ch.zhaw.gartenverwaltung.json;
|
||||||
}
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="785.0" prefWidth="899.0"
|
||||||
|
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.CropDetailController">
|
||||||
|
<children>
|
||||||
|
<ScrollPane fitToWidth="true" prefHeight="759.0" prefWidth="664.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<content>
|
||||||
|
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="503.0" prefWidth="897.0">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<Button mnemonicParsing="false" onAction="#goBack" prefHeight="25.0" prefWidth="91.0" text="Go Back">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Button>
|
||||||
|
<Label fx:id="cropName_label" text="Label">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<HBox prefHeight="265.0" prefWidth="879.0">
|
||||||
|
<children>
|
||||||
|
<GridPane maxWidth="1.7976931348623157E308" prefHeight="296.0" prefWidth="577.0" HBox.hgrow="ALWAYS">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints halignment="LEFT" hgrow="SOMETIMES" maxWidth="284.0" minWidth="10.0" prefWidth="97.33334350585938" />
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" maxWidth="488.99999237060547" minWidth="10.0" prefWidth="481.9999898274739" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints maxHeight="149.66665903727215" minHeight="10.0" prefHeight="149.66665903727215" valignment="TOP" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="187.9999647140503" minHeight="10.0" prefHeight="51.00000762939453" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="105.66662597656247" minHeight="10.0" prefHeight="54.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints maxHeight="105.66662597656247" minHeight="10.0" prefHeight="46.66666666666666" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label prefHeight="17.0" prefWidth="65.0" text="Description:">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label text="Light-Level:" GridPane.rowIndex="1" />
|
||||||
|
<Label text="Spacing:" GridPane.rowIndex="2" />
|
||||||
|
<Label text="Soil-Type:" GridPane.rowIndex="3" />
|
||||||
|
<Label fx:id="description_label" text="Label" wrapText="true" GridPane.columnIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="10.0" top="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="light_label" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="spacing_label" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="soil_label" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="3">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets left="10.0" />
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<ImageView fx:id="imageView" fitHeight="300.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<Label text="Growth Phases:">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<VBox fx:id="growthPhases_vbox" prefHeight="135.0" prefWidth="879.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</VBox>
|
||||||
|
<Button mnemonicParsing="false" onAction="#editTaskList" prefHeight="25.0" prefWidth="92.0" text="Edit Tasklist">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Button>
|
||||||
|
<Label text="Pests:" />
|
||||||
|
<VBox fx:id="pests_vbox" prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</VBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
|
||||||
|
<children>
|
||||||
|
<Label text="Area:">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="60.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="area_label" text="Label">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="10.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Button fx:id="area_button" mnemonicParsing="false" onAction="#setArea" prefHeight="25.0" prefWidth="116.0" text="Add Area" />
|
||||||
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="20.0" layoutY="719.0" prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
|
||||||
|
<children>
|
||||||
|
<Label text="Location:">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="40.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="location_label" text="Label">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="10.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</Label>
|
||||||
|
<Button fx:id="location_button" mnemonicParsing="false" onAction="#setLocation" prefHeight="25.0" prefWidth="115.0" text="Add Location" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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">
|
||||||
|
<children>
|
||||||
|
<Label text="Gartenverwaltung">
|
||||||
|
<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>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="1040.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Task 1" wrapText="true" />
|
||||||
|
<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>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</Pane>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="938.0" prefWidth="1110.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MainFXMLController">
|
||||||
|
<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">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="home_button" mnemonicParsing="false" onAction="#goToHome" prefHeight="38.0" prefWidth="121.0" text="Home" HBox.hgrow="NEVER" />
|
||||||
|
<Button fx:id="plants_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToPlants" prefHeight="38.0" prefWidth="121.0" text="Plants" />
|
||||||
|
<Button fx:id="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="MySchedule" />
|
||||||
|
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</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" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyPlantsController">
|
||||||
|
<children>
|
||||||
|
<VBox layoutY="49.0" prefHeight="655.0" prefWidth="1175.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label text="MyPlants">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="28.0" />
|
||||||
|
</font>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Label>
|
||||||
|
<VBox fx:id="myPlants_vbox" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" />
|
||||||
|
<Button fx:id="addPlant_button" mnemonicParsing="false" onAction="#addPlant" prefHeight="45.0" prefWidth="155.0" text="Add new Plant">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets top="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ListView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.ColumnConstraints?>
|
||||||
|
<?import javafx.scene.layout.GridPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="572.0" prefWidth="867.0"
|
||||||
|
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyScheduleController">
|
||||||
|
<children>
|
||||||
|
<Label layoutX="14.0" layoutY="14.0" text="MySchedule">
|
||||||
|
<font>
|
||||||
|
<Font size="24.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<HBox layoutY="31.0" prefHeight="541.0" prefWidth="867.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="50.0">
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="scheduledPlants_listview" maxWidth="1.7976931348623157E308" prefHeight="522.0" prefWidth="365.0" HBox.hgrow="NEVER" />
|
||||||
|
<VBox maxWidth="1.7976931348623157E308" prefHeight="537.0" prefWidth="650.0" HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<GridPane alignment="CENTER_LEFT" gridLinesVisible="true" maxWidth="1.7976931348623157E308" prefHeight="403.0" prefWidth="575.0" VBox.vgrow="ALWAYS">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints fillWidth="false" hgrow="NEVER" maxWidth="278.0" minWidth="100.0" prefWidth="173.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="10.0" prefWidth="402.0" />
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||||
|
</rowConstraints>
|
||||||
|
<children>
|
||||||
|
<Label fx:id="day1_label" alignment="CENTER" prefHeight="32.0" prefWidth="173.0" text="Label" />
|
||||||
|
<Label fx:id="day2_label" alignment="CENTER" prefHeight="29.0" prefWidth="173.0" text="Label" GridPane.rowIndex="1" />
|
||||||
|
<Label fx:id="day3_label" alignment="CENTER" prefHeight="32.0" prefWidth="172.0" text="Label" GridPane.rowIndex="2" />
|
||||||
|
<Label fx:id="day4_label" alignment="CENTER" prefHeight="35.0" prefWidth="173.0" text="Label" GridPane.rowIndex="3" />
|
||||||
|
<Label fx:id="day5_label" alignment="CENTER" prefHeight="31.0" prefWidth="173.0" text="Label" GridPane.rowIndex="4" />
|
||||||
|
<Label fx:id="day6_label" alignment="CENTER" prefHeight="31.0" prefWidth="173.0" text="Label" GridPane.rowIndex="5" />
|
||||||
|
<Label fx:id="day7_label" alignment="CENTER" prefHeight="35.0" prefWidth="173.0" text="Label" GridPane.rowIndex="6" />
|
||||||
|
<Pane fx:id="day1_pane" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" />
|
||||||
|
<Pane fx:id="day2_pane" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
|
||||||
|
<Pane fx:id="day3_pane" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
<Pane fx:id="day4_pane" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
|
||||||
|
<Pane fx:id="day5_pane" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="4" />
|
||||||
|
<Pane fx:id="day6_pane" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" GridPane.rowIndex="5" />
|
||||||
|
<Pane fx:id="day7_pane" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="6" />
|
||||||
|
</children>
|
||||||
|
</GridPane>
|
||||||
|
<Pane prefHeight="119.0" prefWidth="575.0" VBox.vgrow="NEVER">
|
||||||
|
<children>
|
||||||
|
<Label alignment="TOP_LEFT" layoutX="14.0" layoutY="14.0" prefHeight="17.0" prefWidth="550.0" text="Importants Information:" wrapText="true">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="12.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="information_label" alignment="TOP_LEFT" layoutX="14.0" layoutY="31.0" maxWidth="1.7976931348623157E308" prefHeight="82.0" prefWidth="550.0" text="Label" wrapText="true" />
|
||||||
|
</children>
|
||||||
|
</Pane>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,93 @@
|
||||||
|
<?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.SplitPane?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.control.TitledPane?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
|
||||||
|
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
|
||||||
|
<children>
|
||||||
|
<SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<items>
|
||||||
|
<AnchorPane maxWidth="1.7976931348623157E308" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<VBox maxWidth="1.7976931348623157E308" prefHeight="850.6666666666666" prefWidth="894.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label prefHeight="45.0" prefWidth="903.0" text="Plants">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="30.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<TextField fx:id="search_plants" promptText="Search for Plant Name" />
|
||||||
|
<HBox alignment="CENTER_LEFT" prefHeight="480.0" prefWidth="881.0" VBox.vgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<ListView fx:id="list_plants" maxWidth="1.7976931348623157E308" prefHeight="497.0" prefWidth="400.0" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fx:id="img_plant" fitWidth="300" pickOnBounds="true" preserveRatio="true" HBox.hgrow="NEVER" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<Label prefHeight="33.0" prefWidth="919.0" text="Plant Information:">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="12.0" />
|
||||||
|
</font>
|
||||||
|
<padding>
|
||||||
|
<Insets top="15.0" />
|
||||||
|
</padding>
|
||||||
|
</Label>
|
||||||
|
<Label fx:id="description_plant" alignment="TOP_LEFT" maxWidth="1.7976931348623157E308" prefHeight="194.0" prefWidth="893.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." textAlignment="JUSTIFY" wrapText="true">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
</Label>
|
||||||
|
<Button fx:id="selectSowDay_button" alignment="CENTER" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#selectSowDate" prefHeight="38.0" prefWidth="917.0" text="Select Harvest/Sow Day" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
<AnchorPane maxWidth="300.0" minHeight="0.0" minWidth="300.0" prefHeight="160.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<VBox layoutX="38.0" layoutY="100.0" prefHeight="850.6666666666666" prefWidth="316.6666666666667" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Filter">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="17.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<TitledPane animated="false" text="Saison">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<VBox fx:id="seasons" layoutX="37.0" layoutY="-19.0" prefHeight="180.66666666666666" prefWidth="310.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
<TitledPane animated="false" text="Climate Zones">
|
||||||
|
<content>
|
||||||
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
|
||||||
|
<children>
|
||||||
|
<VBox fx:id="climate_zones" layoutX="36.0" layoutY="-19.0" prefHeight="180.66666666666666" prefWidth="310.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</items>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
</SplitPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.DatePicker?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.RadioButton?>
|
||||||
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0"
|
||||||
|
prefWidth="308.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.SelectSowDayController">
|
||||||
|
<children>
|
||||||
|
<VBox maxWidth="1.7976931348623157E308" prefHeight="408.0" prefWidth="640.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="popup_label" text="Label" />
|
||||||
|
<HBox alignment="CENTER" prefHeight="293.0" prefWidth="631.0">
|
||||||
|
<children>
|
||||||
|
<VBox alignment="CENTER_LEFT" prefHeight="88.0" prefWidth="155.0">
|
||||||
|
<children>
|
||||||
|
<RadioButton fx:id="sow_radio" mnemonicParsing="false" selected="true" text="Sow">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="10.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
<toggleGroup>
|
||||||
|
<ToggleGroup fx:id="group" />
|
||||||
|
</toggleGroup>
|
||||||
|
</RadioButton>
|
||||||
|
<RadioButton fx:id="harvest_radio" mnemonicParsing="false" text="Harvest" toggleGroup="$group" />
|
||||||
|
</children>
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets top="10.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</VBox>
|
||||||
|
<DatePicker fx:id="datepicker" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox fillHeight="false" prefHeight="54.0" prefWidth="631.0" VBox.vgrow="NEVER">
|
||||||
|
<children>
|
||||||
|
<Button fx:id="save_button" mnemonicParsing="false" onAction="#save" prefHeight="25.0" prefWidth="53.0" text="Save">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets right="10.0" />
|
||||||
|
</HBox.margin>
|
||||||
|
</Button>
|
||||||
|
<Button fx:id="cancel_button" mnemonicParsing="false" onAction="#cancel" text="Cancel" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,5 @@
|
||||||
|
Potato, Onion
|
||||||
|
Photos by Lars Blankers: https://unsplash.com/@lmablankers?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||||
|
|
||||||
|
Carrot
|
||||||
|
Photo by Maja Vujic: https://unsplash.com/@majavujic87?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
Binary file not shown.
After Width: | Height: | Size: 3.4 MiB |
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
Binary file not shown.
After Width: | Height: | Size: 50 KiB |
|
@ -0,0 +1,260 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"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,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "04-10",
|
||||||
|
"endDate": "07-10",
|
||||||
|
"type": "PLANT",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 7,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.",
|
||||||
|
"interval": 21,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "06-10",
|
||||||
|
"endDate": "08-10",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvest",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Early Carrot",
|
||||||
|
"description": "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
|
||||||
|
"image": "carrot.jpg",
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "02-20",
|
||||||
|
"endDate": "03-10",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "SOW",
|
||||||
|
"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,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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,
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "5,35,2.5",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Summertime Onion",
|
||||||
|
"description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
|
||||||
|
"image": "onion.jpg",
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "03-15",
|
||||||
|
"endDate": "04-10",
|
||||||
|
"type": "SOW",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 15,
|
||||||
|
"interval": 4,
|
||||||
|
"notes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 0,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "04-10",
|
||||||
|
"endDate": "07-10",
|
||||||
|
"type": "PLANT",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": 15,
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "07-10",
|
||||||
|
"endDate": "09-20",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvesting",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 14,
|
||||||
|
"description": "When ready for harvest, the leaves on your onion plants will start to flop over. This happens at the \"neck\" of the onion and it signals that the plant has stopped growing and is ready for storage. Onions should be harvested soon thereafter",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "15,30,2",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,56 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id" : 1,
|
||||||
|
"name" : "sow plant",
|
||||||
|
"description": "Plant the seeds, crops in de bed.",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-05-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 2,
|
||||||
|
"name" : "water plant",
|
||||||
|
"description": "water the plant, so that the soil is wet around the plant.",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 2,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 3,
|
||||||
|
"name" : "fertilize plant",
|
||||||
|
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
||||||
|
"startDate" : "2022-06-01",
|
||||||
|
"endDate" : "2022-08-01",
|
||||||
|
"interval" : 28,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 4,
|
||||||
|
"name" : "covering plant",
|
||||||
|
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
|
||||||
|
"startDate" : "2022-07-01",
|
||||||
|
"endDate" : "2022-07-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 5,
|
||||||
|
"name" : "look after plant",
|
||||||
|
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 5,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 6,
|
||||||
|
"name" : "harvest plant",
|
||||||
|
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
||||||
|
"startDate" : "2022-09-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,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
|
||||||
|
}
|
||||||
|
]
|
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
|
@ -0,0 +1,113 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.gardenplan;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.*;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.PlantNotFoundException;
|
||||||
|
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.*;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.MonthDay;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class GardenPlanModelTest {
|
||||||
|
GardenPlan gardenPlan;
|
||||||
|
List<Crop> cropList;
|
||||||
|
Crop exampleCropOnion;
|
||||||
|
Crop exampleCropCarrot;
|
||||||
|
Crop exampleCrop1;
|
||||||
|
Crop exampleCrop2;
|
||||||
|
Crop exampleCrop3;
|
||||||
|
Plant examplePlantOnion;
|
||||||
|
Plant examplePlantCarrot;
|
||||||
|
Gardenplanmodel model;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws IOException {
|
||||||
|
|
||||||
|
|
||||||
|
examplePlantOnion = new Plant(
|
||||||
|
0,
|
||||||
|
"summertime onion",
|
||||||
|
"Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
|
||||||
|
null,
|
||||||
|
"15,30,2",
|
||||||
|
0,
|
||||||
|
"sandy to loamy, loose soil, free of stones",
|
||||||
|
new ArrayList<>(),
|
||||||
|
List.of(new GrowthPhase(MonthDay.of(6, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.HARVEST, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(4, 3), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(8, 5), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(2, 8), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(10, 2), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
|
||||||
|
|
||||||
|
exampleCropOnion = new Crop(examplePlantOnion.id(), LocalDate.of(2023,3,1));
|
||||||
|
exampleCropOnion.withId(3);
|
||||||
|
examplePlantCarrot = new Plant(
|
||||||
|
1,
|
||||||
|
"Early Carrot",
|
||||||
|
"Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
|
||||||
|
null,
|
||||||
|
"5,35,2.5",
|
||||||
|
0,
|
||||||
|
"sandy to loamy, loose soil, free of stones",
|
||||||
|
new ArrayList<>(),
|
||||||
|
List.of(new GrowthPhase(MonthDay.of(4, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())));
|
||||||
|
exampleCropCarrot = new Crop(examplePlantCarrot.id(), LocalDate.now());
|
||||||
|
exampleCropCarrot.withId(5);
|
||||||
|
|
||||||
|
exampleCrop1 = new Crop(1, LocalDate.of(2023,2,25));
|
||||||
|
exampleCrop1.withId(0);
|
||||||
|
exampleCrop1.withArea(0.5);
|
||||||
|
exampleCrop2 = new Crop(1,LocalDate.of(2023,3,1));
|
||||||
|
exampleCrop2.withId(1);
|
||||||
|
exampleCrop2.withArea(0.5);
|
||||||
|
exampleCrop3 = new Crop(0,LocalDate.of(2023,3,01));
|
||||||
|
exampleCrop3.withId(2);
|
||||||
|
exampleCrop3.withArea(1.0);
|
||||||
|
|
||||||
|
cropList = new ArrayList<>();
|
||||||
|
cropList.add(exampleCrop1);
|
||||||
|
cropList.add(exampleCrop2);
|
||||||
|
cropList.add(exampleCrop3);
|
||||||
|
gardenPlan = mockGardenPlan(cropList);
|
||||||
|
|
||||||
|
TaskListModel taskListModel = new TaskListModel(new JsonTaskDatabase(), new JsonPlantDatabase());
|
||||||
|
model = new Gardenplanmodel(taskListModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
GardenPlan mockGardenPlan(List<Crop> cropList) throws IOException {
|
||||||
|
GardenPlan gardenPlan = mock(GardenPlan.class);
|
||||||
|
when(gardenPlan.getCrops()).thenReturn(cropList);
|
||||||
|
when(gardenPlan.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
|
||||||
|
when(gardenPlan.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
|
||||||
|
return gardenPlan;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
|
||||||
|
|
||||||
|
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
|
||||||
|
exampleCropOnion = model.getCrop(2L).get();
|
||||||
|
assertEquals(model.getCrops().get(2),exampleCropOnion);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void removeCrop() throws IOException {
|
||||||
|
exampleCrop1.withId(2);
|
||||||
|
exampleCrop1.withArea(1.500000);
|
||||||
|
model.removeCrop(exampleCrop1);
|
||||||
|
assertEquals(2,model.getCrops().size());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class JsonGardenPlanTest {
|
||||||
|
private GardenPlan testDatabase;
|
||||||
|
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
/**
|
||||||
|
* Files to isolate the test-units
|
||||||
|
*/
|
||||||
|
private final URL dbDataSource = this.getClass().getResource("user-crops.json");
|
||||||
|
private final URL testFile = this.getClass().getResource("test-user-crops.json");
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void connectToDb() throws URISyntaxException, IOException {
|
||||||
|
assertNotNull(testFile);
|
||||||
|
assertNotNull(dbDataSource);
|
||||||
|
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
testDatabase = new JsonGardenPlan(dbDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check if results are retrieved completely.")
|
||||||
|
void getCropsNotEmpty() {
|
||||||
|
List<Crop> testList;
|
||||||
|
try {
|
||||||
|
testList = testDatabase.getCrops();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Assertions.assertEquals(3, testList.size());
|
||||||
|
|
||||||
|
List<Long> plantIds = testList.stream().map(Crop::getPlantId).collect(Collectors.toList());
|
||||||
|
List<Long> expected = Arrays.asList(1L, 1L, 0L);
|
||||||
|
Assertions.assertEquals(expected, plantIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check whether single access works.")
|
||||||
|
void getCropById() throws IOException {
|
||||||
|
Optional<Crop> testCrop = testDatabase.getCropById(1);
|
||||||
|
assertTrue(testCrop.isPresent());
|
||||||
|
Assertions.assertEquals(1, testCrop.get().getPlantId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check for a nonexisting crop.")
|
||||||
|
void getCropByIdMustFail() throws IOException {
|
||||||
|
Optional<Crop> testCrop = testDatabase.getCropById(99);
|
||||||
|
Assertions.assertFalse(testCrop.isPresent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Add new Crop.")
|
||||||
|
void addNewCrop() {
|
||||||
|
Crop crop = new Crop(3L, LocalDate.parse("2023-02-22", formatter));
|
||||||
|
try {
|
||||||
|
testDatabase.saveCrop(crop);
|
||||||
|
assertTrue(crop.getCropId().isPresent());
|
||||||
|
Optional<Crop> testCrop = testDatabase.getCropById(crop.getCropId().get());
|
||||||
|
|
||||||
|
assertTrue(testCrop.isPresent());
|
||||||
|
Assertions.assertEquals(crop, testCrop.get());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Remove crop")
|
||||||
|
void removeCrop(){
|
||||||
|
try {
|
||||||
|
Optional<Crop> crop = testDatabase.getCropById(2L);
|
||||||
|
Assertions.assertTrue(crop.isPresent());
|
||||||
|
testDatabase.removeCrop(crop.get());
|
||||||
|
crop = testDatabase.getCropById(2L);
|
||||||
|
Assertions.assertFalse(crop.isPresent());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class JsonPlantDatabaseTest {
|
||||||
|
PlantDatabase testDatabase;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void connectToDb() {
|
||||||
|
testDatabase = new JsonPlantDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check if results are retrieved completely")
|
||||||
|
void getPlantListNotEmpty() {
|
||||||
|
List<Plant> testList;
|
||||||
|
try {
|
||||||
|
testList = testDatabase.getPlantList(HardinessZone.ZONE_8A);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Assertions.assertEquals(3, testList.size());
|
||||||
|
|
||||||
|
List<String> names = testList.stream().map(Plant::name).collect(Collectors.toList());
|
||||||
|
List<String> expected = Arrays.asList("Potato","Early Carrot","Summertime Onion");
|
||||||
|
Assertions.assertEquals(expected,names);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check if results are retrieved correctly when empty")
|
||||||
|
void getPlantListEmpty() {
|
||||||
|
List<Plant> testList;
|
||||||
|
try {
|
||||||
|
testList = testDatabase.getPlantList(HardinessZone.ZONE_1A);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Assertions.assertEquals(0, testList.size());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check whether single access works.")
|
||||||
|
void getPlantByIdAndZone() {
|
||||||
|
Optional<Plant> testPlant;
|
||||||
|
try {
|
||||||
|
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Assertions.assertTrue(testPlant.isPresent());
|
||||||
|
Assertions.assertEquals("Early Carrot", testPlant.get().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check whether single access respects zone correctly.")
|
||||||
|
void getPlantByIdAndWrongZone() throws HardinessZoneNotSetException, IOException {
|
||||||
|
Optional<Plant> testPlant = testDatabase.getPlantById(HardinessZone.ZONE_1A, 1);
|
||||||
|
Assertions.assertFalse(testPlant.isPresent());
|
||||||
|
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
|
||||||
|
Assertions.assertTrue(testPlant.isPresent());
|
||||||
|
|
||||||
|
Assertions.assertEquals("Early Carrot", testPlant.get().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check for a nonexisting plant.")
|
||||||
|
void getPlantByIdMustFail() {
|
||||||
|
Optional<Plant> testPlant;
|
||||||
|
try {
|
||||||
|
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 99);
|
||||||
|
} catch (IOException | HardinessZoneNotSetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
Assertions.assertFalse(testPlant.isPresent());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
public class JsonTaskDatabaseTest {
|
||||||
|
|
||||||
|
TaskDatabase testDatabase;
|
||||||
|
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
|
||||||
|
@BeforeEach
|
||||||
|
void connectToDb() {
|
||||||
|
// testDatabase = new JsonTaskDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Check if results are retrieved completely")
|
||||||
|
void getTasks(){
|
||||||
|
/*
|
||||||
|
List<Task> taskList=null;
|
||||||
|
try {
|
||||||
|
taskList = testDatabase.getTaskList(formatter.parse("01.05.2022"), formatter.parse("01.08.2022"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assertions.assertTrue(taskList.size()>0);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getTaskForCrop() {
|
||||||
|
// TODO implement Test
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void removeTasksForCrop() {
|
||||||
|
// TODO implement Test
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.plantList;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase;
|
||||||
|
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.*;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.MonthDay;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class PlantListModelTest {
|
||||||
|
PlantDatabase plantDatabase;
|
||||||
|
List<Plant> examplePlantList;
|
||||||
|
PlantListModel model;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws HardinessZoneNotSetException, IOException {
|
||||||
|
createExamplePlantList();
|
||||||
|
plantDatabase = mockPlantDatabase(examplePlantList);
|
||||||
|
model = new PlantListModel(plantDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void createExamplePlantList(){
|
||||||
|
examplePlantList = new ArrayList<>();
|
||||||
|
examplePlantList.add(new Plant(
|
||||||
|
20,
|
||||||
|
"summertime onion",
|
||||||
|
"Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
|
||||||
|
null,
|
||||||
|
"15,30,2",
|
||||||
|
0,
|
||||||
|
"sandy to loamy, loose soil, free of stones",
|
||||||
|
new ArrayList<>(),
|
||||||
|
List.of(new GrowthPhase(MonthDay.of(6, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.HARVEST, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(4, 3), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(8, 5), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(2, 8), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(10, 2), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())))
|
||||||
|
);
|
||||||
|
examplePlantList.add(new Plant(
|
||||||
|
0,
|
||||||
|
"Potato",
|
||||||
|
"The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.",
|
||||||
|
null,
|
||||||
|
"35",
|
||||||
|
6,
|
||||||
|
"sandy",
|
||||||
|
new ArrayList<>(),
|
||||||
|
List.of(new GrowthPhase(MonthDay.of(6, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.HARVEST, HardinessZone.ZONE_8A, new ArrayList<>()),
|
||||||
|
new GrowthPhase(MonthDay.of(6, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())))
|
||||||
|
);
|
||||||
|
examplePlantList.add(new Plant(
|
||||||
|
1,
|
||||||
|
"Early Carrot",
|
||||||
|
"Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
|
||||||
|
null,
|
||||||
|
"5,35,2.5",
|
||||||
|
0,
|
||||||
|
"sandy to loamy, loose soil, free of stones",
|
||||||
|
new ArrayList<>(),
|
||||||
|
List.of(new GrowthPhase(MonthDay.of(4, 4), MonthDay.of(12, 4), 0, new WateringCycle(0, 0, null), GrowthPhaseType.PLANT, HardinessZone.ZONE_8A, new ArrayList<>())))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlantDatabase mockPlantDatabase(List<Plant> plantList) throws HardinessZoneNotSetException, IOException {
|
||||||
|
PlantDatabase plantDatabase = mock(JsonPlantDatabase.class);
|
||||||
|
when(plantDatabase.getPlantList(HardinessZone.ZONE_8A)).thenReturn(plantList);
|
||||||
|
when(plantDatabase.getPlantList(HardinessZone.ZONE_1A)).thenReturn(new ArrayList<>());
|
||||||
|
when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 0)).thenReturn(Optional.of(plantList.get(1)));
|
||||||
|
when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 1)).thenReturn(Optional.of(plantList.get(2)));
|
||||||
|
when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 20)).thenReturn(Optional.of(plantList.get(0)));
|
||||||
|
when(plantDatabase.getPlantById(HardinessZone.ZONE_8A, 2)).thenReturn(Optional.empty());
|
||||||
|
|
||||||
|
return plantDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkCurrentZone(HardinessZone expectedZone) {
|
||||||
|
assertEquals(expectedZone, model.getCurrentZone());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setCurrentZone() {
|
||||||
|
checkCurrentZone(HardinessZone.ZONE_8A); // TODO change to get default zone from config
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
checkCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_8A);
|
||||||
|
checkCurrentZone(HardinessZone.ZONE_8A);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPlantList() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
List<Plant> plantListResult = model.getPlantList(HardinessZone.ZONE_8A);
|
||||||
|
checkCurrentZone(HardinessZone.ZONE_8A);
|
||||||
|
assertEquals(examplePlantList.size(), plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(2), plantListResult.get(0));
|
||||||
|
assertEquals(examplePlantList.get(1), plantListResult.get(1));
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(2));
|
||||||
|
|
||||||
|
assertEquals(0, model.getPlantList(HardinessZone.ZONE_1A).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getSortedPlantList() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
List<Plant> plantListResult = model.getSortedPlantList(HardinessZone.ZONE_8A, PlantListModel.sortByName);
|
||||||
|
checkCurrentZone(HardinessZone.ZONE_8A);
|
||||||
|
assertEquals(examplePlantList.size(), plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(2), plantListResult.get(0));
|
||||||
|
assertEquals(examplePlantList.get(1), plantListResult.get(1));
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(2));
|
||||||
|
|
||||||
|
plantListResult = model.getSortedPlantList(HardinessZone.ZONE_8A, PlantListModel.SortById);
|
||||||
|
assertEquals(examplePlantList.size(), plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(1), plantListResult.get(0));
|
||||||
|
assertEquals(examplePlantList.get(2), plantListResult.get(1));
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFilteredPlantList() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
Predicate<Plant> predicate = plant -> plant.name().toUpperCase(Locale.ROOT).contains("E");
|
||||||
|
List<Plant> plantListResult = model.getFilteredPlantList(HardinessZone.ZONE_8A, predicate);
|
||||||
|
checkCurrentZone(HardinessZone.ZONE_8A);
|
||||||
|
assertEquals(2, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(2), plantListResult.get(0));
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFilteredPlantListById() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
List<Plant> plantListResult = model.getFilteredPlantListById(HardinessZone.ZONE_8A, 2L);
|
||||||
|
assertEquals(0, plantListResult.size());
|
||||||
|
plantListResult = model.getFilteredPlantListById(HardinessZone.ZONE_8A, 20L);
|
||||||
|
assertEquals(1, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFilteredPlantListByString() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
List<Plant> plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "#2");
|
||||||
|
assertEquals(0, plantListResult.size());
|
||||||
|
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "#20");
|
||||||
|
assertEquals(1, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(0));
|
||||||
|
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "#a2");
|
||||||
|
assertEquals(0, plantListResult.size());
|
||||||
|
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "onion");
|
||||||
|
assertEquals(1, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(0));
|
||||||
|
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "white roots");
|
||||||
|
assertEquals(1, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(1), plantListResult.get(0));
|
||||||
|
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "apple");
|
||||||
|
assertEquals(0, plantListResult.size());
|
||||||
|
plantListResult = model.getFilteredPlantListByString(HardinessZone.ZONE_8A, "");
|
||||||
|
assertEquals(3, plantListResult.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFilteredPlantListByPlantingSaison() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
List<Plant> plantListResult = model.getFilteredPlantListByPlantingSaison(HardinessZone.ZONE_8A, MonthDay.of(4, 4), MonthDay.of(8, 4));
|
||||||
|
assertEquals(2, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(2), plantListResult.get(0));
|
||||||
|
assertEquals(examplePlantList.get(1), plantListResult.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFilteredPlantListByHarvestSaison() throws HardinessZoneNotSetException, IOException {
|
||||||
|
model.setCurrentZone(HardinessZone.ZONE_1A);
|
||||||
|
List<Plant> plantListResult = model.getFilteredPlantListByHarvestSaison(HardinessZone.ZONE_8A, MonthDay.of(4, 4), MonthDay.of(8, 4));
|
||||||
|
assertEquals(2, plantListResult.size());
|
||||||
|
assertEquals(examplePlantList.get(1), plantListResult.get(0));
|
||||||
|
assertEquals(examplePlantList.get(0), plantListResult.get(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.taskList;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.io.*;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.HardinessZone;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Plant;
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Task;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class TaskListModelTest {
|
||||||
|
TaskDatabase taskDatabase;
|
||||||
|
PlantDatabase plantDatabase;
|
||||||
|
List<Task> exampleTaskList;
|
||||||
|
Map<Long, Plant> examplePlantMap;
|
||||||
|
TaskListModel model;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws IOException {
|
||||||
|
createExampleTaskList();
|
||||||
|
taskDatabase = mockTaskDatabase(exampleTaskList);
|
||||||
|
plantDatabase = mockPlantDatabase(examplePlantMap);
|
||||||
|
model = new TaskListModel(taskDatabase, plantDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskDatabase mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
|
||||||
|
TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
|
||||||
|
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
|
||||||
|
|
||||||
|
List<Task> pastTasks = new ArrayList<>();
|
||||||
|
pastTasks.add(exampleTaskList.get(0));
|
||||||
|
pastTasks.add(exampleTaskList.get(2));
|
||||||
|
pastTasks.add(exampleTaskList.get(4));
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
|
||||||
|
|
||||||
|
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
|
||||||
|
when(taskDatabase.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
|
||||||
|
|
||||||
|
return taskDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlantDatabase mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
|
||||||
|
return new PlantDatabase() {
|
||||||
|
@Override
|
||||||
|
public List<Plant> getPlantList(HardinessZone zone) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Plant> getPlantById(HardinessZone zone, long id) {
|
||||||
|
return Optional.ofNullable(examplePlantMap.get(id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void createExampleTaskList() {
|
||||||
|
exampleTaskList = new ArrayList<>();
|
||||||
|
exampleTaskList.add(new Task("name", "description", LocalDate.now().minusDays(1), 1L));
|
||||||
|
exampleTaskList.add(new Task("name", "description", LocalDate.now().plusDays(1), 2L));
|
||||||
|
exampleTaskList.add(new Task("name", "description", LocalDate.now(), 1L));
|
||||||
|
exampleTaskList.add(new Task("name", "description", LocalDate.of(9019, 5, 5), 1L));
|
||||||
|
exampleTaskList.add(new Task("name", "description", LocalDate.of(2019, 5, 5), 2L));
|
||||||
|
}
|
||||||
|
|
||||||
|
void createExamplePlantMap() {
|
||||||
|
examplePlantMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addTask() throws IOException {
|
||||||
|
Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
|
||||||
|
model.addTask(taskToAdd);
|
||||||
|
verify(taskDatabase, times(1)).saveTask(taskToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void removeTask() throws IOException {
|
||||||
|
Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
|
||||||
|
model.removeTask(taskToRemove);
|
||||||
|
verify(taskDatabase, times(1)).removeTask(taskToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getTaskList() throws IOException {
|
||||||
|
List<Task> listToCheck = model.getTaskList();
|
||||||
|
assertEquals(5, listToCheck.size());
|
||||||
|
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
|
||||||
|
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
|
||||||
|
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
|
||||||
|
assertEquals(exampleTaskList.get(1), listToCheck.get(3));
|
||||||
|
assertEquals(exampleTaskList.get(3), listToCheck.get(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getFutureTasks() throws IOException {
|
||||||
|
List<Task> listToCheck = model.getFutureTasks();
|
||||||
|
assertEquals(3, listToCheck.size());
|
||||||
|
assertEquals(exampleTaskList.get(2), listToCheck.get(0));
|
||||||
|
assertEquals(exampleTaskList.get(1), listToCheck.get(1));
|
||||||
|
assertEquals(exampleTaskList.get(3), listToCheck.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getPastTasks() throws IOException {
|
||||||
|
List<Task> listToCheck = model.getPastTasks();
|
||||||
|
assertEquals(3, listToCheck.size());
|
||||||
|
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
|
||||||
|
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
|
||||||
|
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getTasksUpcomingWeek() throws IOException {
|
||||||
|
List<List<Task>> dayList = model.getTasksUpcomingWeek();
|
||||||
|
assertEquals(7, dayList.size());
|
||||||
|
|
||||||
|
//Check day 0
|
||||||
|
assertEquals(1, dayList.get(0).size());
|
||||||
|
assertEquals(exampleTaskList.get(2), dayList.get(0).get(0));
|
||||||
|
|
||||||
|
//Check day 1
|
||||||
|
assertEquals(1, dayList.get(1).size());
|
||||||
|
assertEquals(exampleTaskList.get(1), dayList.get(1).get(0));
|
||||||
|
|
||||||
|
//Check day 2
|
||||||
|
assertEquals(0, dayList.get(2).size());
|
||||||
|
//Check day 3
|
||||||
|
assertEquals(0, dayList.get(3).size());
|
||||||
|
//Check day 4
|
||||||
|
assertEquals(0, dayList.get(4).size());
|
||||||
|
//Check day 5
|
||||||
|
assertEquals(0, dayList.get(5).size());
|
||||||
|
//Check day 6
|
||||||
|
assertEquals(0, dayList.get(6).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void removeTasksForCrop() throws IOException {
|
||||||
|
model.removeTasksForCrop(1L);
|
||||||
|
verify(taskDatabase, times(1)).removeTasksForCrop(1L);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Potato",
|
||||||
|
"description": "The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.",
|
||||||
|
"light": 6,
|
||||||
|
"spacing": "35",
|
||||||
|
"soil": "sandy",
|
||||||
|
"image": "potato.jpg",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "Rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "Less water."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "03-10",
|
||||||
|
"endDate": "04-10",
|
||||||
|
"type": "SOW",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Germinate",
|
||||||
|
"relativeStartDate": -14,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "04-10",
|
||||||
|
"endDate": "07-10",
|
||||||
|
"type": "PLANT",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 7,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
|
||||||
|
"interval": 21,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "06-10",
|
||||||
|
"endDate": "08-10",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvest",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Early Carrot",
|
||||||
|
"description": "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
|
||||||
|
"image": "carrot.jpg",
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "02-20",
|
||||||
|
"endDate": "03-10",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "SOW",
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 15,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 0,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "03-10",
|
||||||
|
"endDate": "05-10",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "PLANT",
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": [
|
||||||
|
"Be careful not to pour water over the leaves, as this will lead to sunburn."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": 15,
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "05-10",
|
||||||
|
"endDate": "05-20",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvesting",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 14,
|
||||||
|
"description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "5,35,2.5",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Summertime Onion",
|
||||||
|
"description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
|
||||||
|
"image": "onion.jpg",
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "03-15",
|
||||||
|
"endDate": "04-10",
|
||||||
|
"type": "SOW",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 15,
|
||||||
|
"interval": 4,
|
||||||
|
"notes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 0,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "04-10",
|
||||||
|
"endDate": "07-10",
|
||||||
|
"type": "PLANT",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": 15,
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "07-10",
|
||||||
|
"endDate": "09-20",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvesting",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 14,
|
||||||
|
"description": "When ready for harvest, the leaves on your onion plants will start to flop over. This happens at the \"neck\" of the onion and it signals that the plant has stopped growing and is ready for storage. Onions should be harvested soon thereafter",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "15,30,2",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,56 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id" : 1,
|
||||||
|
"name" : "sow plant",
|
||||||
|
"description": "Plant the seeds, crops in de bed.",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-05-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 2,
|
||||||
|
"name" : "water plant",
|
||||||
|
"description": "water the plant, so that the soil is wet around the plant.",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 2,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 3,
|
||||||
|
"name" : "fertilize plant",
|
||||||
|
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
|
||||||
|
"startDate" : "2022-06-01",
|
||||||
|
"endDate" : "2022-08-01",
|
||||||
|
"interval" : 28,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 4,
|
||||||
|
"name" : "covering plant",
|
||||||
|
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
|
||||||
|
"startDate" : "2022-07-01",
|
||||||
|
"endDate" : "2022-07-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 5,
|
||||||
|
"name" : "look after plant",
|
||||||
|
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
|
||||||
|
"startDate" : "2022-05-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 5,
|
||||||
|
"cropId" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id" : 6,
|
||||||
|
"name" : "harvest plant",
|
||||||
|
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
|
||||||
|
"startDate" : "2022-09-01",
|
||||||
|
"endDate" : "2022-09-01",
|
||||||
|
"interval" : 0,
|
||||||
|
"cropId" : 0
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,257 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Potato",
|
||||||
|
"description": "The potato is a tuber, round or oval, with small white roots called 'eyes', that are growth buds. The size varies depending on the variety; the colour of the skin can be white, yellow or even purple.",
|
||||||
|
"light": 6,
|
||||||
|
"spacing": "35",
|
||||||
|
"soil": "sandy",
|
||||||
|
"image": "potato.jpg",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "Rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "Less water."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "03-10",
|
||||||
|
"endDate": "04-10",
|
||||||
|
"type": "SOW",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Germinate",
|
||||||
|
"relativeStartDate": -14,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "04-10",
|
||||||
|
"endDate": "07-10",
|
||||||
|
"type": "PLANT",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 7,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
|
||||||
|
"interval": 21,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "06-10",
|
||||||
|
"endDate": "08-10",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvest",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Once the foliage has wilted and dried completely, harvest on a dry day. Store in a dark and cool location.",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Early Carrot",
|
||||||
|
"description": "Carrot, (Daucus carota), herbaceous, generally biennial plant of the Apiaceae family that produces an edible taproot. Among common varieties root shapes range from globular to long, with lower ends blunt to pointed. Besides the orange-coloured roots, white-, yellow-, and purple-fleshed varieties are known.",
|
||||||
|
"image": "carrot.jpg",
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "02-20",
|
||||||
|
"endDate": "03-10",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "SOW",
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 15,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 0,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "03-10",
|
||||||
|
"endDate": "05-10",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "PLANT",
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": [
|
||||||
|
"Be careful not to pour water over the leaves, as this will lead to sunburn."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": 15,
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "05-10",
|
||||||
|
"endDate": "05-20",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": []
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvesting",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 14,
|
||||||
|
"description": "When the leaves turn to a yellowish brown. Do not harvest earlier. The plant will show when it's ready.",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "5,35,2.5",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Summertime Onion",
|
||||||
|
"description": "Onion, (Allium cepa), herbaceous biennial plant in the amaryllis family (Amaryllidaceae) grown for its edible bulb. The onion is likely native to southwestern Asia but is now grown throughout the world, chiefly in the temperate zones. Onions are low in nutrients but are valued for their flavour and are used widely in cooking. They add flavour to such dishes as stews, roasts, soups, and salads and are also served as a cooked vegetable.",
|
||||||
|
"image": "onion.jpg",
|
||||||
|
"lifecycle": [
|
||||||
|
{
|
||||||
|
"startDate": "03-15",
|
||||||
|
"endDate": "04-10",
|
||||||
|
"type": "SOW",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 15,
|
||||||
|
"interval": 4,
|
||||||
|
"notes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 0,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "04-10",
|
||||||
|
"endDate": "07-10",
|
||||||
|
"type": "PLANT",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 25,
|
||||||
|
"interval": 3,
|
||||||
|
"notes": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "hilling",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": null,
|
||||||
|
"description": "Mound up the soil around the plant until just the top few leaves show above the soil. ",
|
||||||
|
"interval": 15,
|
||||||
|
"isOptional": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"startDate": "07-10",
|
||||||
|
"endDate": "09-20",
|
||||||
|
"type": "HARVEST",
|
||||||
|
"zone": "ZONE_8A",
|
||||||
|
"group": 0,
|
||||||
|
"wateringCycle": {
|
||||||
|
"litersPerSqM": 0,
|
||||||
|
"interval": null,
|
||||||
|
"notes": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"taskTemplates": [
|
||||||
|
{
|
||||||
|
"name": "Harvesting",
|
||||||
|
"relativeStartDate": 0,
|
||||||
|
"relativeEndDate": 14,
|
||||||
|
"description": "When ready for harvest, the leaves on your onion plants will start to flop over. This happens at the \"neck\" of the onion and it signals that the plant has stopped growing and is ready for storage. Onions should be harvested soon thereafter",
|
||||||
|
"interval": null,
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"soil": "sandy to loamy, loose soil, free of stones",
|
||||||
|
"spacing": "15,30,2",
|
||||||
|
"pests": [
|
||||||
|
{
|
||||||
|
"name": "Rot",
|
||||||
|
"description": "rot, any of several plant diseases, caused by any of hundreds of species of soil-borne bacteria, fungi, and funguslike organisms (Oomycota). Rot diseases are characterized by plant decomposition and putrefaction. The decay may be hard, dry, spongy, watery, mushy, or slimy and may affect any plant part.",
|
||||||
|
"measures": "less water"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,20 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cropId": 0,
|
||||||
|
"plantId": 1,
|
||||||
|
"startDate": "2023-02-25",
|
||||||
|
"area": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cropId": 1,
|
||||||
|
"plantId": 1,
|
||||||
|
"startDate": "2023-03-01",
|
||||||
|
"area": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cropId": 2,
|
||||||
|
"plantId": 0,
|
||||||
|
"startDate": "2023-03-25",
|
||||||
|
"area": 1.5
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,2 @@
|
||||||
|
[
|
||||||
|
]
|
Loading…
Reference in New Issue