Compare commits

..
Author SHA1 Message Date
David Guler 05e7bcc2e8 fix: Made GardenPlanModelTest independent from json content.
isolated GardenPlanModelTests by pre-copying files. Tests pass now.
2022-11-15 11:28:48 +01:00
David Guler 09e582b8a2 refactor: converted SelectSowDay popup to proper JFX Dialog 2022-11-15 11:03:36 +01:00
David Guler 2b7cec7e6a refactor: removed MainFXMLController from dependencies
Replaced MainFXMLController-based scene-changing with event-based scene-changing to remove cyclic dependency
2022-11-15 08:40:42 +01:00
David Guler 5ef3f6c587 refactor: annotation-based dependency-injection 2022-11-14 21:15:27 +01:00
David Guler 15279838b7 refactor: first attempt at dependency injection
also some more renaming and improving date-picker dialog
2022-11-14 20:00:01 +01:00
David Guler 4f80a0a3e0 refactor: renamed everything 2022-11-14 13:47:22 +01:00
Roman SchenkandGitHub Enterprise a5e1acc7c3 Merge pull request #59 from schrom01/test_complete_M2
Task Test Copied over from dev
2022-11-14 12:13:15 +01:00
Roman SchenkandGitHub Enterprise a8efa8fc2b Merge branch 'dev' into test_complete_M2 2022-11-14 12:09:37 +01:00
Elias Csomor 6737e67cda Task Test Copied over from dev 2022-11-13 16:30:53 +01:00
Elias Csomor a7fa58344c Continued testing for TaskDatabase
Moved from branch and extended
2022-11-12 16:45:18 +01:00
Elias Csomor 6523e1d791 added jacoco 2022-11-12 16:13:02 +01:00
gulerdavandGitHub Enterprise c653005652 Merge pull request #58 from schrom01/dev
Merge for Hand-in M2
2022-11-11 12:40:03 +01:00
Roman SchenkandGitHub Enterprise ceb448c1ff Update issue templates 2022-10-20 20:05:31 +02:00
Roman SchenkandGitHub Enterprise 8c651aec2b Update issue templates 2022-10-20 20:02:18 +02:00
gulerdavandGitHub Enterprise 56fac84e12 Merge pull request #29 from dev
Documents from teams and project setup
2022-10-18 15:24:11 +02:00
Roman SchenkandGitHub Enterprise 8b3a0c1570 Update issue templates 2022-10-18 11:19:24 +02:00
Roman SchenkandGitHub Enterprise 516a185546 Update issue templates 2022-10-18 11:18:36 +02:00
42 changed files with 989 additions and 653 deletions
+27
View File
@@ -0,0 +1,27 @@
---
name: Bug report
about: Create a report to document bugs.
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Class / Methods**
List of classes or methods which contains the bug. (If known)
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Features to implement
title: "[FEATURE]"
labels: feature
assignees: ''
---
**Description of Usecase**
A clear and concise description of what the feature allows to do.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**List of Classes and Methods to implement**
List all Classes and Methods which have to be implemented.
**Additional context**
Add any other context or screenshots about the feature request here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Tests
about: Tests to implement
title: "[TEST]"
labels: Tests
assignees: ''
---
**Feature description**
A clear and concise description of the functionality which have to be tested.
**Test cases**
A list of cases which have to be tested.
**List of affected Classes and Methods**
A clear and concise list of the classes and methods which have to be tested.
**Additional context**
Add any other context or screenshots about the feature request here.
+11 -1
View File
@@ -3,6 +3,7 @@ plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.13'
id 'org.beryx.jlink' version '2.25.0'
id 'jacoco'
}
group 'ch.zhaw.gartenverwaltung'
@@ -45,6 +46,15 @@ dependencies {
test {
useJUnitPlatform()
finalizedBy jacocoTestReport
}
jacocoTestReport {
dependsOn test
}
jacoco {
toolVersion = "0.8.8"
}
jlink {
@@ -57,4 +67,4 @@ jlink {
jlinkZip {
group = 'distribution'
}
}
@@ -1,19 +0,0 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
public class Config {
private static HardinessZone currentHardinessZone;
static {
currentHardinessZone = HardinessZone.ZONE_8A;
}
public static HardinessZone getCurrentHardinessZone() {
return currentHardinessZone;
}
public static void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
Config.currentHardinessZone = currentHardinessZone;
}
}
@@ -1,14 +1,15 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Pest;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
@@ -20,12 +21,20 @@ import javafx.stage.Stage;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class CropDetailController {
private Crop crop = null;
private final PlantListModel plantListModel = new PlantListModel();
private final TaskListModel taskListModel = new TaskListModel();
private final Gardenplanmodel gardenplanmodel = new Gardenplanmodel(taskListModel);
private Crop crop;
@Inject
private PlantList plantList;
@Inject
private GardenSchedule gardenSchedule;
@Inject
private Garden garden;
private static final Logger LOG = Logger.getLogger(CropDetailController.class.getName());
@FXML
private ImageView imageView;
@@ -63,33 +72,32 @@ public class CropDetailController {
@FXML
private Label spacing_label;
public CropDetailController() throws IOException {
}
@FXML
void editTaskList(ActionEvent event) {
void editTaskList() {
}
@FXML
void goBack(ActionEvent event) {
void goBack() {
Stage stage = (Stage) imageView.getScene().getWindow();
stage.close();
}
@FXML
void setArea(ActionEvent event) {
void setArea() {
}
@FXML
void setLocation(ActionEvent event) {
void setLocation() {
}
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException {
public void setPlantFromCrop(Crop crop) throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
this.crop = crop;
Plant plant = plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0);
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
.orElseThrow(PlantNotFoundException::new);
cropName_label.setText(plant.name());
description_label.setText(plant.description());
light_label.setText(String.valueOf(plant.light()));
@@ -104,12 +112,20 @@ public class CropDetailController {
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 createTaskLists(Crop crop) {
crop.getCropId().ifPresent(id -> {
List<Task> taskList;
try {
taskList = gardenSchedule.getTaskListForCrop(id);
for (Task task : taskList) {
Label label = new Label(task.getDescription());
growthPhases_vbox.getChildren().add(label);
}
} catch (IOException e) {
// TODO: Alert
LOG.log(Level.SEVERE, "Could not get task list for crop", e.getCause());
}
});
}
private void createPestList(Plant plant) {
@@ -1,8 +1,7 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
@@ -10,10 +9,11 @@ import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("MainFXML.fxml"));
Scene scene = new Scene(fxmlLoader.load());
AppLoader appLoader = new AppLoader();
appLoader.loadSceneToStage("MainFXML.fxml", stage);
stage.setTitle("Gartenverwaltung");
stage.setScene(scene);
stage.show();
}
@@ -1,28 +1,25 @@
package ch.zhaw.gartenverwaltung;
import javafx.event.ActionEvent;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
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<>();
public class MainFXMLController {
private static final Logger LOG = Logger.getLogger(MainFXMLController.class.getName());
@Inject
AppLoader appLoader;
@FXML
private Button home_button;
@@ -30,7 +27,7 @@ public class MainFXMLController implements Initializable {
private AnchorPane mainPane;
@FXML
private Button myPlants_button;
private Button myGarden_button;
@FXML
private Button mySchedule_button;
@@ -39,26 +36,26 @@ public class MainFXMLController implements Initializable {
private Button plants_button;
@FXML
void goToHome(ActionEvent event) throws IOException {
loadPane("Home.fxml");
void goToHome() {
showPaneAsMainView("Home.fxml");
styleChangeButton(home_button);
}
@FXML
void goToMyPlants(ActionEvent event) throws IOException {
loadPane("MyPlants.fxml");
styleChangeButton(myPlants_button);
void goToMyPlants() {
showPaneAsMainView("MyGarden.fxml");
styleChangeButton(myGarden_button);
}
@FXML
void goToMySchedule(ActionEvent event) throws IOException {
loadPane("MySchedule.fxml");
void goToMySchedule() {
showPaneAsMainView("MySchedule.fxml");
styleChangeButton(mySchedule_button);
}
@FXML
void goToPlants(ActionEvent event) throws IOException {
loadPane("Plants.fxml");
void goToPlants() {
showPaneAsMainView("Plants.fxml");
styleChangeButton(plants_button);
}
@@ -67,25 +64,27 @@ public class MainFXMLController implements Initializable {
* 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 {
public void showPaneAsMainView(String fxmlFile) {
try {
Pane anchorPane = appLoader.loadPane(fxmlFile);
mainPane.getChildren().setAll(anchorPane);
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
AnchorPane anchorPane = panes.get(fxmlFile);
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);
}
anchorPane.removeEventHandler(ChangeViewEvent.CHANGE_MAIN_VIEW, changeMainViewHandler);
anchorPane.addEventHandler(ChangeViewEvent.CHANGE_MAIN_VIEW, changeMainViewHandler);
} catch (IOException e) {
LOG.log(Level.SEVERE, "Could not load pane.", e);
}
mainPane.getChildren().setAll(anchorPane);
anchorPane.prefWidthProperty().bind(mainPane.widthProperty());
anchorPane.prefHeightProperty().bind(mainPane.heightProperty());
}
private final EventHandler<ChangeViewEvent> changeMainViewHandler = (ChangeViewEvent event) -> showPaneAsMainView(event.view());
private void preloadPanes() throws IOException {
appLoader.loadAndCacheFxml("MyGarden.fxml");
appLoader.loadAndCacheFxml("MySchedule.fxml");
appLoader.loadAndCacheFxml("Plants.fxml");
}
private void styleChangeButton(Button button) {
@@ -96,10 +95,12 @@ public class MainFXMLController implements Initializable {
* loads the default FXML File
* {@inheritDoc}
*/
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
@AfterInject
@SuppressWarnings("unused")
public void init() {
try {
loadPane("Home.fxml");
preloadPanes();
showPaneAsMainView("Home.fxml");
styleChangeButton(home_button);
} catch (IOException e) {
LOG.log(Level.SEVERE, "Failed to load FXML-Pane!", e);
@@ -0,0 +1,146 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyGardenController {
private static final Logger LOG = Logger.getLogger(MyGardenController.class.getName());
@Inject
AppLoader appLoader;
@Inject
private Garden garden;
@Inject
private PlantList plantList;
@FXML
public AnchorPane myGardenRoot;
@FXML
private VBox myPlants_vbox;
@AfterInject
@SuppressWarnings("unused")
public void init() {
garden.getPlantedCrops().addListener((observable, oldValue, newValue) -> {
try {
createPlantView(newValue);
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
});
try {
createPlantView(garden.getPlantedCrops());
} catch (HardinessZoneNotSetException | IOException e) {
LOG.log(Level.SEVERE, "Could not update view of croplist!", e);
}
}
@FXML
void addPlant() {
myGardenRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "Plants.fxml"));
}
private void createPlantView(List<Crop> crops) throws HardinessZoneNotSetException, IOException {
myPlants_vbox.getChildren().clear();
for (Crop crop : crops) {
HBox hBox = createPlantView(crop);
myPlants_vbox.getChildren().add(hBox);
}
}
private HBox createPlantView(Crop crop) throws HardinessZoneNotSetException, IOException {
//ToDo add better design
Plant plant = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId()).get();
HBox hBox = new HBox(10);
ImageView imageView = new ImageView();
imageView.setPreserveRatio(false);
imageView.setFitHeight(100);
imageView.setFitWidth(100);
imageView.maxHeight(100);
if (plant.image() != null) {
imageView.setImage(plant.image());
}
hBox.setMinHeight(100);
Label label = new Label(plant.name());
label.setMaxWidth(2000);
HBox.setHgrow(label, Priority.ALWAYS);
Button details = new Button("Details");
Button delete = new Button("delete");
details.setOnAction(getGoToCropDetailEvent(crop));
delete.setOnAction(getDeleteCropEvent(crop));
hBox.getChildren().addAll(imageView, label, details, delete);
return hBox;
}
private EventHandler<ActionEvent> getGoToCropDetailEvent(Crop crop) {
return (event) -> {
try {
Stage stage = new Stage();
if (appLoader.loadSceneToStage("CropDetail.fxml", stage) instanceof CropDetailController controller) {
controller.setPlantFromCrop(crop);
}
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(true);
stage.showAndWait();
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
// TODO: show error alert
LOG.log(Level.SEVERE, "Could not load plant details.", e);
}
};
}
private EventHandler<ActionEvent> getDeleteCropEvent(Crop crop) {
return (event) -> {
try {
showConfirmation(crop);
} catch (IOException | HardinessZoneNotSetException e) {
e.printStackTrace();
}
};
}
private void showConfirmation(Crop crop) throws IOException, HardinessZoneNotSetException {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Delete Crop");
alert.setHeaderText("Are you sure want to delete this Crop?");
alert.setContentText("Deleting this crop will remove all associated tasks from your schedule.");
alert.showAndWait()
.ifPresent(buttonType -> {
if (buttonType == ButtonType.OK) {
try {
garden.removeCrop(crop);
} catch (IOException e) {
// TODO: Show error alert
LOG.log(Level.SEVERE, "Could not remove crop.", e);
}
}
});
}
}
@@ -1,160 +0,0 @@
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();
}
}
}
@@ -1,18 +1,18 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.gardenplan.Gardenplanmodel;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.taskList.TaskListModel;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.Crop;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.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;
@@ -20,17 +20,22 @@ 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;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MyScheduleController {
private static final Logger LOG = Logger.getLogger(MyScheduleController.class.getName());
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();
@Inject
private GardenSchedule gardenSchedule;
@Inject
private Garden garden;
@Inject
private PlantList plantList;
private final ListProperty<Crop> cropListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
@@ -82,14 +87,12 @@ public class MyScheduleController implements Initializable {
@FXML
private ListView<Crop> scheduledPlants_listview;
public MyScheduleController() throws IOException {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
@AfterInject
@SuppressWarnings("unused")
public void init() {
List<Crop> cropList;
try {
cropList = gardenplanmodel.getCrops();
cropList = garden.getCrops();
cropListProperty.addAll(cropList);
} catch (IOException e) {
e.printStackTrace();
@@ -107,15 +110,12 @@ public class MyScheduleController implements Initializable {
}
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();
}
scheduledPlants_listview.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
selectedCrop = newValue;
try {
loadTaskList();
} catch (IOException e) {
e.printStackTrace();
}
});
}
@@ -132,7 +132,7 @@ public class MyScheduleController implements Initializable {
}
private void setCellFactoryListView() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<Crop>() {
scheduledPlants_listview.setCellFactory(param -> new ListCell<>() {
@Override
protected void updateItem(Crop crop, boolean empty) {
super.updateItem(crop, empty);
@@ -141,9 +141,12 @@ public class MyScheduleController implements Initializable {
setText(null);
} else {
try {
setText(plantListModel.getFilteredPlantListById(Config.getCurrentHardinessZone(), crop.getPlantId()).get(0).name());
String text = plantList.getPlantById(Settings.getInstance().getCurrentHardinessZone(), crop.getPlantId())
.map(Plant::name)
.orElse("");
setText(text);
} catch (HardinessZoneNotSetException | IOException e) {
e.printStackTrace();
LOG.log(Level.WARNING, "Could not get plant for Cell", e);
}
}
}
@@ -151,11 +154,11 @@ public class MyScheduleController implements Initializable {
}
private void loadTaskList() throws IOException {
List<List<Task>> taskLists = new LinkedList<>();
List<List<Task>> taskLists;
if (selectedCrop != null) {
taskLists = taskListModel.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
taskLists = gardenSchedule.getTasksUpcomingWeekForCrop(selectedCrop.getCropId().get());
} else {
taskLists = taskListModel.getTasksUpcomingWeek();
taskLists = gardenSchedule.getTasksUpcomingWeek();
}
if (!taskLists.isEmpty()) {
viewTaskListOfDay(day1_pane, taskLists.get(0));
@@ -1,44 +1,53 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.bootstrap.AfterInject;
import ch.zhaw.gartenverwaltung.bootstrap.AppLoader;
import ch.zhaw.gartenverwaltung.bootstrap.ChangeViewEvent;
import ch.zhaw.gartenverwaltung.bootstrap.Inject;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.models.PlantNotFoundException;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.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.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.time.LocalDate;
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 {
public class PlantsController {
private static final Logger LOG = Logger.getLogger(PlantsController.class.getName());
private final PlantListModel plantListModel = new PlantListModel();
@Inject
private PlantListModel plantListModel;
@Inject
private AppLoader appLoader;
@Inject
private Garden garden;
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
public AnchorPane plantsRoot;
@FXML
private VBox seasons;
@@ -62,20 +71,36 @@ public class PlantsController implements Initializable {
/**
* 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);
void selectSowDate() throws IOException {
Stage stage = new Stage();
stage.setScene(new Scene(root));
stage.initModality(Modality.APPLICATION_MODAL);
stage.setResizable(false);
stage.showAndWait();
Dialog<LocalDate> dateSelection = new Dialog<>();
dateSelection.setTitle("Select Date");
dateSelection.setHeaderText(String.format("Select Harvest/Sow Date for %s:", selectedPlant.name()));
dateSelection.setResizable(false);
DialogPane dialogPane = dateSelection.getDialogPane();
ButtonType sowButton = new ButtonType("Save", ButtonBar.ButtonData.OK_DONE);
dialogPane.getButtonTypes().addAll(sowButton, ButtonType.CANCEL);
if (appLoader.loadSceneToStage("SelectSowDay.fxml", stage) instanceof SelectSowDayController controller) {
controller.initSaveButton((Button) dialogPane.lookupButton(sowButton));
controller.setSelectedPlant(selectedPlant);
dateSelection.setResultConverter(button -> button.equals(sowButton) ? controller.retrieveResult() : null);
}
dialogPane.setContent(stage.getScene().getRoot());
dateSelection.showAndWait()
.ifPresent(date -> {
try {
garden.plantAsCrop(selectedPlant, date);
} catch (IOException | HardinessZoneNotSetException | PlantNotFoundException e) {
LOG.log(Level.SEVERE, "Couldn't save Crop", e);
}
plantsRoot.fireEvent(new ChangeViewEvent(ChangeViewEvent.CHANGE_MAIN_VIEW, "MyGarden.fxml"));
});
}
/**
@@ -85,8 +110,9 @@ public class PlantsController implements Initializable {
* create event listener for selected list entry and search by query
* {@inheritDoc}
*/
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
@AfterInject
@SuppressWarnings("unused")
public void init() {
setListCellFactory();
fillPlantListWithHardinessZone();
list_plants.itemsProperty().bind(plantListProperty);
@@ -97,6 +123,7 @@ public class PlantsController implements Initializable {
createFilterSeasons();
createFilterHardinessZone();
lookForSelectedListEntry();
try {
viewFilteredListBySearch();
} catch (HardinessZoneNotSetException e) {
@@ -107,7 +134,7 @@ public class PlantsController implements Initializable {
}
/**
* set text of list view to plant name
* set text of list view to plant name
*/
private void setListCellFactory() {
list_plants.setCellFactory(param -> new ListCell<>() {
@@ -127,9 +154,10 @@ public class PlantsController implements Initializable {
/**
* 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
* @throws IOException throws exception
*/
private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
clearListView();
@@ -139,8 +167,9 @@ public class PlantsController implements Initializable {
/**
* 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
* @throws IOException throws exception
*/
private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
@@ -185,7 +214,7 @@ public class PlantsController implements Initializable {
for (HardinessZone zone : HardinessZone.values()) {
RadioButton radioButton = new RadioButton(zone.name());
radioButton.setToggleGroup(hardinessGroup);
radioButton.setPadding(new Insets(0,0,10,0));
radioButton.setPadding(new Insets(0, 0, 10, 0));
if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
radioButton.setSelected(true);
}
@@ -207,7 +236,7 @@ public class PlantsController implements Initializable {
for (Seasons season : Seasons.values()) {
RadioButton radioButton = new RadioButton(season.getName());
radioButton.setToggleGroup(seasonGroup);
radioButton.setPadding(new Insets(0,0,10,0));
radioButton.setPadding(new Insets(0, 0, 10, 0));
if (season.equals(Seasons.AllSEASONS)) {
radioButton.setSelected(true);
}
@@ -241,12 +270,12 @@ public class PlantsController implements Initializable {
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) {
if (newValue != null) {
selectedPlant = newValue;
description_plant.setText(selectedPlant.description());
selectSowDay_button.setDisable(false);
Image img1;
if(selectedPlant.image() != null) {
if (selectedPlant.image() != null) {
img1 = selectedPlant.image();
} else {
img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
@@ -1,108 +1,62 @@
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);
public class SelectSowDayController {
private Plant selectedPlant;
@FXML
private DatePicker datepicker;
@FXML
private Label popup_label;
private RadioButton harvest_radio;
@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 {
public LocalDate retrieveResult() {
LocalDate sowDate = datepicker.getValue();
if (harvest_radio.isSelected()) {
//ToDo method to get current lifecycle group in plant
sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0);
}
gardenplanmodel.plantAsCrop(selectedPlant, sowDate);
closeWindow();
return sowDate;
}
/**
* save the plant which will be planted and update label
* Set the {@link Plant} for which a date should be selected.
*
* @param plant Plant
*/
public void getSelectedPlant(Plant plant) {
public void setSelectedPlant(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) {
@FXML
public void initialize() {
clearDatePickerEntries();
Callback<DatePicker, DateCell> dayCellFactory= getDayCellFactory();
Callback<DatePicker, DateCell> dayCellFactory = getDayCellFactory();
datepicker.setDayCellFactory(dayCellFactory);
datepicker.getEditor().setEditable(false);
datepicker.setEditable(false);
}
enableDisableSaveButton();
public void initSaveButton(Button saveButton) {
saveButton.disableProperty().bind(datepicker.valueProperty().isNull());
}
/**
* 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();
}
});
harvest_radio.selectedProperty().addListener((observable, oldValue, isNowSelected) -> datepicker.setValue(null));
}
/**
@@ -111,64 +65,32 @@ public class SelectSowDayController implements Initializable {
*/
private Callback<DatePicker, DateCell> getDayCellFactory() {
final Callback<DatePicker, DateCell> dayCellFactory = new Callback<DatePicker, DateCell>() {
return (datePicker) -> new 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);
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 (harvest_radio.isSelected()) {
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST);
} else {
save_button.setDisable(false);
dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW);
}
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 ((harvest_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) {
setDisable(true);
setStyle("-fx-background-color: #ffc0cb;");
}
}
});
};
}
}
@@ -0,0 +1,26 @@
package ch.zhaw.gartenverwaltung;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
public class Settings {
private HardinessZone currentHardinessZone = HardinessZone.ZONE_8A;
private static Settings instance;
static {
instance = new Settings();
}
public static Settings getInstance() {
return Settings.instance;
}
private Settings() {}
public HardinessZone getCurrentHardinessZone() {
return currentHardinessZone;
}
public void setCurrentHardinessZone(HardinessZone currentHardinessZone) {
this.currentHardinessZone = currentHardinessZone;
}
}
@@ -0,0 +1,14 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a method to be executed after all dependencies annotates with {@link Inject}
* have been injected.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterInject { }
@@ -0,0 +1,138 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import ch.zhaw.gartenverwaltung.HelloApplication;
import ch.zhaw.gartenverwaltung.io.CropList;
import ch.zhaw.gartenverwaltung.io.JsonCropList;
import ch.zhaw.gartenverwaltung.io.JsonPlantList;
import ch.zhaw.gartenverwaltung.io.JsonTaskList;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.io.TaskList;
import ch.zhaw.gartenverwaltung.models.Garden;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class AppLoader {
/**
* Caching the panes
*/
private final Map<String, Pane> panes = new HashMap<>();
/**
* Application-wide dependencies
*/
private final PlantList plantList = new JsonPlantList();
private final CropList cropList = new JsonCropList();
private final TaskList taskList = new JsonTaskList();
private final GardenSchedule gardenSchedule = new GardenSchedule(taskList, plantList);
private final Garden garden = new Garden(gardenSchedule, cropList);
public AppLoader() throws IOException {
}
/**
* Loads and returns a {@link Pane} (cached).
*
* @param fxmlFile The file name to be loaded
* @return The loaded Pane
* @throws IOException if the file could not be loaded
*/
public Pane loadPane(String fxmlFile) throws IOException {
Pane pane = panes.get(fxmlFile);
if (pane == null) {
loadAndCacheFxml(fxmlFile);
pane = panes.get(fxmlFile);
}
return pane;
}
/**
* Loads the given fxml-file from resources (no caching) and creates a new {@link Scene},
* which is then appended to the given {@link Stage}.
* Performs dependency-injection.
*
* @param fxmlFile The file name to be loaded
* @param appendee The {@link Stage} to which the new {@link Scene} is appended.
* @return The controller of the loaded scene.
* @throws IOException if the file could not be loaded
*/
public Object loadSceneToStage(String fxmlFile, Stage appendee) throws IOException {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
Pane root = loader.load();
appendee.setScene(new Scene(root));
Object controller = loader.getController();
annotationInject(controller);
return controller;
}
/**
* Loads the given fxml-file from resources and caches the pane.
* Performs dependency-injection.
*
* @param fxmlFile The file name to be loaded
* @throws IOException if the file could not be loaded
*/
public void loadAndCacheFxml(String fxmlFile) throws IOException {
FXMLLoader loader = new FXMLLoader(Objects.requireNonNull(HelloApplication.class.getResource(fxmlFile)));
Pane pane = loader.load();
panes.put(fxmlFile, pane);
annotationInject(loader.getController());
}
/**
* Injects the applications dependencies into the given object's fields annotated with {@link Inject}.
* Afterwards, all methods on the objects annotated with {@link AfterInject} are executed.
* (Success of the injections is not guaranteed!)
*
* @param controller The class containing the injectable fields
*/
public void annotationInject(Object controller) {
Arrays.stream(controller.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Inject.class))
.forEach(field -> {
field.setAccessible(true);
try {
field.set(controller, getAppDependency(field.getType()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
field.setAccessible(false);
});
Arrays.stream(controller.getClass().getMethods())
.filter(method -> method.isAnnotationPresent(AfterInject.class))
.forEach(afterInjectMethod -> {
if (afterInjectMethod.getParameterCount() == 0) {
try {
afterInjectMethod.invoke(controller);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
private Object getAppDependency(Class<?> type) {
return switch (type.getSimpleName()) {
case "Garden" -> garden;
case "PlantList" -> plantList;
case "PlantListModel" -> new PlantListModel(plantList);
case "GardenSchedule" -> gardenSchedule;
case "AppLoader" -> this;
default -> null;
};
}
}
@@ -0,0 +1,19 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import javafx.event.Event;
import javafx.event.EventType;
public class ChangeViewEvent extends Event {
private final String view;
public static final EventType<ChangeViewEvent> CHANGE_MAIN_VIEW = new EventType<>("CHANGE_MAIN_VIEW");
public ChangeViewEvent(EventType<? extends Event> eventType, String view) {
super(eventType);
this.view = view;
}
public String view() {
return view;
}
}
@@ -0,0 +1,13 @@
package ch.zhaw.gartenverwaltung.bootstrap;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates a Field to be injected from the application-dependencies
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject { }
@@ -6,7 +6,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Optional;
public interface GardenPlan {
public interface CropList {
/**
* Yields a list of all {@link Crop}s in the database.
*
@@ -19,7 +19,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
public class JsonGardenPlan implements GardenPlan {
public class JsonCropList implements CropList {
private final URL dataSource;
private IdProvider idProvider;
@@ -42,7 +42,7 @@ public class JsonGardenPlan implements GardenPlan {
/**
* Default constructor
*/
public JsonGardenPlan() {
public JsonCropList() {
this.dataSource = getClass().getResource("user-crops.json");
}
@@ -50,7 +50,7 @@ public class JsonGardenPlan implements GardenPlan {
* 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) {
public JsonCropList(URL dataSource) {
this.dataSource = dataSource;
}
/**
@@ -20,11 +20,11 @@ import java.util.Map;
import java.util.Optional;
/**
* Implements the {@link PlantDatabase} interface for loading {@link Plant} objects
* Implements the {@link PlantList} interface for loading {@link Plant} objects
* from a JSON file.
* The reads are cached to minimize file-io operations.
*/
public class JsonPlantDatabase implements PlantDatabase {
public class JsonPlantList implements PlantList {
private final URL dataSource = getClass().getResource("plantdb.json");
private HardinessZone currentZone;
@@ -49,7 +49,7 @@ public class JsonPlantDatabase implements PlantDatabase {
* from the {@link #currentZone}, data is loaded from {@link #dataSource}.
* In any case, the values of {@link #plantMap} are returned.
*
* @see PlantDatabase#getPlantList(HardinessZone)
* @see PlantList#getPlantList(HardinessZone)
*/
@Override
public List<Plant> getPlantList(HardinessZone zone) throws IOException, HardinessZoneNotSetException {
@@ -60,7 +60,7 @@ public class JsonPlantDatabase implements PlantDatabase {
}
/**
* @see PlantDatabase#getPlantById(long)
* @see PlantList#getPlantById(long)
*/
@Override
public Optional<Plant> getPlantById(HardinessZone zone, long id) throws HardinessZoneNotSetException, IOException {
@@ -1,6 +1,5 @@
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;
@@ -20,11 +19,11 @@ import java.util.List;
import java.util.Map;
/**
* Implements the {@link TaskDatabase} interface for loading and writing {@link Task} objects
* Implements the {@link TaskList} interface for loading and writing {@link Task} objects
* from and to a JSON file.
* The reads are cached to minimize file-io operations.
*/
public class JsonTaskDatabase implements TaskDatabase{
public class JsonTaskList implements TaskList {
IdProvider idProvider;
private final URL dataSource = getClass().getResource("taskdb.json");
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
@@ -48,7 +47,7 @@ public class JsonTaskDatabase implements TaskDatabase{
* 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)
* @see TaskList#getTaskList(LocalDate, LocalDate)
*/
@Override
public List<Task> getTaskList(LocalDate start, LocalDate end) throws IOException{
@@ -90,7 +89,7 @@ public class JsonTaskDatabase implements TaskDatabase{
* it to the {@link #taskMap}. In any case, the {@link #taskMap} is written
* to the {@link #dataSource}.
*
* @see TaskDatabase#saveTask(Task)
* @see TaskList#saveTask(Task)
*/
@Override
public void saveTask(Task task) throws IOException {
@@ -108,7 +107,7 @@ public class JsonTaskDatabase implements TaskDatabase{
* 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)
* @see TaskList#removeTask(Task)
*/
@Override
public void removeTask(Task task) throws IOException {
@@ -11,7 +11,7 @@ import java.util.Optional;
* A database of {@link Plant}s.
* The interface specifies the minimal required operations.
*/
public interface PlantDatabase {
public interface PlantList {
/**
* Yields a list of all {@link Plant}s in the database with only data relevant to the specfied {@link HardinessZone}
*
@@ -1,20 +1,16 @@
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 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 {
public interface TaskList {
/**
* Yields a list of all {@link Task}s in the database with the start and end date of a period of time.
*
@@ -1,6 +1,6 @@
package ch.zhaw.gartenverwaltung.json;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.io.PlantList;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
@@ -18,7 +18,7 @@ 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()));
URL imageUrl = PlantList.class.getResource(String.format("images/%s", parser.getText()));
if (imageUrl != null) {
try (InputStream is = new FileInputStream(new File(imageUrl.toURI()))) {
result = new Image(is);
@@ -1,41 +1,41 @@
package ch.zhaw.gartenverwaltung.gardenplan;
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.GardenPlan;
import ch.zhaw.gartenverwaltung.io.CropList;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.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 javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
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;
public class Garden {
private final CropList cropList;
private final ListProperty<Crop> plantedCrops = new SimpleListProperty<>(FXCollections.observableArrayList());
private final GardenSchedule gardenSchedule;
/**
* Constructor of Gardenplan model
*
* @param taskListModel holds a reference to the task list object.
* @param gardenSchedule holds a reference to the task list object.
*/
public Gardenplanmodel(TaskListModel taskListModel) throws IOException {
this.taskListModel = taskListModel;
gardenPlan = new JsonGardenPlan();
cropList = new ArrayList<>();
cropList = gardenPlan.getCrops();
public Garden(GardenSchedule gardenSchedule, CropList cropList) throws IOException {
this.gardenSchedule = gardenSchedule;
this.cropList = cropList;
plantedCrops.addAll(cropList.getCrops());
}
public ListProperty<Crop> getPlantedCrops() {
return plantedCrops;
}
/**
@@ -52,9 +52,10 @@ public class Gardenplanmodel {
Crop crop = new Crop(plant.id(), plantingDate);
//TODO Add Area to Plant
//crop.withArea(0);
gardenPlan.saveCrop(crop);
taskListModel.planTasksForCrop(crop);
cropList = gardenPlan.getCrops();
cropList.saveCrop(crop);
gardenSchedule.planTasksForCrop(crop);
plantedCrops.clear();
plantedCrops.addAll(cropList.getCrops());
}
/**
@@ -64,9 +65,10 @@ public class Gardenplanmodel {
* @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();
cropList.removeCrop(crop);
gardenSchedule.removeTasksForCrop(crop.getCropId().orElseThrow(IllegalArgumentException::new));
plantedCrops.clear();
plantedCrops.addAll(cropList.getCrops());
}
/**
* Returns a list of {@link Crop}s which are currently in the gardenplan.
@@ -74,10 +76,7 @@ public class Gardenplanmodel {
* @throws IOException If the database cannot be accessed
*/
public List<Crop> getCrops() throws IOException {
if(!cropList.isEmpty()){
cropList = gardenPlan.getCrops();
}
return cropList;
return cropList.getCrops();
}
/**
@@ -87,6 +86,6 @@ public class Gardenplanmodel {
* @throws IOException If the database cannot be accessed
*/
public Optional<Crop> getCrop(Long cropId) throws IOException {
return gardenPlan.getCropById(cropId);
return cropList.getCropById(cropId);
}
}
@@ -1,6 +1,6 @@
package ch.zhaw.gartenverwaltung.taskList;
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.Config;
import ch.zhaw.gartenverwaltung.Settings;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.*;
@@ -9,29 +9,23 @@ 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;
public class GardenSchedule {
private final TaskList taskList;
private final PlantList plantList;
/**
* Comparators to create sorted Task List
*/
static final Comparator<Task> sortByStartDate = Comparator.comparing(Task::getStartDate);
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;
public GardenSchedule(TaskList taskList, PlantList plantList) {
this.taskList = taskList;
this.plantList = plantList;
}
/**
@@ -40,7 +34,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed
*/
public void addTask(Task task) throws IOException {
taskDatabase.saveTask(task);
taskList.saveTask(task);
}
/**
@@ -51,7 +45,7 @@ public class TaskListModel {
* @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);
Plant plant = plantList.getPlantById(Settings.getInstance().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)));
@@ -65,7 +59,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed
*/
public void removeTasksForCrop(long cropId) throws IOException {
taskDatabase.removeTasksForCrop(cropId);
taskList.removeTasksForCrop(cropId);
}
/**
@@ -74,7 +68,7 @@ public class TaskListModel {
* @throws IOException If the database cannot be accessed
*/
public void removeTask(Task task) throws IOException {
taskDatabase.removeTask(task);
taskList.removeTask(task);
}
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
@@ -144,7 +138,7 @@ public class TaskListModel {
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));
dayTaskList.add(taskList.getTaskList(date, date));
}
return dayTaskList;
}
@@ -158,7 +152,7 @@ public class TaskListModel {
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));
dayTaskList.add(filterListByCrop(taskList.getTaskList(date, date), cropId));
}
return dayTaskList;
}
@@ -171,7 +165,7 @@ public class TaskListModel {
* @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);
return getSortedTaskList(taskList.getTaskList(start, end), sortByStartDate);
}
/**
@@ -1,8 +1,7 @@
package ch.zhaw.gartenverwaltung.plantList;
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.types.GrowthPhaseType;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
@@ -16,7 +15,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PlantListModel {
private PlantDatabase plantDatabase;
private final PlantList plantList;
private HardinessZone currentZone;
/**
@@ -28,18 +27,13 @@ public class PlantListModel {
/**
* Constructor to create Database Object.
*/
public PlantListModel() {
plantDatabase = new JsonPlantDatabase();
setDefaultZone();
}
public PlantListModel(PlantDatabase plantDatabase) {
this.plantDatabase = plantDatabase;
public PlantListModel(PlantList plantList) {
this.plantList = plantList;
setDefaultZone();
}
private void setDefaultZone() {
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Config
currentZone = HardinessZone.ZONE_8A; // TODO: get Default Zone from Settings
}
public void setCurrentZone(HardinessZone currentZone) {
@@ -72,7 +66,7 @@ public class PlantListModel {
*/
public List<Plant> getSortedPlantList(HardinessZone zone, Comparator<Plant> comparator) throws HardinessZoneNotSetException, IOException {
setCurrentZone(zone);
return plantDatabase.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList());
return plantList.getPlantList(zone).stream().sorted(comparator).collect(Collectors.toList());
}
/**
@@ -101,7 +95,7 @@ public class PlantListModel {
public List<Plant> getFilteredPlantListById(HardinessZone zone, Long id) throws HardinessZoneNotSetException, IOException {
setCurrentZone(zone);
List<Plant> plantList = new ArrayList<>();
plantDatabase.getPlantById(zone, id).ifPresent(plantList::add);
this.plantList.getPlantById(zone, id).ifPresent(plantList::add);
return plantList;
}
@@ -1,4 +1,4 @@
package ch.zhaw.gartenverwaltung.taskList;
package ch.zhaw.gartenverwaltung.models;
public class PlantNotFoundException extends Exception {
public PlantNotFoundException() {
+2
View File
@@ -9,6 +9,8 @@ module ch.zhaw.gartenverwaltung {
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.io;
exports ch.zhaw.gartenverwaltung.types;
exports ch.zhaw.gartenverwaltung.models;
exports ch.zhaw.gartenverwaltung.json;
}
@@ -11,8 +11,8 @@
<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" />
<Button fx:id="myGarden_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMyPlants" prefHeight="38.0" prefWidth="121.0" text="My Garden" />
<Button fx:id="mySchedule_button" layoutX="10.0" layoutY="10.0" mnemonicParsing="false" onAction="#goToMySchedule" prefHeight="38.0" prefWidth="121.0" text="My Schedule" />
<Pane maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" HBox.hgrow="ALWAYS" />
</children>
</HBox>
@@ -7,11 +7,11 @@
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyPlantsController">
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="655.0" prefWidth="1175.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ch.zhaw.gartenverwaltung.MyGardenController" fx:id="myGardenRoot">
<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">
<Label text="My Garden">
<font>
<Font name="System Bold" size="28.0" />
</font>
@@ -15,7 +15,8 @@
<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">
fx:controller="ch.zhaw.gartenverwaltung.PlantsController"
fx:id="plantsRoot">
<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>
@@ -1,9 +1,7 @@
<?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?>
@@ -16,12 +14,11 @@
<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">
<RadioButton fx:id="sow_radio" mnemonicParsing="false" selected="true" text="Sow" toggleGroup="$group">
<VBox.margin>
<Insets bottom="10.0" />
</VBox.margin>
@@ -38,16 +35,6 @@
<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" />
@@ -22,8 +22,8 @@ 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;
public class JsonCropListTest {
private CropList testDatabase;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* Files to isolate the test-units
@@ -36,7 +36,7 @@ public class JsonGardenPlanTest {
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonGardenPlan(dbDataSource);
testDatabase = new JsonCropList(dbDataSource);
}
@@ -13,12 +13,12 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class JsonPlantDatabaseTest {
PlantDatabase testDatabase;
public class JsonPlantListTest {
PlantList testDatabase;
@BeforeEach
void connectToDb() {
testDatabase = new JsonPlantDatabase();
testDatabase = new JsonPlantList();
}
@@ -1,47 +0,0 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.*;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class JsonTaskDatabaseTest {
TaskDatabase testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach
void connectToDb() {
// testDatabase = new JsonTaskDatabase();
}
@Test
@DisplayName("Check if results are retrieved completely")
void getTasks(){
/*
List<Task> taskList=null;
try {
taskList = testDatabase.getTaskList(formatter.parse("01.05.2022"), formatter.parse("01.08.2022"));
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new RuntimeException(e);
}
Assertions.assertTrue(taskList.size()>0);
*/
}
@Test
void getTaskForCrop() {
// TODO implement Test
}
@Test
void removeTasksForCrop() {
// TODO implement Test
}
}
@@ -0,0 +1,117 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.*;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class JsonTaskListTest {
TaskList testDatabase;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private final URL dbDataSource = this.getClass().getResource("taskdb.json");
private final URL testFile = this.getClass().getResource("test-taskdb.json");
@BeforeEach
void connectToDb() throws URISyntaxException, IOException {
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonTaskList();
}
@Test
@DisplayName("Check if results are retrieved completely")
void getTasks() {
List<Task> taskList = null;
try {
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("31.05.2022", formatter));
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(3, taskList.size());
}
@Disabled("disabled until adding works.")
@Test
@DisplayName("Add task.")
void addTask() {
Task task = new Task("Testtask", "This is a test Task.", LocalDate.parse("01.05.2022", formatter), 1);
try {
testDatabase.saveTask(task);
List<Task> taskList = null;
try {
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("31.05.2022", formatter));
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(4, taskList.size());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
@DisplayName("Remove task.")
void removeTask() {
Task task = new Task("Dummy", "Dummy", LocalDate.parse("31.05.2022", formatter), 1).withId(2);
try {
testDatabase.removeTask(task);
List<Task> taskList = null;
taskList = testDatabase.getTaskList(LocalDate.parse("30.04.2022", formatter),
LocalDate.parse("31.05.2022", formatter));
Assertions.assertEquals(2, taskList.size());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
void getTaskForCrop() {
List<Task> taskList = null;
try {
taskList = testDatabase.getTaskForCrop(0);
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(6, taskList.size());
}
@Disabled("Disabled until removing works")
@Test
void removeTasksForCrop() {
List<Task> taskList = null;
try {
testDatabase.removeTasksForCrop(0);
taskList = testDatabase.getTaskForCrop(0);
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(0, taskList.size());
}
}
@@ -1,28 +1,32 @@
package ch.zhaw.gartenverwaltung.gardenplan;
package ch.zhaw.gartenverwaltung.models;
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.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.MonthDay;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.*;
public class GardenPlanModelTest {
GardenPlan gardenPlan;
List<Crop> cropList;
private final URL dbDataSource = JsonCropList.class.getResource("user-crops.json");
private final URL testFile = JsonCropList.class.getResource("test-user-crops.json");
CropList cropList;
List<Crop> exampleCrops;
Crop exampleCropOnion;
Crop exampleCropCarrot;
Crop exampleCrop1;
@@ -30,10 +34,10 @@ public class GardenPlanModelTest {
Crop exampleCrop3;
Plant examplePlantOnion;
Plant examplePlantCarrot;
Gardenplanmodel model;
Garden model;
@BeforeEach
void setUp() throws IOException {
void setUp() throws IOException, URISyntaxException {
examplePlantOnion = new Plant(
@@ -72,41 +76,48 @@ public class GardenPlanModelTest {
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 = new Crop(0,LocalDate.of(2023,3,1));
exampleCrop3.withId(2);
exampleCrop3.withArea(1.0);
cropList = new ArrayList<>();
cropList.add(exampleCrop1);
cropList.add(exampleCrop2);
cropList.add(exampleCrop3);
gardenPlan = mockGardenPlan(cropList);
exampleCrops = new ArrayList<>();
exampleCrops.add(exampleCrop1);
exampleCrops.add(exampleCrop2);
exampleCrops.add(exampleCrop3);
cropList = mockCropList(exampleCrops);
TaskListModel taskListModel = new TaskListModel(new JsonTaskDatabase(), new JsonPlantDatabase());
model = new Gardenplanmodel(taskListModel);
// Reset Crop "database" before test
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
CropList testDatabase = new JsonCropList(dbDataSource);
GardenSchedule gardenSchedule = mock(GardenSchedule.class);
model = new Garden(gardenSchedule, testDatabase);
}
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;
CropList mockCropList(List<Crop> cropList) throws IOException {
CropList croplist = mock(CropList.class);
when(croplist.getCrops()).thenReturn(cropList);
when(croplist.getCropById(5)).thenReturn(java.util.Optional.ofNullable(exampleCropCarrot));
when(croplist.getCropById(3)).thenReturn(java.util.Optional.ofNullable(exampleCropOnion));
return croplist;
}
@Test
void plantAsCrop() throws HardinessZoneNotSetException, IOException, PlantNotFoundException {
model.plantAsCrop(examplePlantOnion, LocalDate.of(2023,3,1));
exampleCropOnion = model.getCrop(2L).get();
assertEquals(model.getCrops().get(2),exampleCropOnion);
Optional<Crop> exampleCrop = model.getCrop(2L);
assertTrue(exampleCrop.isPresent());
assertEquals(model.getCrops().get(2), exampleCrop.get());
}
@Test
void removeCrop() throws IOException {
exampleCrop1.withId(2);
exampleCrop1.withArea(1.500000);
exampleCrop1.withArea(1.5);
model.removeCrop(exampleCrop1);
assertEquals(2,model.getCrops().size());
}
@@ -1,8 +1,9 @@
package ch.zhaw.gartenverwaltung.plantList;
package ch.zhaw.gartenverwaltung.models;
import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.io.JsonPlantDatabase;
import ch.zhaw.gartenverwaltung.io.PlantDatabase;
import ch.zhaw.gartenverwaltung.io.JsonPlantList;
import ch.zhaw.gartenverwaltung.io.PlantList;
import ch.zhaw.gartenverwaltung.models.PlantListModel;
import ch.zhaw.gartenverwaltung.types.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -22,15 +23,15 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class PlantListModelTest {
PlantDatabase plantDatabase;
PlantList plantList;
List<Plant> examplePlantList;
PlantListModel model;
@BeforeEach
void setUp() throws HardinessZoneNotSetException, IOException {
createExamplePlantList();
plantDatabase = mockPlantDatabase(examplePlantList);
model = new PlantListModel(plantDatabase);
plantList = mockPlantDatabase(examplePlantList);
model = new PlantListModel(plantList);
}
@AfterEach
@@ -79,8 +80,8 @@ class PlantListModelTest {
);
}
PlantDatabase mockPlantDatabase(List<Plant> plantList) throws HardinessZoneNotSetException, IOException {
PlantDatabase plantDatabase = mock(JsonPlantDatabase.class);
PlantList mockPlantDatabase(List<Plant> plantList) throws HardinessZoneNotSetException, IOException {
PlantList plantDatabase = mock(JsonPlantList.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)));
@@ -1,6 +1,7 @@
package ch.zhaw.gartenverwaltung.taskList;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.models.GardenSchedule;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
@@ -14,47 +15,47 @@ import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class TaskListModelTest {
TaskDatabase taskDatabase;
PlantDatabase plantDatabase;
class GardenScheduleTest {
TaskList taskList;
PlantList plantList;
List<Task> exampleTaskList;
Map<Long, Plant> examplePlantMap;
TaskListModel model;
GardenSchedule model;
@BeforeEach
void setUp() throws IOException {
createExampleTaskList();
taskDatabase = mockTaskDatabase(exampleTaskList);
plantDatabase = mockPlantDatabase(examplePlantMap);
model = new TaskListModel(taskDatabase, plantDatabase);
taskList = mockTaskDatabase(exampleTaskList);
plantList = mockPlantDatabase(examplePlantMap);
model = new GardenSchedule(taskList, plantList);
}
private TaskDatabase mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
private TaskList mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
TaskList taskList = mock(JsonTaskList.class);
when(taskList.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
when(taskList.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
List<Task> pastTasks = new ArrayList<>();
pastTasks.add(exampleTaskList.get(0));
pastTasks.add(exampleTaskList.get(2));
pastTasks.add(exampleTaskList.get(4));
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
when(taskList.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());
when(taskList.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
when(taskList.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
when(taskList.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
when(taskList.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
return taskDatabase;
return taskList;
}
private PlantDatabase mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
return new PlantDatabase() {
private PlantList mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
return new PlantList() {
@Override
public List<Plant> getPlantList(HardinessZone zone) {
return null;
@@ -86,14 +87,14 @@ class TaskListModelTest {
void addTask() throws IOException {
Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
model.addTask(taskToAdd);
verify(taskDatabase, times(1)).saveTask(taskToAdd);
verify(taskList, times(1)).saveTask(taskToAdd);
}
@Test
void removeTask() throws IOException {
Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
model.removeTask(taskToRemove);
verify(taskDatabase, times(1)).removeTask(taskToRemove);
verify(taskList, times(1)).removeTask(taskToRemove);
}
@Test
@@ -153,6 +154,6 @@ class TaskListModelTest {
@Test
void removeTasksForCrop() throws IOException {
model.removeTasksForCrop(1L);
verify(taskDatabase, times(1)).removeTasksForCrop(1L);
verify(taskList, times(1)).removeTasksForCrop(1L);
}
}
@@ -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
}
]