Compare commits

..
Author SHA1 Message Date
giavaphi ea4a1ecc6a gui details of crop basics 2022-11-06 17:46:35 +01:00
giavaphi 3077e02b32 #48 and #47 basics of gui 2022-11-05 23:50:45 +01:00
David Guler f6ec411b7a doc: design class diagram "dcd" 2022-11-05 09:50:35 +01:00
giavaphiandGitHub Enterprise a43a23427c Merge pull request #50 from schrom01/feature_json-gardenplan_M2 2022-11-04 12:12:43 +01:00
gulerdavandGitHub Enterprise fb0c50a715 Merge branch 'dev' into feature_json-gardenplan_M2 2022-11-03 14:31:23 +01:00
David Guler 83bc011870 #26 added fixed test files, made Crop class testable 2022-11-03 14:28:26 +01:00
David Guler ce93531ab8 Made tests pass for plants not in zone 2022-11-03 14:25:25 +01:00
giavaphiandGitHub Enterprise 7a6a0eb66f Merge pull request #45 from schrom01/feature_controllerPlantList_M2
Plant Controller Filter to hardiness zone and season
2022-10-31 13:07:49 +01:00
gulerdavandGitHub Enterprise 3afeb8a22d Merge branch 'dev' into feature_controllerPlantList_M2 2022-10-31 13:05:49 +01:00
gulerdavandGitHub Enterprise 88f9bf7990 Merge pull request #44 from schrom01/feature_plantlist-gui_M2
Display images in gui and fix image deserialise with spaces
2022-10-31 12:59:06 +01:00
giavaphi 5f35d99839 fixed merge conflict PlantsController 2022-10-31 12:52:27 +01:00
schrom01 d0cef1fe82 fixed problems with file Path
#43
2022-10-31 12:43:13 +01:00
Elias Csomor 82eab6d5cd continue GardenPlan tests 2022-10-31 11:21:45 +01:00
Elias Csomor 5411ac69ae initial GardenPlan tests 2022-10-31 11:15:27 +01:00
Elias Csomor 146c43d5d9 extended tests for Zones 2022-10-31 10:07:06 +01:00
David Guler 7a060be84a case insensitive serarch, images 2022-10-31 10:06:17 +01:00
Elias Csomor 160880d4f5 removed useless quotes, corrected typo 2022-10-31 09:42:39 +01:00
giavaphi 5b039eb762 #12 small changes and java doc 2022-10-31 09:23:44 +01:00
gulerdavandGitHub Enterprise 6c00b7f182 Merge pull request #41 from schrom01/feature_plantList_M2
fixed Method getFilteredPlantListByString
2022-10-31 08:01:43 +01:00
giavaphi c83b8695ab #12 PlantsController filter list view 2022-10-30 23:23:19 +01:00
giavaphi f22ef61d3c #42 bug resize window fix 2022-10-30 18:06:18 +01:00
gulerdavandGitHub Enterprise f452b73233 Merge pull request #40 from schrom01/feature_plantList_M2
Feature plant list m2
2022-10-30 10:36:07 +01:00
Roman SchenkandGitHub Enterprise 691d2a6345 Merge pull request #38 from schrom01/feature_skeletonGUI_M2
#16 create MainFXML with skeletal structure
2022-10-30 09:23:21 +01:00
Roman SchenkandGitHub Enterprise 246fed7826 Merge pull request #39 from schrom01/feature_json-plant-db_M2
#36 Added images to Plant database
2022-10-29 11:36:14 +02:00
David Guler 5b860e0ff4 #10 doc: layer-diagram 2022-10-29 10:17:51 +02:00
gulerdavandGitHub Enterprise 554c560832 Merge pull request #37 from schrom01/feature_json-task-db_M2
Feature json task db m2
2022-10-29 09:47:17 +02:00
gulerdavandGitHub Enterprise 8120071348 Merge branch 'dev' into feature_json-task-db_M2 2022-10-29 09:46:50 +02:00
David Guler bfbda8d753 #36 Added images to Plant database 2022-10-29 09:44:02 +02:00
Gian-Andrea Hutter 51e8f27a20 #17 implementation objectmapper writeFile 2022-10-28 12:24:55 +02:00
Gian-Andrea Hutter bd2aa60128 Merge remote-tracking branch 'origin/feature_json-gardenplan_M2' into feature_json-task-db_M2
# Conflicts:
#	build.gradle
2022-10-28 11:52:49 +02:00
Gian-Andrea Hutter 63f0501397 Merge branch 'dev' into feature_json-task-db_M2 2022-10-25 18:15:26 +02:00
Gian-Andrea Hutter 6c75fcd0ec #17 taskdb.json changed to the right variable types, JsonTaskDatabase.java fully implemented and documented 2022-10-25 18:11:29 +02:00
Gian-Andrea Hutter 8fd57d91f2 Merge remote-tracking branch 'origin/feature_json-task-db_M2' into feature_json-task-db_M2 2022-10-25 10:11:02 +02:00
Gian-Andrea Hutter 23d87f7a85 #17 taskdb.json changed to the right variable types, ad isIntimePeriode to , JasonTaskDatabase 2022-10-25 10:10:40 +02:00
Elias Csomor af0e73c007 Prepare for tests 2022-10-24 14:20:59 +02:00
Gian-Andrea Hutter e550f5549a Merge remote-tracking branch 'origin/feature_json-gardenplan_M2' into feature_json-task-db_M2 2022-10-24 13:54:43 +02:00
Gian-Andrea Hutter 694da97cd6 #17 implementation JsonTaskDatabase.java, writeTasklistToFile 2022-10-24 13:54:28 +02:00
Gian-Andrea Hutter 0ca381f8cc Merge branch 'dev' into feature_json-task-db_M2
# Conflicts:
#	src/main/java/module-info.java
2022-10-24 12:53:09 +02:00
Gian-Andrea Hutter 904041afc0 #17 implementation JsonTaskDatabase.java, gradle import for jackson 2022-10-24 12:43:50 +02:00
Gian-Andrea Hutter e228b9019d #17 implementation JsonTaskDatabase.java, gradle import for jackson 2022-10-22 11:52:06 +02:00
Gian-Andrea Hutter a2008450e6 #17 implementation of an example task database 2022-10-21 21:18:29 +02:00
Gian-Andrea Hutter 96b5dba36c #17 first version of tasks 2022-10-20 21:59:18 +02:00
37 changed files with 1310 additions and 216 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+7
View File
@@ -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=.&gt;
</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=.&gt;
</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=.&gt;
</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=.&gt;
</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=.&gt;
</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=.&gt;
</panel_attributes><additional_attributes>10;10;50;10;50;100</additional_attributes></element></diagram>
Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

+144
View File
@@ -0,0 +1,144 @@
<diagram program="umletino" version="15.0.0"><zoom_level>10</zoom_level><element><id>UMLClass</id><coordinates><x>482</x><y>360</y><w>100</w><h>30</h></coordinates><panel_attributes>Main</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1082</x><y>380</y><w>370</w><h>100</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
PlantDatabase
--
+ getPlantList(zone: HardinessZone): List&lt;Plant&gt;
+ getPlantById(zone: HardinessZone id: long): Optional&lt;Plant&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>672</x><y>710</y><w>220</w><h>40</h></coordinates><panel_attributes>TaskListController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1132</x><y>110</y><w>300</w><h>180</h></coordinates><panel_attributes>&lt;&lt;Record&gt;&gt;
Plant
--
+ id: long
+ name: String
+ description: String
+ spacing: int
+ lifecycle: List&lt;GrowthPhase&gt;
--
+ calculateStartDate(harvestDate: Date): Date
+ generateTasks()
</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>942</x><y>710</y><w>210</w><h>120</h></coordinates><panel_attributes>TaskListModel
--
- tasks: ListProperty&lt;Task&gt;
- taskDb: TaskDatabase
--
+ getTask(id: long): Optional&lt;Task&gt;
+ saveTask(task: Task)
+ removeTask(task: Task)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>322</x><y>690</y><w>250</w><h>120</h></coordinates><panel_attributes>GardenPlanModel
--
- tasks: ListProperty&lt;Crop&gt;
- gardenPlan: GardenPlan
--
+ plantAsCrop(planting: UserPlanting)
+ removePlanting(planting: UserPlanting)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>72</x><y>680</y><w>180</w><h>80</h></coordinates><panel_attributes>GardenPlanController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1362</x><y>830</y><w>490</w><h>240</h></coordinates><panel_attributes>Task
--
+ id: long
+ name: String {readOnly}
+ description: String {readOnly}
+ startDate: Date {readOnly}
+ isOptional: boolean {readOnly}
- interval: int
- endDate: Date
--
+ Task(name: String, description: String, startDate: String, isReadOnly: boolean): Task
+ withInterval(interval: int): Task
+ withEndDate(endDate: Date): Task
+ withId(id: long): Task
--
+ getInterval(): Optional&lt;int&gt;
+ getEndDate(): Optional&lt;Date&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>902</x><y>930</y><w>280</w><h>140</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
TaskDatabase
--
+ getTaskList(start: Date, end: Date): List&lt;Task&gt;
+ saveTask(Task task) throws ??Exception
+ removeTask(Task task) throws ??Exception</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>442</x><y>440</y><w>170</w><h>80</h></coordinates><panel_attributes>MainWindowController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>962</x><y>1310</y><w>210</w><h>70</h></coordinates><panel_attributes>NotificationService
--
- taskDb: TaskDatabase
--
+ tick()</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1412</x><y>1310</y><w>240</w><h>90</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
WeatherProvider
--
+ getWeatherForecast: WeatherForecast</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1392</x><y>1150</y><w>400</w><h>100</h></coordinates><panel_attributes>WeatherService
--
- weatherPovider: WeatherProvider
- taskDb: TaskDatabase
--
+ WeatherService(provider: WeatherProvider, taskDb: TaskDatabase)
- updateTasks()</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1502</x><y>110</y><w>260</w><h>150</h></coordinates><panel_attributes>&lt;&lt;Record&gt;&gt;
GrowthPhase
--
+ startDate: MonthDay
+ endDate: MonthDay
+ type: GrowthPhaseType
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1422</x><y>180</y><w>100</w><h>40</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;&gt;
m1=*
m2=1</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>Text</id><coordinates><x>152</x><y>130</y><w>190</w><h>70</h></coordinates><panel_attributes>Note:
--
{final, readOnly} omitted on public data fields in &lt;&lt;Record&gt;&gt; Entities for clarity
style=wordwrap</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1172</x><y>970</y><w>210</w><h>50</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns &gt;</panel_attributes><additional_attributes>190;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>1032</x><y>820</y><w>140</w><h>130</h></coordinates><panel_attributes>lt=&lt;-
m1=1 external database
m2=1 internal list
accesses</panel_attributes><additional_attributes>10;110;10;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>322</x><y>1120</y><w>240</w><h>150</h></coordinates><panel_attributes>Crop
--
- cropId: Long
+ plantId: long {final, readOnly}
+ startDate: LocalDate {final, readOnly}
+ area: Double
--
+ withId(long): Crop
+ withArea(double): Crop
--
+ getCropId(): Optional&lt;Long&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1262</x><y>280</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns</panel_attributes><additional_attributes>10;10;10;100</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1632</x><y>290</y><w>130</w><h>100</h></coordinates><panel_attributes>&lt;&lt;Enumeration&gt;&gt;
GrowthPhaseType
--
SOW
PLANT
HARVEST</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1582</x><y>250</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;
m1=1
m2=*</panel_attributes><additional_attributes>50;90;10;90;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>882</x><y>720</y><w>80</w><h>30</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;</panel_attributes><additional_attributes>60;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>432</x><y>1020</y><w>110</w><h>120</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns / saves</panel_attributes><additional_attributes>10;100;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>242</x><y>710</y><w>100</w><h>30</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>302</x><y>890</y><w>280</w><h>140</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
GardenPlan
--
+ getPlantings(): List&lt;UserPlanting&gt;
+ addPlanting(plantId: long, startDate)
+ savePlanting(planting: UserPlanting)
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>422</x><y>800</y><w>80</w><h>110</h></coordinates><panel_attributes>lt=&lt;.
m1=1
m2=1
accesses</panel_attributes><additional_attributes>10;90;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1522</x><y>1240</y><w>30</w><h>90</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;70;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1062</x><y>1060</y><w>80</w><h>270</h></coordinates><panel_attributes>lt=&lt;-
m1=1
m2=1
accesses</panel_attributes><additional_attributes>10;10;10;250</additional_attributes></element><element><id>Relation</id><coordinates><x>1142</x><y>1060</y><w>270</w><h>170</h></coordinates><panel_attributes>lt=&lt;-
m1=1
m2=1
adds Tasks</panel_attributes><additional_attributes>10;10;10;140;250;140</additional_attributes></element><element><id>UMLClass</id><coordinates><x>822</x><y>1140</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonTaskDatabase</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>912</x><y>1060</y><w>30</w><h>100</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>10;10;10;80</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1562</x><y>430</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonPlantDatabase</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1442</x><y>440</y><w>140</w><h>30</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>10;10;120;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>32</x><y>930</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonGardenPlan</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>222</x><y>940</y><w>100</w><h>30</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>582</x><y>510</y><w>220</w><h>220</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>200;200;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>172</x><y>510</y><w>340</w><h>190</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;170;320;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>582</x><y>250</y><w>160</w><h>60</h></coordinates><panel_attributes>PlantListController</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>522</x><y>380</y><w>30</w><h>80</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;60;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>602</x><y>300</y><w>80</w><h>210</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>60;10;10;190</additional_attributes></element><element><id>UMLClass</id><coordinates><x>732</x><y>390</y><w>160</w><h>60</h></coordinates><panel_attributes>PlantListModel</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>732</x><y>280</y><w>90</w><h>130</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>70;110;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>882</x><y>400</y><w>220</w><h>70</h></coordinates><panel_attributes>lt=&lt;-
m1=1\nexternal\ndatabase
m2=1\ninternal\nlist
accesses &gt;</panel_attributes><additional_attributes>200;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>582</x><y>0</y><w>190</w><h>50</h></coordinates><panel_attributes>lt=-
m1=0..n
m2=0..1
teaches to &gt;</panel_attributes><additional_attributes>10;20;170;20</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1412</x><y>530</y><w>420</w><h>200</h></coordinates><panel_attributes>TaskTemplate
--
+ name: String {readOnly}
+ description: String {readOnly}
+ relativeStartDate: int {readOnly}
- interval: Integer
- relativeEndDate: Integer
- isOptional: boolean
--
+ Task(name: String, description: String, startDate: String): TaskTemplate
+ setRelativeEndDate(relativeEndDate: int)
+ setInterval(interval: Integer)
+ generateTask(realStartDate: LocalDate): Task
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1752</x><y>180</y><w>130</w><h>490</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;&gt;
m1=*
m2=1</panel_attributes><additional_attributes>80;460;110;460;110;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1602</x><y>720</y><w>90</w><h>130</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
generates</panel_attributes><additional_attributes>10;110;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>562</x><y>770</y><w>400</w><h>40</h></coordinates><panel_attributes>lt=&lt;-
delegates generating Tasks</panel_attributes><additional_attributes>380;20;10;20</additional_attributes></element></diagram>
@@ -0,0 +1,64 @@
package ch.zhaw.gartenverwaltung;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
public class CropDetailController {
@FXML
private Button area_button;
@FXML
private Label area_label;
@FXML
private Label cropName_label;
@FXML
private Label description_label;
@FXML
private VBox growthPahses_vbox;
@FXML
private Label lacation_label;
@FXML
private Label light_label;
@FXML
private Button location_button;
@FXML
private VBox pests_vbox;
@FXML
private Label soil_label;
@FXML
private Label spacing_label;
@FXML
void editTaskList(ActionEvent event) {
}
@FXML
void goBack(ActionEvent event) {
}
@FXML
void setArea(ActionEvent event) {
}
@FXML
void setLocation(ActionEvent event) {
}
}
@@ -10,10 +10,16 @@ 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;
public class MainFXMLController implements Initializable {
/**
* Caching the panes
*/
private final Map<String, AnchorPane> panes = new HashMap<>();
@FXML
private Button home_button;
@@ -62,15 +68,22 @@ public class MainFXMLController implements Initializable {
* @throws IOException exception when file does not exist
*/
public void loadPane(String fxmlFile) throws IOException {
//ToDo HGrow and VGrow of new node
Node node;
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
node = (Node)loader.load();
if(fxmlFile.equals("MyPlants.fxml")) {
MyPlantsController myPlantsController = loader.getController();
myPlantsController.getMainController(this);
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(node);
mainPane.getChildren().setAll(anchorPane);
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
}
private void styleChangeButton(Button button) {
@@ -1,24 +1,27 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Crop;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
public class MyPlantsController implements Initializable {
MainFXMLController mainController;
@FXML
private Button addPlant_button;
@FXML
private VBox myPlants_vbox;
@@ -30,14 +33,14 @@ public class MyPlantsController implements Initializable {
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
//ToDo
List<Plant> myPlants = getMyPlants();
createPlantView(myPlants);
List<Crop> cropList = getCropList();
createPlantView(cropList);
}
private void createPlantView(List<Plant> myPlants) {
//ToDo
for(Plant plant : myPlants) {
createPlantView();
private void createPlantView(List<Crop> crops) {
for(Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
@@ -45,14 +48,60 @@ public class MyPlantsController implements Initializable {
mainController = controller;
}
private List<Plant> getMyPlants() {
//ToDo method to get myPlantList(scheduled)
//Method to call all Plants saved
List<Plant> myPlantList = new LinkedList<>();
return myPlantList;
private List<Crop> getCropList() {
//ToDo method to get cropList
//Method to get Crop List
List<Crop> cropList = new LinkedList<>();
return cropList;
}
private void createPlantView() {
//ToDo FXML Panel with Plant data
private HBox createPlantView(Crop crop) {
//ToDo add dynamic design
HBox hBox = new HBox();
Label label = new Label("Placeholder");
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
//ToDo uncomment when new FXML exists
try {
mainController.loadPane("CropDetail.fxml");
} catch (IOException e) {
e.printStackTrace();
}
}
};
return event;
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
showConfirmation(crop);
}
};
return event;
}
private void showConfirmation(Crop crop) {
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) {
//ToDo Method to remove crop
}
}
}
@@ -1,14 +1,19 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.Plant;
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.net.URL;
import java.time.LocalDate;
@@ -17,7 +22,9 @@ import java.util.List;
import java.util.ResourceBundle;
public class MyScheduleController implements Initializable {
private Plant selectedPlant = null;
private Crop selectedCrop = null;
private final ListProperty<Crop> cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML
private Label day1_label;
@@ -65,28 +72,26 @@ public class MyScheduleController implements Initializable {
private Label information_label;
@FXML
private ListView<Plant> scheduledPlants_listview;
private ListView<Crop> scheduledPlants_listview;
@Override
public void initialize(URL location, ResourceBundle resources) {
List<Plant> plantList = new LinkedList<>();
fillListViewMyPlantsInSchedule(plantList);
getSelectedPlantTask();
//ToDo method to load crop list
List<Crop> cropList = new LinkedList<>();
setCellFactoryListView();
scheduledPlants_listview.itemsProperty().bind(cropListProperty);
lookForSelectedListEntries();
setDayLabels();
information_label.setText("");
loadTaskList();
}
private void getSelectedPlantTask() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() {
private void lookForSelectedListEntries() {
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Crop>() {
@Override
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) {
if(newValue != null) {
selectedPlant = newValue;
//ToDo update day<x>_panel with task for the day
} else {
selectedPlant = null;
//ToDo update day<x>_panel with task for the day (all plants)
}
public void changed(ObservableValue<? extends Crop> observable, Crop oldValue, Crop newValue) {
selectedCrop = newValue;
loadTaskList();
}
});
}
@@ -102,23 +107,49 @@ public class MyScheduleController implements Initializable {
day7_label.setText(today.plusDays(6).getDayOfWeek().toString());
}
private void fillListViewMyPlantsInSchedule(List<Plant> list) {
for (Plant plant : list) {
scheduledPlants_listview.getItems().add(plant);
}
scheduledPlants_listview.setCellFactory(param -> new ListCell<Plant>() {
private void setCellFactoryListView() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<Crop>() {
@Override
protected void updateItem(Plant plant, boolean empty) {
super.updateItem(plant, empty);
protected void updateItem(Crop crop, boolean empty) {
super.updateItem(crop, empty);
if (empty || plant == null || plant.name() == null) {
if (empty || crop == null) {
setText(null);
} else {
setText(plant.name());
setText("placeholder");
}
}
});
}
private void loadTaskList() {
//ToDo load task list according to selectedCrop
List<List<Task>> taskLists = new LinkedList<>();
if (selectedCrop != null) {
} else {
}
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("placeholder");
vBox.getChildren().add(label);
}
pane.getChildren().add(vBox);
}
}
@@ -4,19 +4,26 @@ 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.application.Platform;
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.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Bounds;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.InputMethodEvent;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
@@ -25,8 +32,12 @@ public class PlantsController implements Initializable {
private Plant selectedPlant = null;
private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;
// TODO: move to model
private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@FXML
private CheckBox autum_filter;
private VBox seasons;
@FXML
private VBox climate_zones;
@@ -46,79 +57,45 @@ public class PlantsController implements Initializable {
@FXML
private TextField search_plants;
@FXML
private CheckBox sommer_filter;
@FXML
private CheckBox spring_filter;
@FXML
private CheckBox winter_filter;
@FXML
void filterAutum(ActionEvent event) {
//ToDo
}
@FXML
void filterSommer(ActionEvent event) {
//ToDo
}
@FXML
void filterSpring(ActionEvent event) {
//ToDo
}
@FXML
void filterWinter(ActionEvent event) {
//ToDo
}
/**
* saves the current selected plant in new JSON database
* @param event event
*/
@FXML
void saveToMyPlant(ActionEvent event) {
//ToDo model save selectedPlant to mySelectedPlant(IO)
}
@FXML
void searchForPlant(InputMethodEvent event) {
viewFilteredListBySearch(search_plants.getText());
}
/**
* fill list view with current hardiness zone
* set default values
* create filter of season and hardiness zone
* create event listener for selected list entry and search by query
* {@inheritDoc}
*/
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
List<Plant> plantList = new LinkedList<>();
try {
plantList = plantListModel.getPlantList(DEFAULT_HARDINESS_ZONE);
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
fillListViewWithData(plantList);
setListCellFactory();
fillPlantListWithHardinessZone();
list_plants.itemsProperty().bind(plantListProperty);
description_plant.setText("");
saveToMyPlant_button.setDisable(true);
createFilterSeasons();
createFilterHardinessZone();
lookForSelectedListEntry();
try {
viewFilteredListBySearch();
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
/**
* update the ListView according to the plant list provided
* Entry in ListView is plant name
* @param list plantList which fill the ListView
* set text of list view to plant name
*/
private void fillListViewWithData(List<Plant> list) {
clearListView();
for (Plant plant : list) {
list_plants.getItems().add(plant);
}
private void setListCellFactory() {
list_plants.setCellFactory(param -> new ListCell<Plant>() {
@Override
protected void updateItem(Plant plant, boolean empty) {
@@ -133,44 +110,139 @@ public class PlantsController implements Initializable {
});
}
private void viewFilteredListByFilters() {
boolean springValue = spring_filter.isSelected();
boolean sommerValue = sommer_filter.isSelected();
boolean autumValue = autum_filter.isSelected();
boolean winterValue = winter_filter.isSelected();
//ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, <predicate>))
//List<Plant> plantList = new LinkedList<>();
//fillListViewWithData(plantList);
}
private void viewFilteredListBySearch(String query) {
//ToDo getFilteredPlantList with (plantListModel.getFilteredPlantList(DEFAULT_HARDINESS_ZONE, <predicate>))
//List<Plant> plantList = new LinkedList<>();
//fillListViewWithData(plantList);
}
private void createFilterHardinessZone() {
//ToDo create radioList of hardinessZone in VBox climate_zones
/**
* get plant list according to param season and hardiness zone
* fill list view with plant list
* @param season enum of seasons
* @throws HardinessZoneNotSetException throws exception
* @throws IOException throws exception
*/
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
clearListView();
plantListProperty.addAll(plantListModel.getFilteredPlantListBySaisonWithoutGrowthPhase(plantListModel.getCurrentZone(), season.getStartDate(), season.getEndDate()));
}
/**
* observes changes in the selectedProperty of ListView and updates the description label
* get plant list filtered by search plant entry and hardiness zone
* fill list view with plant list
* @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
* @throws IOException throws exception
*/
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.isEmpty()) {
fillPlantListWithHardinessZone();
}else {
try {
List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
clearListView();
plantListProperty.addAll(filteredPlants);
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
});
}
/**
* 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 | IOException e) {
e.printStackTrace();
}
}
/**
* 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(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean 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.name());
radioButton.setToggleGroup(seasonGroup);
radioButton.setPadding(new Insets(0,0,10,0));
if (season.equals(Seasons.AllSEASONS)) {
radioButton.setSelected(true);
}
radioButton.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (season.equals(Seasons.AllSEASONS)) {
fillPlantListWithHardinessZone();
} else {
try {
viewFilteredListBySeason(season);
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
}
}
}
});
seasons.getChildren().add(radioButton);
}
}
/**
* observes changes in the selectedProperty of ListView and updates:
* the description label
* image of the plant
*/
private void lookForSelectedListEntry() {
list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Plant>() {
@Override
public void changed(ObservableValue<? extends Plant> observable, Plant oldValue, Plant newValue) {
//ToDo
if(newValue != null) {
selectedPlant = newValue;
description_plant.setText(selectedPlant.description());
saveToMyPlant_button.setDisable(false);
//update img plant
Image img;
if(selectedPlant.image() != null) {
img = selectedPlant.image();
} else {
img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
}
img_plant.setImage(img);
} else {
selectedPlant = null;
description_plant.setText("");
saveToMyPlant_button.setDisable(true);
//update img when null placeholder PNG
Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
img_plant.setImage(img);
}
}
});
@@ -181,6 +253,6 @@ public class PlantsController implements Initializable {
* clears the ListView of entries
*/
private void clearListView() {
list_plants.getItems().clear();
plantListProperty.clear();
}
}
@@ -0,0 +1,8 @@
package ch.zhaw.gartenverwaltung.gardenplan;
public class Gardenplanmodel {
// private JasonGardenplan gardenplan;
// liste von crops
//task generieren
//plant holen, task template mit tasktemplateklasse tasks erstellen
}
@@ -20,7 +20,7 @@ import java.util.Map;
import java.util.Optional;
public class JsonGardenPlan implements GardenPlan {
private final URL dataSource = getClass().getResource("user-crops.json");
private final URL dataSource;
private IdProvider idProvider;
private Map<Long, Crop> cropMap = Collections.emptyMap();
@@ -40,7 +40,21 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#getCrops()
* Default constructor
*/
public JsonGardenPlan() {
this.dataSource = getClass().getResource("user-crops.json");
}
/**
* Constructor to use a specified {@link URL} as a {@link #dataSource}
* @param dataSource A {@link URL} to the file to be used as a data source
*/
public JsonGardenPlan(URL dataSource) {
this.dataSource = dataSource;
}
/**
* {@inheritDoc}
*/
@Override
public List<Crop> getCrops() throws IOException {
@@ -51,7 +65,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#getCropById(long)
* {@inheritDoc}
*/
@Override
public Optional<Crop> getCropById(long id) throws IOException {
@@ -62,7 +76,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#saveCrop(Crop)
* {@inheritDoc}
*
* Saves a crop to the database.
* If no {@link Crop#cropId} is set, one will be generated and
@@ -79,7 +93,7 @@ public class JsonGardenPlan implements GardenPlan {
}
/**
* @see GardenPlan#removeCrop(Crop)
* {@inheritDoc}
*/
@Override
public void removeCrop(Crop crop) throws IOException {
@@ -1,10 +1,13 @@
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;
@@ -31,10 +34,14 @@ public class JsonPlantDatabase implements PlantDatabase {
* 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());
}
/**
@@ -75,21 +82,23 @@ public class JsonPlantDatabase implements PlantDatabase {
}
if (dataSource != null) {
currentZone = zone;
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule);
ObjectMapper mapper = new ObjectMapper()
.registerModule(timeModule)
.registerModule(imageModule);
List<Plant> result;
result = mapper.readerForListOf(Plant.class).readValue(dataSource);
for (Plant plant : result) {
plant.inZone(currentZone);
}
// Turn list into a HashMap with structure id => Plant
plantMap = result.stream()
// Remove plants not in the current zone
.filter(plant -> {
plant.inZone(currentZone);
return !plant.lifecycle().isEmpty();
})
// Create Hashmap from results
.collect(HashMap::new,
(res, plant) -> res.put(plant.id(), plant),
(existing, replacement) -> { });
(existing, replacement) -> {});
}
}
}
@@ -0,0 +1,126 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Task;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import java.io.File;
import java.io.IOException;
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 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);
timeModule.addDeserializer(LocalDate.class, dateDeserializer);
}
/**
* If no data is currently loaded, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #taskMap} are returned.
*
* @see TaskDatabase#getTaskList(LocalDate, LocalDate)
*/
@Override
public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{
if(taskMap.isEmpty()) {
loadTaskListFromFile();
}
return taskMap.values().stream().filter(task -> task.isInTimePeriode(start, end)).toList();
}
/**
* If no data is currently loaded, data is loaded from {@link #dataSource}.
* If the {@link Task} has an id than the task is added to the {@link #taskMap}
* otherwise the id is generated with the {@link IdProvider} before adding
* it to the {@link #taskMap}. In any case, the {@link #taskMap} is written
* to the {@link #dataSource}.
*
* @see TaskDatabase#saveTask(Task)
*/
@Override
public void saveTask(Task task) throws IOException {
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 {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule)
.registerModule(new Jdk8Module());
if(dataSource != null) {
mapper.writeValue(new File(dataSource.getFile()), taskMap);
}
}
/**
* Loads the database from {@link #dataSource} and updates the cached data.
*
* @throws IOException If the database cannot be accessed
*/
private void loadTaskListFromFile() throws IOException {
if (dataSource != null) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(timeModule);
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) -> {});
}
}
}
@@ -1,13 +1,42 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
/**
* A database of {@link Task}s.
* The interface specifies the minimal required operations.
*/
public interface TaskDatabase {
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;
/**
* Saves the {@link Task} in the Cache.
*
* @param task The {@link Task} which is wanted to be saved in the TaskList
* @throws IOException If the database cannot be accessed
*/
void saveTask(Task task) throws IOException;
/**
* Removes the {@link Task}from the Cache.
*
* @param task The {@link Task} which has to be removed from the {@link Task} list.
* @throws IOException If the database cannot be accessed
*/
void removeTask(Task task) throws IOException;
}
@@ -17,11 +17,12 @@ public class GrowthPhaseTypeDeserializer extends StdDeserializer<GrowthPhaseType
@Override
public GrowthPhaseType deserialize(JsonParser parser, DeserializationContext context) throws IOException {
GrowthPhaseType result = null;
String token = parser.getText();
try {
result = GrowthPhaseType.valueOf(parser.getText().toUpperCase());
result = GrowthPhaseType.valueOf(token.toUpperCase());
} catch (IllegalArgumentException e) {
// TODO: Log
System.err.println("bad growth phase type");
System.err.printf("Bad growth phase type \"%s\"\n", token);
}
return result;
}
@@ -18,11 +18,12 @@ public class HardinessZoneDeserializer extends StdDeserializer<HardinessZone> {
@Override
public HardinessZone deserialize(JsonParser parser, DeserializationContext context) throws IOException {
HardinessZone result = null;
String token = parser.getText();
try {
result = HardinessZone.valueOf(parser.getText().toUpperCase());
result = HardinessZone.valueOf(token.toUpperCase());
} catch (IllegalArgumentException e) {
// TODO: Log
System.err.println("bad growth phase type");
System.err.printf("Unknown Hardiness Zone \"%s\"\n", token);
}
return result;
}
@@ -0,0 +1,33 @@
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) {
// TODO: Log
e.printStackTrace();
System.err.printf("Cannot find Image \"%s\"\n", imageUrl.getFile());
}
}
return result;
}
}
@@ -33,12 +33,12 @@ public class PlantListModel {
setDefaultZone();
}
public PlantListModel(PlantDatabase plantDatabase){
public PlantListModel(PlantDatabase plantDatabase) {
this.plantDatabase = plantDatabase;
setDefaultZone();
}
private void setDefaultZone(){
private void setDefaultZone() {
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config
}
@@ -52,6 +52,7 @@ public class PlantListModel {
/**
* Method to get actual Plant List in alphabetic Order
*
* @return actual Plant List in alphabetic Order
*/
public List<Plant> getPlantList(HardinessZone zone) throws HardinessZoneNotSetException, IOException {
@@ -62,10 +63,11 @@ public class PlantListModel {
/**
* Method to get the actual Plant list in custom Order
* @param zone selected hardiness zone
*
* @param zone selected hardiness zone
* @param comparator comparator to sort the list
* @return sorted list with plants in the given hardiness zone
* @throws IOException If the database cannot be accessed
* @throws IOException If the database cannot be accessed
* @throws HardinessZoneNotSetException If no {@link HardinessZone} was specified
*/
public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException {
@@ -75,10 +77,11 @@ public class PlantListModel {
/**
* Method to get Filtered plant list
*
* @param predicate predicate to filter the list
* @param zone selected hardiness zone
* @param zone selected hardiness zone
* @return filterd list with plants in the hardinness zone
* @throws IOException If the database cannot be accessed
* @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 {
@@ -88,10 +91,11 @@ public class PlantListModel {
/**
* Method to get Filtered plant list by id by exact match
*
* @param zone selected hardiness zone
* @param id id of plant
* @param id id of plant
* @return if id doesn't exist: empty List, else list with 1 plant entry.
* @throws IOException If the database cannot be accessed
* @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 {
@@ -102,54 +106,68 @@ public class PlantListModel {
}
/**
*
* @param zone selected hardiness zone
* @param zone selected hardiness zone
* @param searchString the string to search plant List, set '#' as first char the search by id.
* @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
* @throws IOException If the database cannot be accessed
*/
public List<Plant> getFilteredPlantListByString(HardinessZone zone, String searchString) throws HardinessZoneNotSetException, IOException {
if(searchString.length() == 0){
if (searchString.length() == 0) {
return getPlantList(zone);
} else if(searchString.charAt(0) == '#') {
} else if (searchString.charAt(0) == '#') {
try {
return getFilteredPlantListById(zone, Long.parseLong(searchString.substring(1)));
} catch (NumberFormatException e) {
return new ArrayList<>();
}
} else {
return getFilteredPlantList(zone, plant -> plant.name().contains(searchString) || plant.description().contains(searchString));
String caseInsensitiveSearchString = searchString.toLowerCase();
return getFilteredPlantList(zone, plant ->
plant.name().toLowerCase().contains(caseInsensitiveSearchString) ||
plant.description().toLowerCase().contains(caseInsensitiveSearchString)
);
}
}
/**
*
* @param type GrowPhaseType to filter
* @param zone selected hardiness zone
* @param from the earliest date to for the filter
* @param to the lastest date 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
* @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
* @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
* @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
@@ -159,7 +177,7 @@ public class PlantListModel {
* @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);
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)));
}
}
@@ -1,6 +1,7 @@
package ch.zhaw.gartenverwaltung.types;
import java.time.LocalDate;
import java.util.Objects;
import java.util.Optional;
public class Crop {
@@ -41,6 +42,24 @@ public class Crop {
public LocalDate getStartDate() { return startDate; }
public double getArea() { return area; }
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other instanceof Crop otherCrop) {
return Objects.equals(this.cropId, otherCrop.cropId) &&
plantId == otherCrop.plantId &&
startDate != null && startDate.equals(otherCrop.startDate) &&
area == otherCrop.area;
}
return false;
}
@Override
public int hashCode() {
int startCode = startDate != null ? startDate.hashCode() : 0;
return (int) plantId ^ (startCode << 16);
}
@Override
public String toString() {
return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]",
@@ -1,5 +1,7 @@
package ch.zhaw.gartenverwaltung.types;
import javafx.scene.image.Image;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
@@ -10,6 +12,7 @@ public record Plant(
long id,
String name,
String description,
Image image,
String spacing,
int light,
String soil,
@@ -0,0 +1,26 @@
package ch.zhaw.gartenverwaltung.types;
import java.time.MonthDay;
public enum Seasons {
AllSEASONS("--01-01", "--12-31"),
SPRING("--03-01", "--05-30"),
SOMMER("--06-01", "--08-30"),
AUTUM("--09-01", "--11-30"),
WINTER("--12-01", "--02-28");
public final String startDate;
public final String endDate;
Seasons(String startDate, String endDate) {
this.startDate = startDate;
this.endDate = endDate;
}
public MonthDay getStartDate() {
return MonthDay.parse(this.startDate);
}
public MonthDay getEndDate() {
return MonthDay.parse(this.endDate);
}
}
@@ -16,11 +16,22 @@ public class Task {
private Integer interval;
private LocalDate endDate;
/**
* default constructor
* (used by Json deserializer)
*/
public Task(){
name= "";
description= "";
startDate = LocalDate.now();
}
public Task(String name, String description, LocalDate startDate) {
this.name = name;
this.description = description;
this.startDate = startDate;
}
public Task(String name, String description, LocalDate startDate, LocalDate endDate, int interval) {
this.name = name;
this.description = description;
@@ -43,6 +54,13 @@ public class Task {
return this;
}
public boolean isInTimePeriode(LocalDate searchStartDate, LocalDate searchEndDate){
if(startDate.isAfter(searchStartDate) &&startDate.isBefore(searchEndDate)){
return true;
}
return false;
}
// Getters
public long getId() { return id; }
public String getName() { return name; }
@@ -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 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="growthPahses_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="lacation_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>
@@ -3,7 +3,6 @@
<?import javafx.geometry.Insets?>
<?import javafx.geometry.Rectangle2D?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.SplitPane?>
@@ -16,8 +15,9 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0" prefWidth="1219.0"
xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
<AnchorPane maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="1000.0" prefHeight="853.0"
prefWidth="1219.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="ch.zhaw.gartenverwaltung.PlantsController">
<children>
<SplitPane dividerPositions="0.7377363661277062" layoutX="539.0" layoutY="266.0" prefHeight="853.0" prefWidth="1219.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
@@ -30,7 +30,7 @@
<Font name="System Bold" size="30.0" />
</font>
</Label>
<TextField fx:id="search_plants" onInputMethodTextChanged="#searchForPlant" promptText="Search for Plant Name" />
<TextField fx:id="search_plants" promptText="Search for Plant Name" />
<HBox alignment="CENTER_LEFT" prefHeight="480.0" prefWidth="881.0" VBox.vgrow="ALWAYS">
<children>
<ListView fx:id="list_plants" maxWidth="1.7976931348623157E308" prefHeight="497.0" prefWidth="580.0" HBox.hgrow="ALWAYS" />
@@ -75,30 +75,7 @@
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<VBox layoutX="37.0" layoutY="-19.0" prefHeight="180.66666666666666" prefWidth="310.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<CheckBox fx:id="spring_filter" mnemonicParsing="false" onAction="#filterSpring" text="Spring">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
<CheckBox fx:id="sommer_filter" mnemonicParsing="false" onAction="#filterSommer" text="Sommer">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
<CheckBox fx:id="autum_filter" mnemonicParsing="false" onAction="#filterAutum" text="Autum">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
<CheckBox fx:id="winter_filter" mnemonicParsing="false" onAction="#filterWinter" text="Winter">
<padding>
<Insets top="10.0" />
</padding>
</CheckBox>
</children>
</VBox>
<VBox fx:id="seasons" layoutX="37.0" layoutY="-19.0" prefHeight="180.66666666666666" prefWidth="310.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</content>
@@ -0,0 +1,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

@@ -6,6 +6,7 @@
"light": 6,
"spacing": "35",
"soil": "sandy",
"image": "potato.jpg",
"pests": [
{
"name": "Rot",
@@ -30,7 +31,7 @@
"name": "Germinate",
"relativeStartDate": -14,
"relativeEndDate": null,
"description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"",
"description": "Take an egg carton and fill it with soil. Put the seedling deep enough so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.",
"interval": null,
"isOptional": false
}
@@ -52,7 +53,7 @@
"name": "hilling",
"relativeStartDate": 0,
"relativeEndDate": null,
"description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"",
"description": "When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.",
"interval": 21,
"isOptional": false
}
@@ -86,6 +87,7 @@
"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",
@@ -167,6 +169,7 @@
"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",
@@ -0,0 +1,50 @@
[
{
"id" : 1,
"name" : "sow plant",
"description": "Plant the seeds, crops in de bed.",
"startDate" : "2022-05-01",
"endDate" : "2022-05-01",
"interval" : 0
},
{
"id" : 2,
"name" : "water plant",
"description": "water the plant, so that the soil is wet around the plant.",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 2
},
{
"id" : 3,
"name" : "fertilize plant",
"description": "The fertilizer has to be mixed with water. Then fertilize the plants soil with the mixture",
"startDate" : "2022-06-01",
"endDate" : "2022-08-01",
"interval" : 28
},
{
"id" : 4,
"name" : "covering plant",
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
"startDate" : "2022-07-01",
"endDate" : "2022-07-01",
"interval" : 0
},
{
"id" : 5,
"name" : "look after plant",
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
"startDate" : "2022-05-01",
"endDate" : "2022-09-01",
"interval" : 5
},
{
"id" : 6,
"name" : "harvest plant",
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
"startDate" : "2022-09-01",
"endDate" : "2022-09-01",
"interval" : 0
}
]
@@ -0,0 +1,105 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Crop;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JsonGardenPlanTest {
private GardenPlan testDatabase;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* Files to isolate the test-units
*/
private final URL dbDataSource = this.getClass().getResource("user-crops.json");
private final URL testFile = this.getClass().getResource("test-user-crops.json");
@BeforeEach
void connectToDb() throws URISyntaxException, IOException {
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonGardenPlan(dbDataSource);
}
@Test
@DisplayName("Check if results are retrieved completely.")
void getCropsNotEmpty() {
List<Crop> testList;
try {
testList = testDatabase.getCrops();
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(3, testList.size());
List<Long> plantIds = testList.stream().map(Crop::getPlantId).collect(Collectors.toList());
List<Long> expected = Arrays.asList(1L, 1L, 0L);
Assertions.assertEquals(expected, plantIds);
}
@Test
@DisplayName("Check whether single access works.")
void getCropById() throws IOException {
Optional<Crop> testCrop = testDatabase.getCropById(1);
assertTrue(testCrop.isPresent());
Assertions.assertEquals(1, testCrop.get().getPlantId());
}
@Test
@DisplayName("Check for a nonexisting crop.")
void getCropByIdMustFail() throws IOException {
Optional<Crop> testCrop = testDatabase.getCropById(99);
Assertions.assertFalse(testCrop.isPresent());
}
@Test
@DisplayName("Add new Crop.")
void addNewCrop() {
Crop crop = new Crop(3L, LocalDate.parse("2023-02-22", formatter));
try {
testDatabase.saveCrop(crop);
assertTrue(crop.getCropId().isPresent());
Optional<Crop> testCrop = testDatabase.getCropById(crop.getCropId().get());
assertTrue(testCrop.isPresent());
Assertions.assertEquals(crop, testCrop.get());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
@DisplayName("Remove crop")
void removeCrop(){
try {
Optional<Crop> crop = testDatabase.getCropById(2L);
Assertions.assertTrue(crop.isPresent());
testDatabase.removeCrop(crop.get());
crop = testDatabase.getCropById(2L);
Assertions.assertFalse(crop.isPresent());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -8,7 +8,6 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@@ -16,7 +15,6 @@ import java.util.stream.Collectors;
public class JsonPlantDatabaseTest {
PlantDatabase testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach
void connectToDb() {
@@ -26,13 +24,11 @@ public class JsonPlantDatabaseTest {
@Test
@DisplayName("Check if results are retrieved completely")
void getPlantList() {
void getPlantListNotEmpty() {
List<Plant> testList;
try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_8A);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HardinessZoneNotSetException e) {
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(3, testList.size());
@@ -42,32 +38,51 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals(expected,names);
}
@Test
@DisplayName("Check if results are retrieved correctly when empty")
void getPlantListEmpty() {
List<Plant> testList;
try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_1A);
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(0, testList.size());
}
@Test
@DisplayName("Check whether single access works.")
void getPlantById() {
void getPlantByIdAndZone() {
Optional<Plant> testPlant;
try {
testDatabase.getPlantList(HardinessZone.ZONE_8A);
testPlant = testDatabase.getPlantById(1);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HardinessZoneNotSetException e) {
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 {
testDatabase.getPlantList(HardinessZone.ZONE_8A);
testPlant = testDatabase.getPlantById(99);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (HardinessZoneNotSetException e) {
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 99);
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
Assertions.assertFalse(testPlant.isPresent());
@@ -0,0 +1,37 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.*;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class JsonTaskDatabaseTest {
TaskDatabase testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach
void connectToDb() {
// testDatabase = new JsonTaskDatabase();
}
@Test
@DisplayName("Check if results are retrieved completely")
void getTasks(){
/*
List<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);
*/
}
}
@@ -43,6 +43,7 @@ class PlantListModelTest {
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",
@@ -57,6 +58,7 @@ class PlantListModelTest {
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",
@@ -68,6 +70,7 @@ class PlantListModelTest {
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",
@@ -0,0 +1,50 @@
[
{
"id" : "1",
"name" : "sow plant",
"description": "Plant the seeds/ crops in de bed.",
"startDate" : "01.05.2022",
"endDate" : "01.05.2022",
"interval" : "null"
},
{
"id" : "2",
"name" : "water plant",
"description": "water the plant, so that the soil is wet around the plant.",
"startDate" : "01.05.2022",
"endDate" : "01.09.2022",
"interval" : "2"
},
{
"id" : "3",
"name" : "fertilize plant",
"description": "The fertilizer has to be mixed with water. Then fertilize the plant's soil with the mixture",
"startDate" : "01.06.2022",
"endDate" : "01.08.2022",
"interval" : "28"
},
{
"id" : "4",
"name" : "covering plant",
"description": "Take a big enough coverage for the plants. Cover the whole plant with a bit space between the plant and the coverage",
"startDate" : "15.07.2022",
"endDate" : "15.07.2022",
"interval" : "null"
},
{
"id" : "5",
"name" : "look after plant",
"description": "Look for pest or illness at the leaves of the plant. Check the soil around the plant, if the roots are enough covered with soil",
"startDate" : "01.05.2022",
"endDate" : "01.09.2022",
"interval" : "5"
},
{
"id" : "6",
"name" : "harvest plant",
"description": "Pull the ripe vegetables out from the soil. Clean them with clear, fresh water. ",
"startDate" : "01.09.2022",
"endDate" : "01.09.2022",
"interval" : "null"
}
]
@@ -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 @@
[
]